# Price Structure Suite Indicator Development Instructions_2 | Gold EA Download- MT4/MT5 Resources
//+------------------------------------------------------------------+ //| Price Structure Suite.mq4 | //| Visualizes structure, price action patterns, volume, MTF bias, | //| and risk metrics for discretionary traders. | //+------------------------------------------------------------------+ #property strict #property indicator_chart_window #property indicator_plots 4 #property indicator_label1 "Swing High" #property indicator_type1 DRAW_ARROW #property indicator_color1 clrTomato #property indicator_style1 STYLE_SOLID #property indicator_width1 1 #property indicator_label2 "Swing Low" #property indicator_type2 DRAW_ARROW #property indicator_color2 clrDeepSkyBlue #property indicator_style2 STYLE_SOLID #property indicator_width2 1 #property indicator_label3 "Volume Boost" #property indicator_type3 DRAW_HISTOGRAM #property indicator_color3 clrGold #property indicator_style3 STYLE_SOLID #property indicator_width3 2 #property indicator_label4 "Structure Bias" #property indicator_type4 DRAW_LINE #property indicator_color4 clrSilver #property indicator_style4 STYLE_DOT #property indicator_width4 1 //--- inputs input int SwingDepth = 5; // fractal depth input int MaxLookbackBars = 800; // max bars processed input int VolumeLookback = 20; // volume average window input double VolumeFactor = 1.6; // volume spike factor input ENUM_TIMEFRAMES HigherTF = PERIOD_H4; input int HigherMAPeriod = 55; input int AtrPeriod = 14; input double RiskPercent = 1.0; // account risk per trade (percent) input double AccountValueOverride = 0.0; // custom equity override input double PatternTolerancePips = 20.0; // allowed deviation for patterns input double LabelOffsetPips = 15.0; // text/arrow offset input bool EnableHeadShoulders = true; input bool EnableTriangles = true; input bool EnableDoubleTopBottom = true; //--- buffers double SwingHighBuffer[]; double SwingLowBuffer[]; double VolumeBoostBuffer[]; double StructureBiasBuffer[]; //--- internal state #define MAX_SWINGS 20 string PatternPrefix = "PS_PATTERN"; int patternIdCounter = 0; int swingIndex[MAX_SWINGS]; double swingPrice[MAX_SWINGS]; int swingType[MAX_SWINGS]; // 1 = high, -1 = low datetime swingTime[MAX_SWINGS]; int swingCount = 0; //--- pattern anchors int lastDoubleTopIdx = -1; int lastDoubleBottomIdx = -1; int lastHSPatternIdx = -1; int lastInvHSPatternIdx = -1; int lastAscTriangleIdx = -1; int lastDescTriangleIdx = -1; //--- function declarations bool IsSwingHigh(const double &high[], int index); bool IsSwingLow(const double &low[], int index); void RegisterSwing(int index, double price, int type, datetime time); int DetermineStructureBias(); int HigherTimeframeBias(datetime t); void EvaluatePatterns(); bool DetectDoubleTop(double tolerance); bool DetectDoubleBottom(double tolerance); bool DetectHeadAndShoulders(double tolerance); bool DetectInverseHeadAndShoulders(double tolerance); bool DetectAscendingTriangle(double tolerance); bool DetectDescendingTriangle(double tolerance); void RenderPattern(int arrayPos, const string label, color clr, bool placeAbove); double SimpleAverageVolume(const long &tick_volume[], int index); void UpdateRiskDashboard(); double PipValue(); //+--------------------------------------------------------------------------------+ int OnInit() { IndicatorSetString(INDICATOR_SHORTNAME, "Price Structure Suite"); SetIndexBuffer(0, SwingHighBuffer, INDICATOR_DATA); SetIndexBuffer(1, SwingLowBuffer, INDICATOR_DATA); SetIndexBuffer(2, VolumeBoostBuffer, INDICATOR_DATA); SetIndexBuffer(3, StructureBiasBuffer, INDICATOR_DATA); ArraySetAsSeries(SwingHighBuffer, true); ArraySetAsSeries(SwingLowBuffer, true); ArraySetAsSeries(VolumeBoostBuffer, true); ArraySetAsSeries(StructureBiasBuffer, true); PlotIndexSetInteger(0, PLOT_ARROW, 234); // up arrow PlotIndexSetInteger(1, PLOT_ARROW, 241); // down arrow PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(2, PLOT_SHOW_DATA, true); PlotIndexSetInteger(3, PLOT_SHOW_DATA, true); SetIndexDrawBegin(2, VolumeLookback + SwingDepth); SetIndexDrawBegin(3, SwingDepth); IndicatorDigits(Digits()); return(INIT_SUCCEEDED); } //+----------------------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0, PatternPrefix); } //+----------------------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(rates_total < SwingDepth 2 + 10) return prev_calculated; int barsToProcess = rates_total; if(MaxLookbackBars > 0 && MaxLookbackBars < rates_total) barsToProcess = MaxLookbackBars; // reset buffers for(int i = 0; i < rates_total; i++) { SwingHighBuffer[i] = EMPTY_VALUE; SwingLowBuffer[i] = EMPTY_VALUE; VolumeBoostBuffer[i] = 0.0; StructureBiasBuffer[i] = EMPTY_VALUE; } // reset swing and pattern state swingCount = 0; for(int j = 0; j < MAX_SWINGS; j++) { swingIndex[j] = -1; swingPrice[j] = 0.0; swingType[j] = 0; swingTime[j] = 0; } lastDoubleTopIdx = -1; lastDoubleBottomIdx = -1; lastHSPatternIdx = -1; lastInvHSPatternIdx = -1; lastAscTriangleIdx = -1; lastDescTriangleIdx = -1; patternIdCounter = 0; ObjectsDeleteAll(0, PatternPrefix); int startIndex = MathMin(rates_total - SwingDepth - 1, barsToProcess); for(int i = startIndex; i >= SwingDepth; i--) { bool swingHigh = IsSwingHigh(high, i); bool swingLow = IsSwingLow(low, i); if(swingHigh) { SwingHighBuffer[i] = high[i] + LabelOffsetPips _Point; RegisterSwing(i, high[i], 1, time[i]); EvaluatePatterns(); } else if(swingLow) { SwingLowBuffer[i] = low[i] - LabelOffsetPips _Point; RegisterSwing(i, low[i], -1, time[i]); EvaluatePatterns(); } int bias = DetermineStructureBias(); int higherBias = HigherTimeframeBias(time[i]); StructureBiasBuffer[i] = bias + higherBias; double avgVol = SimpleAverageVolume(tick_volume, i); if(avgVol > 0 && tick_volume[i] > avgVol VolumeFactor && (bias != 0 || higherBias != 0)) VolumeBoostBuffer[i] = (double)tick_volume[i]; else VolumeBoostBuffer[i] = 0.0; } UpdateRiskDashboard(); return(rates_total); } //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high[], int index) { for(int k = 1; k <= SwingDepth; k++) { if(high[index] <= high[index - k] || high[index] < high[index + k]) return(false); } return(true); } //+------------------------------------------------------------------+ bool IsSwingLow(const double &low[], int index) { for(int k = 1; k <= SwingDepth; k++) { if(low[index] >= low[index - k] || low[index] > low[index + k]) return(false); } return(true); } //+--------------------------------------------------------------------------------+ void RegisterSwing(int index, double price, int type, datetime t) { for(int i = MAX_SWINGS - 1; i > 0; i--) { swingIndex[i] = swingIndex[i - 1]; swingPrice[i] = swingPrice[i - 1]; swingType[i] = swingType[i - 1]; swingTime[i] = swingTime[i - 1]; } swingIndex[0] = index; swingPrice[0] = price; swingType[0] = type; swingTime[0] = t; if(swingCount < MAX_SWINGS) swingCount++; } //+----------------------------------------------------------------------------------+ int DetermineStructureBias() { if(swingCount < 4) return(0); double lastHigh = -1.0; double lastLow = -1.0, prevLow = -1.0; for(int i = 0; i < swingCount; i++) { if(swingType[i] == 1) { if(lastHigh < 0.0) lastHigh = swingPrice[i]; else if(prevHigh < 0.0) prevHigh = swingPrice[i]; } else if(swingType[i] == -1) { if(lastLow < 0.0) lastLow = swingPrice[i]; else if(prevLow < 0.0) prevLow = swingPrice[i]; } if(lastHigh > 0 && prevHigh > 0 && lastLow > 0 && prevLow > 0) break; } if(lastHigh < 0 || prevHigh < 0 || lastLow < 0 || prevLow < 0) return(0); if(lastHigh > prevHigh && lastLow > prevLow) return(1); if(lastHigh < prevHigh && lastLow < prevLow) return(-1); return(0); } //+--------------------------------------------------------------------------------+ int HigherTimeframeBias(datetime t) { if(HigherTF == PERIOD_CURRENT) return(0); int shift = iBarShift(NULL, HigherTF, t, true); if(shift < 0) return(0); double ma = iMA(NULL, HigherTF, HigherMAPeriod, 0, MODE_EMA, PRICE_CLOSE, shift); double closePrice = iClose(NULL, HigherTF, shift); if(closePrice > ma) return(1); if(closePrice < ma) return(-1); return(0); } //+------------------------------------------------------------------+ void EvaluatePatterns() { if(swingCount < 3) return; double tolerance = PatternTolerancePips _Point; if(tolerance <= 0) tolerance = 10 Point; if(EnableDoubleTopBottom) { if(swingType[0] == 1 && DetectDoubleTop(tolerance) && lastDoubleTopIdx != swingIndex[0]) { RenderPattern(0, "Double Top", clrOrangeRed, true); lastDoubleTopIdx = swingIndex[0]; } if(swingType[0] == -1 && DetectDoubleBottom(tolerance) && lastDoubleBottomIdx != swingIndex[0]) { RenderPattern(0, "Double Bottom", clrDeepSkyBlue, false); lastDoubleBottomIdx = swingIndex[0]; } } if(EnableHeadShoulders) { if(swingType[0] == 1 && DetectHeadAndShoulders(tolerance) && lastHSPatternIdx != swingIndex[0]) { RenderPattern(0, "Head & Shoulders", clrFireBrick, true); lastHSPatternIdx = swingIndex[0]; } if(swingType[0] == -1 && DetectInverseHeadAndShoulders(tolerance) && lastInvHSPatternIdx != swingIndex[0]) { RenderPattern(0, "Inverse H&S", clrDodgerBlue, false); lastInvHSPatternIdx = swingIndex[0]; } } if(EnableTriangles) { if(swingType[0] == -1 && DetectAscendingTriangle(tolerance) && lastAscTriangleIdx != swingIndex[0]) { RenderPattern(0, "Ascending Triangle", clrMediumSeaGreen, false); lastAscTriangleIdx = swingIndex[0]; } if(swingType[0] == 1 && DetectDescendingTriangle(tolerance) && lastDescTriangleIdx != swingIndex[0]) { RenderPattern(0, "Descending Triangle", clrDarkOrange, true); lastDescTriangleIdx = swingIndex[0]; } } } //+--------------------------------------------------------------------------------+ bool DetectDoubleTop(double tolerance) { int firstHigh = -1, secondHigh = -1; for(int i = 0; i < swingCount; i++) { if(swingType[i] != 1) continue; if(firstHigh == -1) firstHigh = i; else { secondHigh = i; break; } } if(firstHigh == -1 || secondHigh == -1) return(false); if(MathAbs(swingPrice[firstHigh] - swingPrice[secondHigh]) > tolerance) return(false); for(int j = firstHigh + 1; j < secondHigh; j++) { if(swingType[j] == -1) return(true); } return(false); } //+--------------------------------------------------------------------------------+ bool DetectDoubleBottom(double tolerance) { int firstLow = -1, secondLow = -1; for(int i = 0; i < swingCount; i++) { if(swingType[i] != -1) continue; if(firstLow == -1) firstLow = i; else { secondLow = i; break; } } if(firstLow == -1 || secondLow == -1) return(false); if(MathAbs(swingPrice[firstLow] - swingPrice[secondLow]) > tolerance) return(false); for(int j = firstLow + 1; j < secondLow; j++) { if(swingType[j] == 1) return(true); } return(false); } //+--------------------------------------------------------------------------------+ bool DetectHeadAndShoulders(double tolerance) { if(swingCount < 5) return(false); if(!(swingType[0] == 1 && swingType[1] == -1 && swingType[2]== 1 && swingType[3] == -1 && swingType[4] == 1)) return(false); double right = swingPrice[0]; double necklineRight = swingPrice[1]; double head = swingPrice[2]; double necklineLeft = swingPrice[3]; double left = swingPrice[4]; if(MathAbs(left - right) > tolerance) return(false); double neckline = (necklineLeft + necklineRight) / 2.0; if(head <= left || head <= right) return(false); if(MathAbs(necklineRight - necklineLeft) > tolerance) return(false); if(head - neckline < tolerance 0.5) return(false); return(true); } //+--------------------------------------------------------------------------------+ bool DetectInverseHeadAndShoulders(double tolerance) { if(swingCount < 5) return(false); if(!(swingType[0] == -1 && swingType[1] == 1 && swingType[2] == -1 && swingType[3] == 1 && swingType[4] == -1)) return(false); double right = swingPrice[0]; double necklineRight = swingPrice[1]; double head = doublePrice[2]; necklineLeft = swingPrice[3]; double left = swingPrice[4]; if(MathAbs(left - right) > tolerance) return(false); double neckline = (necklineLeft + necklineRight) / 2.0; if(head >= left || head >= right) return(false); if(MathAbs(necklineRight - necklineLeft) > tolerance) return(false); if(neckline - head < tolerance 0.5) return(false); return(true); } //+------------------------------------------------------------------+ bool DetectAscendingTriangle(double tolerance) { if(swingCount < 5) return(false); double highs[2]; double lows[2]; int hCount = 0, lCount = 0; for(int i = 0; i < swingCount && (hCount < 2 || lCount < 3); i++) { if(swingType[i] == 1 && hCount < 2) highs[hCount++] = swingPrice[i]; else if(swingType[i] == -1 && lCount < 3) lows[lCount++] = swingPrice[i]; } if(hCount < 2 || lCount < 3) return(false); if(MathAbs(highs[0] - highs[1]) > tolerance) return(false); if(!(lows[0] > lows[1] && lows[1] > lows[2])) return(false); return(true); } //+------------------------------------------------------------------+ bool DetectDescendingTriangle(double tolerance) { if(swingCount < 5) return(false); double highs[3]; double lows[2]; int hCount = 0, lCount = 0; for(int i = 0; i < swingCount && (hCount < 3 || lCount < 2); i++) { if(swingType[i] == 1 && hCount < 3) highs[hCount++] = swingPrice[i]; else if(swingType[i] == -1 && lCount < 2) lows[lCount++] = swingPrice[i]; } if(hCount < 3 || lCount < 2) return(false); if(!(highs[0] < highs[1] && highs[1] < highs[2])) return(false); if(MathAbs(lows[0] - lows[1]) > tolerance) return(false); return(true); } //+--------------------------------------------------------------------------------+ void RenderPattern(int arrayPos, const string label, color clr, bool placeAbove) { if(arrayPos >= swingCount || arrayPos < 0) return; string name = PatternPrefix + "" + IntegerToString(patternIdCounter++); double offset = LabelOffsetPips _Point; double price = swingPrice[arrayPos]; datetime t = swingTime[arrayPos]; if(placeAbove) price += offset; else price -= offset; if(!ObjectCreate(0, name, OBJ_TEXT, 0, t, price)) return; ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_ANCHOR, placeAbove ? ANCHOR_LOWER : ANCHOR_UPPER); ObjectSetInteger(0, name, OBJPROP_BACK, false); ObjectSetText(name, label, 8, "Arial", clr); } //+------------------------------------------------------------------+ double SimpleAverageVolume(const long &tick_volume[], int index) { double sum = 0.0; int count = 0; for(int i = 0; i < VolumeLookback && (index + i) < Bars; i++) { sum += (double)tick_volume[index + i]; count++; } if(count == 0) return(0.0); return(sum / count); } //+------------------------------------------------------------------+ double PipValue() { double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE); double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE); if(tickValue <= 0.0 || tickSize <= 0.0) return(0.0); return(tickValue / tickSize _Point); } //+--------------------------------------------------------------------+ void UpdateRiskDashboard() { double equity = (AccountValueOverride > 0.0) ? AccountValueOverride : AccountEquity(); double atr = iATR(NULL, 0, AtrPeriod, 0); double pipVal = PipValue(); double riskMoney = equity (RiskPercent / 100.0); double suggestedLots = 0.0; double atrPips = 0.0; if(atr > 0.0 && pipVal > 0.0) { atrPips = atr / _Point; double riskPerLot = atr pipVal; if(riskPerLot > 0.0) suggestedLots = riskMoney / riskPerLot; } string text = StringFormat("ATR(%d): %.1f pips | Risk %.2f%% approx %.2f | Suggested lots approx %.2f", AtrPeriod, atrPips, RiskPercent, riskMoney, suggestedLots); Comment(text); } //+---------------------------------------------------------------------------------+
💡 Featured Recommendations
✍️ Latest by the author
- •
- •
- •
- •
- •
- •
📌 Popular topics
- •
- •
- •
- •
- •
- •
- •
- •
🔗 You May Be Interested In
- •
- •
- •
- •
- •
- •