forked from MasoodIqbal/RSI-Stoch-MA-EA
492 lines
No EOL
13 KiB
MQL5
492 lines
No EOL
13 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| ChartStructure.mqh |
|
|
//| Market Structure Analysis: HH, HL, LH, LL |
|
|
//| STANDALONE VERSION - No external dependencies |
|
|
//+------------------------------------------------------------------+
|
|
|
|
#property copyright "QuarterTheory x VIZION"
|
|
#property version "1.00"
|
|
#property strict
|
|
|
|
//================ STRUCTURE ENUMS ==================//
|
|
enum ENUM_SWING_TYPE
|
|
{
|
|
SWING_HIGH,
|
|
SWING_LOW
|
|
};
|
|
|
|
enum ENUM_MARKET_STRUCTURE
|
|
{
|
|
STRUCTURE_UPTREND,
|
|
STRUCTURE_DOWNTREND,
|
|
STRUCTURE_RANGING,
|
|
STRUCTURE_BREAKING
|
|
};
|
|
|
|
//================ STRUCTURE CLASSES ==================//
|
|
struct SwingPoint
|
|
{
|
|
ENUM_SWING_TYPE type;
|
|
double price;
|
|
datetime time;
|
|
int bar_index;
|
|
bool is_higher_high;
|
|
bool is_lower_low;
|
|
bool is_higher_low;
|
|
bool is_lower_high;
|
|
bool is_structure_break;
|
|
};
|
|
|
|
//================ GLOBAL VARIABLES ==================//
|
|
SwingPoint g_SwingPoints[];
|
|
int g_SwingCount = 0;
|
|
const int MAX_SWINGS = 50;
|
|
|
|
ENUM_MARKET_STRUCTURE g_CurrentStructure = STRUCTURE_RANGING;
|
|
double g_LastHighHigh = 0;
|
|
double g_LastHighLow = 0;
|
|
double g_LastLowHigh = 0;
|
|
double g_LastLowLow = 0;
|
|
|
|
int g_ConsecutiveHH = 0;
|
|
int g_ConsecutiveHL = 0;
|
|
int g_ConsecutiveLH = 0;
|
|
int g_ConsecutiveLL = 0;
|
|
|
|
bool g_BullishStructureBreak = false;
|
|
bool g_BearishStructureBreak = false;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize Chart Structure System |
|
|
//+------------------------------------------------------------------+
|
|
bool InitializeChartStructure()
|
|
{
|
|
ArrayResize(g_SwingPoints, MAX_SWINGS);
|
|
g_SwingCount = 0;
|
|
|
|
Print("✅ Chart Structure Analysis Initialized");
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Main Structure Detection |
|
|
//+------------------------------------------------------------------+
|
|
void DetectMarketStructure()
|
|
{
|
|
MqlRates rates[];
|
|
ArraySetAsSeries(rates, true);
|
|
int copied = CopyRates(_Symbol, _Period, 0, 200, rates);
|
|
|
|
if(copied < 50) return;
|
|
|
|
IdentifySwingPoints(rates);
|
|
ClassifySwingRelationships();
|
|
DetermineMarketStructure();
|
|
DetectStructureBreaks(rates);
|
|
UpdateStructureCounters();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Identify Swing Highs and Lows |
|
|
//+------------------------------------------------------------------+
|
|
void IdentifySwingPoints(const MqlRates &rates[])
|
|
{
|
|
g_SwingCount = 0;
|
|
|
|
const int swing_bars = 5;
|
|
|
|
for(int i = swing_bars; i < ArraySize(rates) - swing_bars; i++)
|
|
{
|
|
bool is_swing_high = true;
|
|
bool is_swing_low = true;
|
|
|
|
for(int j = 1; j <= swing_bars; j++)
|
|
{
|
|
if(rates[i].high <= rates[i-j].high || rates[i].high <= rates[i+j].high)
|
|
{
|
|
is_swing_high = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(int j = 1; j <= swing_bars; j++)
|
|
{
|
|
if(rates[i].low >= rates[i-j].low || rates[i].low >= rates[i+j].low)
|
|
{
|
|
is_swing_low = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(is_swing_high && g_SwingCount < MAX_SWINGS)
|
|
{
|
|
SwingPoint swing;
|
|
swing.type = SWING_HIGH;
|
|
swing.price = rates[i].high;
|
|
swing.time = rates[i].time;
|
|
swing.bar_index = i;
|
|
swing.is_higher_high = false;
|
|
swing.is_lower_high = false;
|
|
swing.is_structure_break = false;
|
|
|
|
g_SwingPoints[g_SwingCount] = swing;
|
|
g_SwingCount++;
|
|
}
|
|
|
|
if(is_swing_low && g_SwingCount < MAX_SWINGS)
|
|
{
|
|
SwingPoint swing;
|
|
swing.type = SWING_LOW;
|
|
swing.price = rates[i].low;
|
|
swing.time = rates[i].time;
|
|
swing.bar_index = i;
|
|
swing.is_higher_low = false;
|
|
swing.is_lower_low = false;
|
|
swing.is_structure_break = false;
|
|
|
|
g_SwingPoints[g_SwingCount] = swing;
|
|
g_SwingCount++;
|
|
}
|
|
}
|
|
|
|
SortSwingsByTime();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Sort Swings by Time |
|
|
//+------------------------------------------------------------------+
|
|
void SortSwingsByTime()
|
|
{
|
|
for(int i = 0; i < g_SwingCount - 1; i++)
|
|
{
|
|
for(int j = 0; j < g_SwingCount - i - 1; j++)
|
|
{
|
|
if(g_SwingPoints[j].time < g_SwingPoints[j+1].time)
|
|
{
|
|
SwingPoint temp = g_SwingPoints[j];
|
|
g_SwingPoints[j] = g_SwingPoints[j+1];
|
|
g_SwingPoints[j+1] = temp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Classify Swing Relationships |
|
|
//+------------------------------------------------------------------+
|
|
void ClassifySwingRelationships()
|
|
{
|
|
if(g_SwingCount < 2) return;
|
|
|
|
for(int i = 0; i < g_SwingCount; i++)
|
|
{
|
|
SwingPoint curr = g_SwingPoints[i];
|
|
|
|
for(int j = i + 1; j < g_SwingCount; j++)
|
|
{
|
|
SwingPoint previous = g_SwingPoints[j];
|
|
|
|
if(curr.type == previous.type)
|
|
{
|
|
if(curr.type == SWING_HIGH)
|
|
{
|
|
if(curr.price > previous.price)
|
|
{
|
|
g_SwingPoints[i].is_higher_high = true;
|
|
g_LastHighHigh = curr.price;
|
|
}
|
|
else
|
|
{
|
|
g_SwingPoints[i].is_lower_high = true;
|
|
g_LastLowHigh = curr.price;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(curr.price > previous.price)
|
|
{
|
|
g_SwingPoints[i].is_higher_low = true;
|
|
g_LastHighLow = curr.price;
|
|
}
|
|
else
|
|
{
|
|
g_SwingPoints[i].is_lower_low = true;
|
|
g_LastLowLow = curr.price;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Determine Overall Market Structure |
|
|
//+------------------------------------------------------------------+
|
|
void DetermineMarketStructure()
|
|
{
|
|
int hh_count = 0, hl_count = 0;
|
|
int lh_count = 0, ll_count = 0;
|
|
|
|
int lookback = MathMin(10, g_SwingCount);
|
|
|
|
for(int i = 0; i < lookback; i++)
|
|
{
|
|
if(g_SwingPoints[i].is_higher_high) hh_count++;
|
|
if(g_SwingPoints[i].is_higher_low) hl_count++;
|
|
if(g_SwingPoints[i].is_lower_high) lh_count++;
|
|
if(g_SwingPoints[i].is_lower_low) ll_count++;
|
|
}
|
|
|
|
ENUM_MARKET_STRUCTURE prev_structure = g_CurrentStructure;
|
|
|
|
if(hh_count >= 2 && hl_count >= 1)
|
|
{
|
|
g_CurrentStructure = STRUCTURE_UPTREND;
|
|
}
|
|
else if(lh_count >= 2 && ll_count >= 1)
|
|
{
|
|
g_CurrentStructure = STRUCTURE_DOWNTREND;
|
|
}
|
|
else
|
|
{
|
|
g_CurrentStructure = STRUCTURE_RANGING;
|
|
}
|
|
|
|
if(prev_structure != g_CurrentStructure && prev_structure != STRUCTURE_RANGING)
|
|
{
|
|
if(prev_structure == STRUCTURE_UPTREND && g_CurrentStructure == STRUCTURE_DOWNTREND)
|
|
{
|
|
g_BearishStructureBreak = true;
|
|
Print("📉 BEARISH STRUCTURE BREAK");
|
|
}
|
|
else if(prev_structure == STRUCTURE_DOWNTREND && g_CurrentStructure == STRUCTURE_UPTREND)
|
|
{
|
|
g_BullishStructureBreak = true;
|
|
Print("📈 BULLISH STRUCTURE BREAK");
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Detect Structure Breaks |
|
|
//+------------------------------------------------------------------+
|
|
void DetectStructureBreaks(const MqlRates &rates[])
|
|
{
|
|
if(g_SwingCount < 4) return;
|
|
|
|
double current_price = rates[0].close;
|
|
|
|
if(g_CurrentStructure == STRUCTURE_UPTREND)
|
|
{
|
|
for(int i = 0; i < g_SwingCount; i++)
|
|
{
|
|
if(g_SwingPoints[i].type == SWING_HIGH)
|
|
{
|
|
if(current_price > g_SwingPoints[i].price)
|
|
{
|
|
Print("⚡ BOS - Bullish structure continues");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(g_CurrentStructure == STRUCTURE_DOWNTREND)
|
|
{
|
|
for(int i = 0; i < g_SwingCount; i++)
|
|
{
|
|
if(g_SwingPoints[i].type == SWING_LOW)
|
|
{
|
|
if(current_price < g_SwingPoints[i].price)
|
|
{
|
|
Print("⚡ BOS - Bearish structure continues");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Structure Counters |
|
|
//+------------------------------------------------------------------+
|
|
void UpdateStructureCounters()
|
|
{
|
|
g_ConsecutiveHH = 0;
|
|
g_ConsecutiveHL = 0;
|
|
g_ConsecutiveLH = 0;
|
|
g_ConsecutiveLL = 0;
|
|
|
|
int lookback = MathMin(6, g_SwingCount);
|
|
|
|
for(int i = 0; i < lookback; i++)
|
|
{
|
|
if(g_SwingPoints[i].is_higher_high) g_ConsecutiveHH++;
|
|
if(g_SwingPoints[i].is_higher_low) g_ConsecutiveHL++;
|
|
if(g_SwingPoints[i].is_lower_high) g_ConsecutiveLH++;
|
|
if(g_SwingPoints[i].is_lower_low) g_ConsecutiveLL++;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get Structure Information Functions |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_MARKET_STRUCTURE GetCurrentStructure()
|
|
{
|
|
return g_CurrentStructure;
|
|
}
|
|
|
|
string GetStructureString()
|
|
{
|
|
switch(g_CurrentStructure)
|
|
{
|
|
case STRUCTURE_UPTREND: return "UPTREND (HH+HL)";
|
|
case STRUCTURE_DOWNTREND: return "DOWNTREND (LH+LL)";
|
|
case STRUCTURE_RANGING: return "RANGING";
|
|
case STRUCTURE_BREAKING: return "BREAKING";
|
|
default: return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
bool IsUptrend()
|
|
{
|
|
return g_CurrentStructure == STRUCTURE_UPTREND;
|
|
}
|
|
|
|
bool IsDowntrend()
|
|
{
|
|
return g_CurrentStructure == STRUCTURE_DOWNTREND;
|
|
}
|
|
|
|
bool IsRanging()
|
|
{
|
|
return g_CurrentStructure == STRUCTURE_RANGING;
|
|
}
|
|
|
|
int GetConsecutiveHH()
|
|
{
|
|
return g_ConsecutiveHH;
|
|
}
|
|
|
|
int GetConsecutiveHL()
|
|
{
|
|
return g_ConsecutiveHL;
|
|
}
|
|
|
|
int GetConsecutiveLH()
|
|
{
|
|
return g_ConsecutiveLH;
|
|
}
|
|
|
|
int GetConsecutiveLL()
|
|
{
|
|
return g_ConsecutiveLL;
|
|
}
|
|
|
|
bool IsBullishStructureBreak()
|
|
{
|
|
bool result = g_BullishStructureBreak;
|
|
g_BullishStructureBreak = false;
|
|
return result;
|
|
}
|
|
|
|
bool IsBearishStructureBreak()
|
|
{
|
|
bool result = g_BearishStructureBreak;
|
|
g_BearishStructureBreak = false;
|
|
return result;
|
|
}
|
|
|
|
double GetNearestSwingHigh()
|
|
{
|
|
for(int i = 0; i < g_SwingCount; i++)
|
|
{
|
|
if(g_SwingPoints[i].type == SWING_HIGH)
|
|
return g_SwingPoints[i].price;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
double GetNearestSwingLow()
|
|
{
|
|
for(int i = 0; i < g_SwingCount; i++)
|
|
{
|
|
if(g_SwingPoints[i].type == SWING_LOW)
|
|
return g_SwingPoints[i].price;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int GetStructureStrength()
|
|
{
|
|
int strength = 0;
|
|
|
|
if(g_CurrentStructure == STRUCTURE_UPTREND)
|
|
{
|
|
strength = (g_ConsecutiveHH * 20) + (g_ConsecutiveHL * 15);
|
|
}
|
|
else if(g_CurrentStructure == STRUCTURE_DOWNTREND)
|
|
{
|
|
strength = (g_ConsecutiveLH * 20) + (g_ConsecutiveLL * 15);
|
|
}
|
|
|
|
return MathMin(strength, 100);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Draw Structure on Chart |
|
|
//+------------------------------------------------------------------+
|
|
void DrawStructureOnChart()
|
|
{
|
|
ObjectsDeleteAll(0, "STRUCTURE_");
|
|
|
|
for(int i = 0; i < MathMin(20, g_SwingCount); i++)
|
|
{
|
|
string name = "STRUCTURE_SWING_" + IntegerToString(i);
|
|
|
|
if(g_SwingPoints[i].type == SWING_HIGH)
|
|
{
|
|
if(g_SwingPoints[i].is_higher_high)
|
|
{
|
|
ObjectCreate(0, name, OBJ_ARROW_DOWN, 0, g_SwingPoints[i].time,
|
|
g_SwingPoints[i].price);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clrLime);
|
|
ObjectSetString(0, name, OBJPROP_TEXT, "HH");
|
|
}
|
|
else if(g_SwingPoints[i].is_lower_high)
|
|
{
|
|
ObjectCreate(0, name, OBJ_ARROW_DOWN, 0, g_SwingPoints[i].time,
|
|
g_SwingPoints[i].price);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clrOrange);
|
|
ObjectSetString(0, name, OBJPROP_TEXT, "LH");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(g_SwingPoints[i].is_higher_low)
|
|
{
|
|
ObjectCreate(0, name, OBJ_ARROW_UP, 0, g_SwingPoints[i].time,
|
|
g_SwingPoints[i].price);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clrLime);
|
|
ObjectSetString(0, name, OBJPROP_TEXT, "HL");
|
|
}
|
|
else if(g_SwingPoints[i].is_lower_low)
|
|
{
|
|
ObjectCreate(0, name, OBJ_ARROW_UP, 0, g_SwingPoints[i].time,
|
|
g_SwingPoints[i].price);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clrRed);
|
|
ObjectSetString(0, name, OBJPROP_TEXT, "LL");
|
|
}
|
|
}
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8);
|
|
ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_CENTER);
|
|
}
|
|
}
|
|
|
|
void CleanupStructureObjects()
|
|
{
|
|
ObjectsDeleteAll(0, "STRUCTURE_");
|
|
} |