//+------------------------------------------------------------------+ //| 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_"); }