This MT5 indicator detects Crash market spike patterns using a specific 3-candle formation:
-
Red-Green-Red candle pattern (where red candles are strong spikes and the green is a middle retracement).
-
When the pattern is found, it draws a box around the high/low of those 3 candles.
-
It also plots a horizontal entry line at the middle candle's open price.
-
The line stays active until price returns to that level (mitigation).
-
Once the entry price is hit (mitigated), the entry line is replaced by a shorter fixed line from the pattern to the mitigation candle.
-
It works on both historical and real-time candles.
-
Visually helps identify potential return-to-zone trading opportunities.

| Input Name | Description |
|---|---|
| BoxColor | Color of the zone box (default: Red). |
| LineColor | Color of the entry line (default: Dodger Blue). |
| BoxWidth | Thickness of the zone box border. |
| LineWidth | Thickness of the entry line. |
| BoxStyle | Style of the box border (solid, dashed, etc.). |
| LineStyle | Style of the entry line (solid, dashed, etc.). |
//==== Input Parameters ==== input color BoxColor = clrRed; input color LineColor = clrDodgerBlue; input int BoxWidth = 2; input int LineWidth = 1; input ENUM_LINE_STYLE BoxStyle = STYLE_SOLID; input ENUM_LINE_STYLE LineStyle = STYLE_SOLID;
//+------------------------------------------------------------------+ //| CrashSpikeBoxMitigationFinal.mq5 | //| Spike pattern + entry line that expires at mitigation | //+------------------------------------------------------------------+ #property indicator_chart_window #property version "1.0" #property strict //==== Input Parameters ==== input color BoxColor = clrRed; input color LineColor = clrDodgerBlue; input int BoxWidth = 2; input int LineWidth = 1; input ENUM_LINE_STYLE BoxStyle = STYLE_SOLID; input ENUM_LINE_STYLE LineStyle = STYLE_SOLID; //==== Zone Struct ==== struct Zone { datetime boxTime; double entryPrice; string lineName; bool mitigated; }; Zone zones[100]; int zoneCount = 0; datetime lastProcessedTime = 0; //+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ int OnInit() { zoneCount = 0; lastProcessedTime = 0; ObjectsDeleteAll(0, "Box_"); ObjectsDeleteAll(0, "Line_"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| OnDeinit | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0, "Box_"); ObjectsDeleteAll(0, "Line_"); } //+------------------------------------------------------------------+ //| OnCalculate - Main processing function | //+------------------------------------------------------------------+ 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[]) { // Process historical data on first run or when new data arrives if(prev_calculated == 0 || rates_total > prev_calculated) { ProcessHistory(rates_total, time, open, high, low, close); } // Process real-time data if(TimeCurrent() > lastProcessedTime) { ProcessRealtime(); lastProcessedTime = TimeCurrent(); } return rates_total; } //+------------------------------------------------------------------+ //| Process historical data | //+------------------------------------------------------------------+ void ProcessHistory(const int rates_total, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[]) { if(rates_total < 10) return; // ---- Pattern Detection ---- for(int i = rates_total - 4; i >= 2; i--) { // Skip if we've already processed this candle if(time[i+2] <= lastProcessedTime) continue; double body1 = MathAbs(open[i] - close[i]); double wick1 = high[i] - low[i]; bool isRedSpike1 = close[i] < open[i] && body1 > 0.7 * wick1; bool isGreenMid = close[i+1] > open[i+1]; double body3 = MathAbs(open[i+2] - close[i+2]); double wick3 = high[i+2] - low[i+2]; bool isRedSpike2 = close[i+2] < open[i+2] && body3 > 0.7 * wick3; if(isRedSpike1 && isGreenMid && isRedSpike2) { CreateZone(time[i], time[i+2], high[i], high[i+1], high[i+2], low[i], low[i+1], low[i+2], open[i+1]); } } // ---- Mitigation Detection ---- CheckMitigationHistory(rates_total, time, low, high); } //+------------------------------------------------------------------+ //| Process real-time data | //+------------------------------------------------------------------+ void ProcessRealtime() { MqlRates rt[]; CopyRates(NULL, 0, 0, 4, rt); // Get last 4 candles if(ArraySize(rt) < 4) return; // Check if current candle is complete (for M1, wait until minute change) if(TimeCurrent() - rt[0].time < PeriodSeconds()) return; // Check pattern on completed candles for(int i = 3; i >= 2; i--) { // Skip if we've already processed this candle if(rt[i].time <= lastProcessedTime) continue; double body1 = MathAbs(rt[i-2].open - rt[i-2].close); double wick1 = rt[i-2].high - rt[i-2].low; bool isRedSpike1 = rt[i-2].close < rt[i-2].open && body1 > 0.7 * wick1; bool isGreenMid = rt[i-1].close > rt[i-1].open; double body3 = MathAbs(rt[i].open - rt[i].close); double wick3 = rt[i].high - rt[i].low; bool isRedSpike2 = rt[i].close < rt[i].open && body3 > 0.7 * wick3; if(isRedSpike1 && isGreenMid && isRedSpike2) { CreateZone(rt[i-2].time, rt[i].time, rt[i-2].high, rt[i-1].high, rt[i].high, rt[i-2].low, rt[i-1].low, rt[i].low, rt[i-1].open); } } // Check for mitigation in real-time CheckMitigationRealtime(); } //+------------------------------------------------------------------+ //| Create a new zone | //+------------------------------------------------------------------+ void CreateZone(datetime startTime, datetime endTime, double high1, double high2, double high3, double low1, double low2, double low3, double entryPrice) { // Check duplicate bool exists = false; for(int j = 0; j < zoneCount; j++) { if(zones[j].boxTime == endTime) { exists = true; break; } } if(!exists && zoneCount < ArraySize(zones)) { double top = MathMax(MathMax(high1, high2), high3); double bottom = MathMin(MathMin(low1, low2), low3); string boxName = "Box_" + IntegerToString((int)endTime); ObjectCreate(0, boxName, OBJ_RECTANGLE, 0, startTime, top, endTime, bottom); ObjectSetInteger(0, boxName, OBJPROP_COLOR, BoxColor); ObjectSetInteger(0, boxName, OBJPROP_WIDTH, BoxWidth); ObjectSetInteger(0, boxName, OBJPROP_STYLE, BoxStyle); ObjectSetInteger(0, boxName, OBJPROP_BACK, true); // Draw extending entry line datetime futureTime = TimeCurrent() + PeriodSeconds() * 100; string lineName = "Line_" + IntegerToString((int)endTime); ObjectCreate(0, lineName, OBJ_TREND, 0, endTime, entryPrice, futureTime, entryPrice); ObjectSetInteger(0, lineName, OBJPROP_COLOR, LineColor); ObjectSetInteger(0, lineName, OBJPROP_WIDTH, LineWidth); ObjectSetInteger(0, lineName, OBJPROP_STYLE, LineStyle); zones[zoneCount].boxTime = endTime; zones[zoneCount].entryPrice = entryPrice; zones[zoneCount].lineName = lineName; zones[zoneCount].mitigated = false; zoneCount++; } } //+------------------------------------------------------------------+ //| Check for mitigation in historical data | //+------------------------------------------------------------------+ void CheckMitigationHistory(const int rates_total, const datetime &time[], const double &low[], const double &high[]) { for(int j = 0; j < zoneCount; j++) { if(zones[j].mitigated) continue; for(int i = 1; i < rates_total; i++) { if(time[i] <= zones[j].boxTime) continue; if(low[i] <= zones[j].entryPrice && high[i] >= zones[j].entryPrice) { MitigateZone(j, time[i]); break; } } } } //+------------------------------------------------------------------+ //| Check for mitigation in real-time | //+------------------------------------------------------------------+ void CheckMitigationRealtime() { MqlRates current[]; CopyRates(NULL, 0, 0, 1, current); if(ArraySize(current) < 1) return; for(int j = 0; j < zoneCount; j++) { if(zones[j].mitigated) continue; if(current[0].low <= zones[j].entryPrice && current[0].high >= zones[j].entryPrice) { MitigateZone(j, current[0].time); } } } //+------------------------------------------------------------------+ //| Handle zone mitigation | //+------------------------------------------------------------------+ void MitigateZone(int zoneIndex, datetime mitigationTime) { // Delete old line if(ObjectFind(0, zones[zoneIndex].lineName) >= 0) ObjectDelete(0, zones[zoneIndex].lineName); // Draw fixed-length line to mitigation candle string fixedName = zones[zoneIndex].lineName + "_Fixed"; ObjectCreate(0, fixedName, OBJ_TREND, 0, zones[zoneIndex].boxTime, zones[zoneIndex].entryPrice, mitigationTime, zones[zoneIndex].entryPrice); ObjectSetInteger(0, fixedName, OBJPROP_COLOR, LineColor); ObjectSetInteger(0, fixedName, OBJPROP_WIDTH, LineWidth); ObjectSetInteger(0, fixedName, OBJPROP_STYLE, LineStyle); zones[zoneIndex].mitigated = true; }
This indicator detects a specific bearish spike formation over 3 candles:
-
Candle 1:
-
Bearish (Red candle).
-
Long wick.
-
Body must be greater than 70% of the total candle range (i.e., a strong spike).
-
-
Candle 2 (Middle):
-
Bullish (Green candle).
-
Can be any size; just needs to be positive.
-
-
Candle 3:
-
Bearish again.
-
Same condition as Candle 1: body > 70% of full range.
-
When this pattern is found:
-
It identifies a "supply zone" created by aggressive selling.
-
A box is drawn around the high and low of the 3 candles.
-
An entry line is drawn at the open price of the middle (green) candle, expecting price to return.
Core Idea / Strategy Concept
This tool is based on the Smart Money Concept (SMC) — particularly the idea of mitigation zones and supply/demand imbalances.
-
After a strong bearish impulse, price often retraces back to the origin of the move (the middle candle).
-
The indicator marks that area visually as a "mitigation zone".
-
Once price returns to this zone (entry line), it is considered "mitigated", and a trader may look for re-entry or reaction.
-
It removes the entry line after mitigation to keep the chart clean and optionally draws a shorter line to show mitigation.
Practical Use:
-
Look for return-to-box trades.
-
Can be used for sell setups on Crash markets (or Boom with inversion).
-
Works great in confluence with other indicators (like EMA, OBV, or order block confirmations).