Building A Candlestick Trend Constraint Model (Part 11): Revealing Lower-Timeframe Triggers Within Higher-Timeframe Candle Relationships
A higher-timeframe candle gives us a broad view of price direction, but its final shape hides the sequence of lower-timeframe events. I returned to our candlestick research to make that sequence explicit. In this article, we develop an A-B-C-D model in which completed A-B candles select the directional rules, C provides the first qualifying setup, and D supplies a closed-bar confirmation window for optional execution.
Contents
- Objective and Scope
- The Four-Candle Constraint Model
- Classifying Candles A and B
- Scanning Trigger Bars Inside Candle C
- Confirmation and Invalidation Inside Candle D
- System Architecture
- Implementation
- Operating the Expert Advisor
- Planning Compilation and Runtime Tests
- Limitations
- Conclusion
- Key Lessons
- Attachments
Objective and Scope
Our first article studied the internal development of a daily candle. In Part 10, we combined trend constraints with Golden and Death Cross logic. We now use the completed higher-timeframe relationship to select the lower-timeframe conditions we want to examine. A-B supplies our directional hypothesis, and the C-D sequence makes its event order explicit.
Candles A and B define the context. Candle C is accepted when its lower-timeframe bars meet the retest and displacement rules, and Candle D provides the execution window. We use accepted to mean a match to those rules. We assess the trading value of the resulting signals through separate experiments.
We want an Expert Advisor (EA) that distinguishes a completed context from a developing trigger. The state model answers four operational questions:
- What completed relationship exists between Candles A and B?
- Did the configured trigger bars inside C satisfy the retest and structure-break rules?
- Did a completed trigger bar inside D close beyond the C extreme and buffer?
- Did the examined pre-trigger closes remain within the protected-level rule?
The defaults are D1 for Candles A-D and M1 for trigger bars. InpContextTimeframe and InpTriggerTimeframe select the periods independently of the host chart. Initialization requires positive period lengths and a trigger period shorter than the context period; it does not verify boundary alignment. We use D1/M1 as the worked configuration. Other combinations require separate synchronization checks. Trading is optional and disabled by default.
The Four-Candle Constraint Model
When D opens, A, B, and C are completed context bars. The EA classifies A-B and requests C history without using D’s eventual close. Live decisions use a completed bar from InpTriggerTimeframe, keeping the developing trigger bar outside the decision.
| Candle | Status when D opens | Responsibility | Data used |
|---|---|---|---|
| A | Closed | Defines the original range and direction | Context-timeframe OHLC |
| B | Closed | Continues, reverses, or compresses A | Context-timeframe OHLC relative to A |
| C | Closed | Retests the reference and arms the sequence | Trigger bars whose opening times lie between C open and D open |
| D | Developing | Confirms, invalidates, or expires the sequence | Newly observed completed trigger bars |
RefreshContextSequence requests four records from InpContextTimeframe on the chart symbol. With series indexing enabled, records 0, 1, 2, and 3 represent D, C, B, and A respectively. Changing the host chart period does not change these assignments when both timeframe inputs are explicit; PERIOD_CURRENT would make the selected input follow the chart.
An unclassified A-B pair or a C scan without an accepted setup is labeled SEQUENCE_EXPIRED immediately; in this implementation that state also means inactive. An accepted C setup produces SEQUENCE_TRACK_D. A pre-trigger D close may then produce SEQUENCE_TRIGGERED or SEQUENCE_INVALIDATED. At the next context opening, the EA logs the old state and initializes a new sequence. An unconfirmed tracking state is not explicitly relabeled expired before that log.

Fig. 1. A-B context, the C minute setup, and the D confirmation workflow
Classifying Candles A and B
The classifier intentionally uses a small collection of explainable relationships. It does not attempt to name every traditional candlestick pattern. Each accepted relationship must supply either a direction or a controlled way for C to choose one.
| Relationship | A-B condition | Reference level | Protected level |
|---|---|---|---|
| Bullish continuation | A and B are bullish; B closes above the A high plus the breakout buffer | A high | Lower of the A and B lows |
| Bearish continuation | A and B are bearish; B closes below the A low minus the breakout buffer | A low | Higher of the A and B highs |
| Bullish reversal | A is bearish and B is bullish; B low < A low − buffer and B close ≥ BullishRecovery | A low | B low |
| Bearish reversal | A is bullish and B is bearish; B high > A high + buffer and B close ≤ BearishRecovery | A high | B high |
| Inside-bar compression | B high < A high − buffer and B low > A low + buffer | Boundary broken by C | Opposite A boundary |
| Unclassified | No listed condition is satisfied | None | None; no signal is permitted |
The price buffer is InpBreakBufferPoints multiplied by SYMBOL_POINT. Continuation, sweep, compression, and structure-break comparisons use strict inequalities at their buffered boundaries. The reversal recovery thresholds are:
BullishRecovery = A low + (A high - A low) × f
BearishRecovery = A high - (A high - A low) × f
A low and A high define Candle A’s range, and f is InpReversalCloseFraction, strictly between zero and one. Recovery includes equality at the threshold: a bullish B may close exactly at BullishRecovery, and a bearish B may close exactly at BearishRecovery. Equality at a sweep or buffered compression boundary does not qualify. A doji is neither bullish nor bearish, although it may still satisfy the compression range test. A nonpositive A range is unclassified. The classifier tests compression first, then continuation, then reversal.
The following extract is the complete classifier used by our EA. Because A and B are completed records, its output remains fixed throughout Candle D.
//+------------------------------------------------------------------+ //| Classifies the completed relationship between Candles A and B | //+------------------------------------------------------------------+ ENUM_AB_RELATION ClassifyAB(const MqlRates &a,const MqlRates &b) { double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); double buffer=InpBreakBufferPoints*point; double a_range=a.high-a.low; if(a_range<=0.0) return(AB_RELATION_NONE); //--- Compression is directionless until Candle C breaks a boundary if(b.high<a.high-buffer && b.low>a.low+buffer) return(AB_INSIDE_COMPRESSION); //--- Two aligned candles plus a close beyond A define continuation if(IsBullish(a) && IsBullish(b) && b.close>a.high+buffer) return(AB_BULLISH_CONTINUATION); if(IsBearish(a) && IsBearish(b) && b.close<a.low-buffer) return(AB_BEARISH_CONTINUATION); //--- A sweep and recovery through a configurable part of A define reversal double bullish_recovery=a.low+a_range*InpReversalCloseFraction; double bearish_recovery=a.high-a_range*InpReversalCloseFraction; if(IsBearish(a) && IsBullish(b) && b.low<a.low-buffer && b.close>=bullish_recovery) return(AB_BULLISH_REVERSAL); if(IsBullish(a) && IsBearish(b) && b.high>a.high+buffer && b.close<=bearish_recovery) return(AB_BEARISH_REVERSAL); return(AB_RELATION_NONE); }
Continuation requires two aligned candle bodies and a confirmed close beyond A. Reversal requires opposing candle directions, a sweep, and recovery. Compression deliberately starts without a direction because Candle C must reveal which A boundary the lower-timeframe path accepts.
Scanning Trigger Bars Inside Candle C
The time-range form of CopyRates selects C’s trigger records by their opening timestamps, from C open through D open minus one second. The stop time excludes a record opening at D. The EA scans the returned records chronologically once the minimum count is available. History coverage remains a separate check, described below.
For a directional continuation or reversal, C must complete two stages:
- A trigger candle meets the reference-level retest inequalities below.
- A displacement close breaks the previous InpSwingDepth trigger highs or lows by the buffer. In the directional scanner, the retest and break may occur on the same bar.
Let r be the reference, t be InpRetestTolerancePoints × SYMBOL_POINT, and b be the price buffer. A touch is defined by the following inequalities, not by an absolute distance from every OHLC value:
| Scanner and direction | Retest condition |
|---|---|
| Directional bullish | Low ≤ r + t and Close ≥ r − b |
| Directional bearish | High ≥ r − t and Close ≤ r + b |
| Compression bullish | Low ≤ r + t and Close ≥ r |
| Compression bearish | High ≥ r − t and Close ≤ r |
These are one-sided tests: a wick can extend through the reference if the close meets its condition and does not invalidate protection. The directional touched flag remains set once accepted. Both scanners return at the first qualifying structure break and store that bar’s opening time in c_setup_time. They do not select a later or stronger setup, and they do not scan the remainder of C for a later cancellation. Thus C protection is checked only through the accepted setup bar.
A displacement candle is not defined by color alone. Its body must occupy a minimum proportion of its complete range:
R_body = |Close - Open| / (High - Low)
Open, High, Low, and Close belong to the examined trigger bar. IsDisplacement first rejects High − Low ≤ 0, so it never divides by a zero range. For a positive range, R_body must be at least InpMinBodyRangeRatio, and the body must have the required direction. Equality at the default ratio of 0.55 passes. The ratio, buffer, retest tolerance, recovery fraction, and swing depth are design defaults, not statistically estimated thresholds.
Compression has three distinct bar stages: a displacement close outside A plus the buffer, a subsequent retest of that broken boundary, and a later displacement structure break. The first qualifying breakout in chronological order fixes the direction. The scanner does not switch to the opposite direction; a later close beyond its protected A boundary cancels the scan. The breakout and retest stages each continue to the next bar, so neither can also serve as the final structure-break stage.
AnalyzeCSetup requests the configured trigger history and enforces a minimum count before dispatching to the compression or directional scanner. It does not test every expected bar time against the symbol’s trading sessions.
//+------------------------------------------------------------------+ //| Loads trigger bars inside C and checks the setup | //+------------------------------------------------------------------+ bool AnalyzeCSetup(void) { //--- Load every trigger bar that closed before Candle D opened datetime stop_time=g_sequence.d.time-1; MqlRates trigger_bars[]; ResetLastError(); int copied=CopyRates(_Symbol,InpTriggerTimeframe, g_sequence.c.time,stop_time,trigger_bars); if(copied<InpSwingDepth+2) { PrintFormat("C setup unavailable: copied=%d, error=%d%s", copied,GetLastError(),(copied<0 && MQLInfoInteger(MQL_TESTER) ? " (check tester mode and trigger-history availability)" : "")); return(false); } //--- CopyRates stores the oldest record first in physical memory ArraySetAsSeries(trigger_bars,false); //--- Compression and directional setups use different scanners if(g_sequence.relation==AB_INSIDE_COMPRESSION) return(AnalyzeCompressionC(trigger_bars)); SetDirectionalLevels(); return(AnalyzeDirectionalC(trigger_bars)); }
If too few records are returned, the EA leaves C unavailable and does not index the short array. A partial return above the minimum can still hide missing bars. The terminal may build or download history during a request, so reinitialize after synchronization when necessary. For periods that do not align, an included bar may open inside C and close after D opens. Use an aligned pair such as D1/M1 until explicit containment and coverage checks are added.
AnalyzeDirectionalC implements the one-sided retest, checked-close protection, and first-setup policy described above. Notice that touched can be set immediately before the structure comparison on the same bar.
//+------------------------------------------------------------------+ //| Scans C for a retest and a trigger-bar structure break | //+------------------------------------------------------------------+ bool AnalyzeDirectionalC(const MqlRates &trigger_bars[]) { double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); double tolerance=InpRetestTolerancePoints*point; double buffer=InpBreakBufferPoints*point; //--- Walk C chronologically looking for a retest then a structure break bool touched=false; for(int i=0;i<ArraySize(trigger_bars);i++) { //--- Cancel a setup that violates the A-B protected extreme if(g_sequence.direction>0 && trigger_bars[i].close<g_sequence.protected_level-buffer) return(false); if(g_sequence.direction<0 && trigger_bars[i].close>g_sequence.protected_level+buffer) return(false); //--- The retest must touch the reference and close on its valid side if(!touched && g_sequence.direction>0 && trigger_bars[i].low<=g_sequence.reference_level+tolerance && trigger_bars[i].close>=g_sequence.reference_level-buffer) touched=true; if(!touched && g_sequence.direction<0 && trigger_bars[i].high>=g_sequence.reference_level-tolerance && trigger_bars[i].close<=g_sequence.reference_level+buffer) touched=true; if(!touched || i<InpSwingDepth) continue; //--- A displacement close through recent trigger-timeframe structure arms Candle D if(g_sequence.direction>0 && IsDisplacement(trigger_bars[i],1) && trigger_bars[i].close>PreviousHigh(trigger_bars,i)+buffer) { g_sequence.c_setup_time=trigger_bars[i].time; g_sequence.c_setup_level=trigger_bars[i].close; return(true); } if(g_sequence.direction<0 && IsDisplacement(trigger_bars[i],-1) && trigger_bars[i].close<PreviousLow(trigger_bars,i)-buffer) { g_sequence.c_setup_time=trigger_bars[i].time; g_sequence.c_setup_level=trigger_bars[i].close; return(true); } } return(false); }
Confirmation and Invalidation Inside Candle D
C arms the sequence without submitting an order. D confirmation marks the point where our entry conditions are satisfied. We assess the predictive value of those conditions separately.
A bullish confirmation requires a bullish displacement close strictly above C high + buffer; the bearish rule requires a bearish displacement close strictly below C low − buffer. While the state is tracking, an invalidating close is checked first. A wick or tick through protection is insufficient, and equality at the buffered protection boundary does not invalidate. After a signal, the state only updates excursion measurements; it does not apply this invalidation rule as position management.
| D event | Bullish sequence | Bearish sequence | Result |
|---|---|---|---|
| Confirmation | Bullish displacement close above the C high and buffer | Bearish displacement close below the C low and buffer | One signal; state becomes triggered |
| Invalidation | Trigger close < protected level − buffer | Trigger close > protected level + buffer | No later D signal is permitted |
| No decision | Neither boundary is confirmed | Neither boundary is confirmed | Our model keeps waiting until D closes |
| Expiry | D ends without a trigger | D ends without a trigger | The next context opening starts a new sequence; the old tracking state is logged without an explicit expiry transition |
If D opens beyond C’s high or low, the opening gap alone does not trigger a signal. The EA waits for an examined completed trigger bar that satisfies direction, body ratio, and buffered close requirements; a fresh crossing from the other side is not required. On startup, ReplayDHistory uses the same detector with allow_trade=false, so reconstructed historical signals cannot submit orders.
UpdateExcursion measures each processed trigger bar relative to the signal bar’s close. For a bullish signal, favorable movement is High − signal close and adverse movement is signal close − Low; a bearish signal reverses the directions. The function clamps negative values to zero, retains the maxima, and reports them in symbol points. We use these OHLC values as diagnostics of the processed price path.
Measurement begins with the confirmation bar’s full range, including prices reached before its closing signal. Subsequent processed bars extend the maxima. The live gate processes the latest completed bar, so a tick gap can leave intervening bars unexamined. At a context transition, the new sequence is initialized before the old window’s final closed bar is evaluated. The diagnostic horizon therefore consists of the bars actually processed, including that initial confirmation-bar range.
System Architecture
The context loader assigns four bars from InpContextTimeframe. The classifier selects an A-B relationship, the C scanner applies trigger-timeframe rules, and the D state machine examines completed trigger bars. Visualization and optional execution consume the decision. D1/M1 in the diagrams denotes the default worked configuration.

Fig. 2. Components, data flow, and execution safety boundary
The safety boundary is deliberate. Detection remains useful when trading permissions are unavailable or InpEnableTrading is false. Optional execution performs independent permission, position, spread, volume, stop distance, and trade-result checks. A confirmed analytical signal therefore does not imply that a broker accepted a position.
Implementation
State and Candle Data
SSequence stores the four context records with their classification, direction, levels, event timestamps, and excursion diagnostics. Keeping them together gives each function the same A-B-C-D assignment.
struct SSequence { MqlRates a; MqlRates b; MqlRates c; MqlRates d; ENUM_AB_RELATION relation; ENUM_SEQUENCE_STATE state; int direction; double reference_level; double protected_level; datetime c_setup_time; double c_setup_level; datetime d_signal_time; double signal_price; double mfe; double mae; };
A direction of 1 selects bullish comparisons, −1 selects bearish comparisons, and zero leaves direction unresolved or inactive. The reference is the retest threshold. The protected level is the threshold for a cancellation based on examined closes, subject to the scanner’s stopping policy.
Gating Completed Trigger Bars
The OnTick handler copies the two most recent records of InpTriggerTimeframe. It examines record 1 when its opening time differs from g_last_trigger_time. This suppresses repeated processing of the latest completed bar; it does not guarantee processing every bar when several have elapsed between ticks.
//+------------------------------------------------------------------+ //| Processes each newly completed trigger-timeframe candle | //+------------------------------------------------------------------+ void OnTick(void) { MqlRates trigger_bars[]; ArraySetAsSeries(trigger_bars,true); if(CopyRates(_Symbol,InpTriggerTimeframe,0,2,trigger_bars)!=2) return; //--- Resolve the newest completed trigger candle datetime closed_time=trigger_bars[1].time; //--- Skip repeats of the same completed candle if(closed_time==g_last_trigger_time) return; //--- Roll the sequence forward when a new D candle has opened if(!RefreshContextSequence(false)) return; g_last_trigger_time=closed_time; //--- Trigger bars before D opened are outside the sequence if(closed_time<g_sequence.d.time) return; //--- Refresh the D box, evaluate the bar, and repaint the text UpdateCandleD(); EvaluateDBar(trigger_bars[1],true); UpdateStatus(); }
RefreshContextSequence runs before EvaluateDBar. A timestamp guard then excludes bars opening before the new D. This keeps the old window’s last bar out of the new sequence, but the live path does not separately process that bar for the old sequence.
History Reconstruction
On initialization, the EA loads the current context and replays the available completed trigger bars inside D. Replay calls EvaluateDBar with allow_trade=false. State and chart signals can be reconstructed without a historical order request; incomplete history can still change the reconstructed state.
The PeriodSeconds function supplies nominal period lengths for validation and the D box’s visual extent. Context opening timestamps bound the C history request. Neither mechanism proves session coverage or containment of trigger bars in non-aligned periods. Bar timestamps identify opening time; the confirmation is available only after that bar closes.
Visualization
The EA uses ObjectCreate for candle boxes, decision lines, labels, and a signal arrow. Names contain the CTC_P11 prefix, magic number, and D opening time where applicable. This separates sequences and distinct magic numbers on a chart; it is not a globally unique instance identifier. Reusing the same namespace on one chart can reuse or delete existing objects. Use a distinct magic number for each independent deployment.
The CCanvas class displays six chart-comment lines: title, A-B relationship, direction, state, C setup time, and trading setting. The canvas is observational. Candle boxes, levels, and arrows are separate chart objects.
CreateStatusCanvas() initializes the bitmap and font. UpdateStatus() clears the bitmap, draws the text lines using the chart foreground color, and then calls Update(). DestroyStatusCanvas() releases the bitmap and the bound label during deinitialization.
//+------------------------------------------------------------------+ //| Creates the canvas used only for chart-comment text | //+------------------------------------------------------------------+ bool CreateStatusCanvas(void) { if(!InpShowStatusPanel) return(true); //--- Create the transparent bitmap label that hosts the text string name=g_prefix+"STATUS_CANVAS"; ResetLastError(); if(!g_status_canvas.CreateBitmapLabel(0,0,name,10,20,360,116, COLOR_FORMAT_ARGB_NORMALIZE)) { PrintFormat("CreateBitmapLabel() failed. Error=%d",GetLastError()); return(false); } //--- A fixed font keeps every status line aligned if(!g_status_canvas.FontSet("Arial",-100)) { PrintFormat("CCanvas FontSet() failed. Error=%d",GetLastError()); //--- Clean up the partially created canvas on failure g_status_canvas.Destroy(); return(false); } ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,name,OBJPROP_HIDDEN,true); g_status_canvas_ready=true; return(true); } //+------------------------------------------------------------------+ //| Draws the original chart comments through CCanvas | //+------------------------------------------------------------------+ void UpdateStatus(void) { if(!g_status_canvas_ready) return; //--- Compose the compact state lines from the runtime sequence string direction="Neutral"; if(g_sequence.direction>0) direction="Bullish"; else if(g_sequence.direction<0) direction="Bearish"; string c_setup=(g_sequence.c_setup_time>0 ? TimeToString(g_sequence.c_setup_time, TIME_DATE|TIME_MINUTES) : "none"); string lines[6]; lines[0]="Candlestick Trend Constraint Part 11"; lines[1]="A-B: "+RelationName(g_sequence.relation); lines[2]="Direction: "+direction; lines[3]="State: "+StateName(g_sequence.state); lines[4]="C setup: "+c_setup; lines[5]="Trading: "+(InpEnableTrading ? "enabled" : "disabled"); //--- Paint in the chart foreground color for any theme color chart_text=(color)ChartGetInteger(0,CHART_COLOR_FOREGROUND); uint text_color=ColorToARGB(chart_text,255); //--- Clear the previous text while keeping the canvas transparent g_status_canvas.Erase(0); for(int i=0;i<ArraySize(lines);i++) g_status_canvas.TextOut(4,4+i*18,lines[i],text_color); g_status_canvas.Update(); } //+------------------------------------------------------------------+ //| Releases the canvas and its bound bitmap-label object | //+------------------------------------------------------------------+ void DestroyStatusCanvas(void) { if(!g_status_canvas_ready) return; g_status_canvas.Destroy(); g_status_canvas_ready=false; }
If InpDeleteObjectsOnRemoval is false, the completed sequence objects remain on the chart after our EA is removed. Enabling that input deletes only objects owned by our EA's prefix. The canvas itself is always destroyed during deinitialization because its bitmap resource belongs to the running EA instance.
Figure 3 illustrates a bullish sequence in synthetic price units: the price buffer is 0.2, retest tolerance is 0.3, swing depth is three trigger bars, and the minimum body ratio is 0.55. C retests A high at 105 and records its first structure break. D’s confirming close at 112 is above C high 111 plus the buffer. These illustrative values are not market measurements or the default symbol-point settings.

Fig. 3. Illustrative bullish A-B context, C retest and setup, and D confirmation; synthetic price units
Optional Execution
Our EA includes the CTrade class, but InpEnableTrading defaults to false. When enabled, a D confirmation passes through the following controls:
- MQL program, terminal, and account trading permissions must be available.
- A netting account must have no existing position on the symbol.
- A hedging account must have no position owned by this symbol and magic number.
- The current spread must not exceed InpMaxSpreadPoints.
- The requested volume is clamped and stepped to the symbol's permitted volume properties.
- The stop is placed beyond the protected level and adjusted to respect the symbol's minimum stop distance.
- The take-profit distance is derived from the final entry-to-stop distance and InpRiskReward.
- The Boolean trade call and the server result code are both checked.
TradeResultAccepted accepts TRADE_RETCODE_DONE, TRADE_RETCODE_DONE_PARTIAL, and TRADE_RETCODE_PLACED. DONE reports completion; DONE_PARTIAL reports a partial result; PLACED reports placement rather than a proven fill. As the CTrade Buy documentation explains, a successful method return alone does not establish trade execution. Check the return code and deal/order evidence. This EA logs those identifiers but does not implement transaction-level reconciliation or retry a failed request for the same sequence. The signal remains triggered even when trading is disabled or the request is rejected.
Operating the Expert Advisor
Place Candlestick_Trend_Constraint_P11.mq5 in MQL5/Experts and compile it in MetaEditor. Attach it to the intended symbol. Select explicit context and trigger periods; with those inputs fixed, the host chart timeframe does not select the strategy’s periods.
| Input | Default | Purpose |
|---|---|---|
| InpContextTimeframe | PERIOD_D1 | Timeframe of Candles A-D |
| InpTriggerTimeframe | PERIOD_M1 | Internal timeframe used for C and D |
| InpBreakBufferPoints | 10 | Separates a qualified break from marginal equality |
| InpReversalCloseFraction | 0.50 | Required recovery through the A range after a sweep |
| InpSwingDepth | 3 | Number of preceding trigger bars defining local structure |
| InpRetestTolerancePoints | 30 | Permitted distance around a reference retest |
| InpMinBodyRangeRatio | 0.55 | Minimum body share of a trigger displacement candle |
| InpDrawSequence | true | Displays boxes, levels, labels, and signals |
| InpDeleteObjectsOnRemoval | false | Controls whether owned chart objects persist |
| InpShowStatusPanel | true | Displays the original chart-comment lines through CCanvas |
| InpEnableTrading | false | Keeps analysis separate from live execution |
| InpVolume | 0.10 | Requested fixed volume when execution is enabled |
| InpRiskReward | 2.00 | Take-profit multiple of the final stop distance |
| InpStopBufferPoints | 100 | Places the stop beyond the protected level |
| InpMaxSpreadPoints | 50 | Blocks execution during excessive spread |
| InpDeviationPoints | 20 | Maximum permitted trade deviation |
| InpMagicNumber | 24516011 | Identifies positions and chart objects owned by Part 11 |
Begin with trading disabled. Inspect several completed sequences in the Strategy Tester visualization and verify that the reference, protected level, C setup, and D signal match the Experts log. Point-based inputs are symbol-specific. A value that is suitable for a five-digit currency symbol may be too small or too large for an index, metal, or synthetic symbol.
Planning Compilation and Runtime Tests
The following scenarios specify our test plan for the EA’s rules and event handling. Compilation results and recorded scenario outcomes remain to be collected. We use the visual walkthrough to explain the workflow; the planned runs will supply the evidence for evaluating its behavior.
Compile Candlestick_Trend_Constraint_P11.mq5 in MetaEditor and record the build plus error and warning counts. Synchronize the configured trigger history before the visual run. Initialization and compilation are separate checks.
For the default worked case, select D1/M1 and begin with InpEnableTrading=false. Use "Every tick based on real ticks" when the required history is available, and record the modeling mode actually used. For this walkthrough, avoid "Open prices only". The official tester reference restricts that mode’s access to periods below, or non-multiple periods relative to, the testing period. Intrabar ordering and fill analysis require appropriate tick data and execution records.
| Scenario | Expected observation | Evidence required |
|---|---|---|
| Bullish continuation | A-B classification, rule-qualified C retest and structure break, then one D close above the C high | Chart objects and Experts log |
| Bearish continuation | Mirrored classification and one D close below the C low | Chart objects and Experts log |
| Bullish and bearish reversal | B sweep and recovery satisfy the configured fraction before C is analyzed | Context-timeframe OHLC and relation log |
| Inside compression | C chooses a direction, retests the broken A boundary, and breaks trigger-bar structure | Trigger-bar visual replay |
| C without an accepted setup | The sequence expires and D never triggers | CCanvas chart comment and log |
| Protected-level breach | Invalidation takes priority and later D breaks are ignored | Trigger-bar close and state log |
| Mid-D attachment | Earlier D trigger bars are replayed, but no historical trade is submitted | Journal and account history |
| Insufficient history | Our EA reports the unavailable C setup without accessing invalid indices | Experts log |
| Trading enabled | At most one request per sequence after all execution gates pass | Trade request/result log and account mode |
| Additional planned check | Expected rule or limitation |
|---|---|
| Buffered equality and doji | Touching a strict breakout, sweep, or compression boundary fails; recovery equality and body-ratio equality pass. A doji has no body direction. |
| Zero-range input | A with High=Low is unclassified; a zero-range trigger bar cannot satisfy displacement. |
| C competition and cancellation | Confirm first-break direction for compression, same-bar retest/break for directional C, first qualifying setup, and no scan after that setup. |
| D gap and protection | An opening gap alone does not confirm. A wick beyond protection does not invalidate; a strict buffered close does while tracking. |
| History and event coverage | Test missing trigger records, several bars between ticks, and a non-aligned timeframe pair. Record omissions rather than treating a successful copy as complete coverage. |
| End of context window | Inspect the final old-D trigger bar and its omission from the old sequence before context replacement. |
| Ownership and trade results | Check retained objects with a reused magic number, cleanup scope, and DONE/PARTIAL/PLACED outcomes independently of signal counts. |

Fig. 4. Strategy Tester visual demonstration
Keep the terminal build, symbol, explicit periods, demo account mode, date range, modeling mode, and input set with the recorded run. Check signals independently of order requests and deals. To study predictive value, compare the detector with an eligible baseline and use chronological holdout data, realistic costs, and several instruments or regimes.
Limitations
- The A-B taxonomy and default thresholds are research choices. Reproducible comparisons do not establish their stability or market advantage.
- Protection uses examined closes before a trigger. The C scanner stops at its first setup, and triggered D states do not model later analytical invalidation or trade exits.
- The trigger period must be shorter than the context period, but initialization does not enforce alignment or complete history coverage.
- The live timestamp gate processes only the latest completed trigger bar. Tick gaps and the old/new context transition can omit bars.
- Excursion diagnostics include the confirmation bar’s full range and omit trading costs. They are not verified post-confirmation outcomes or realized returns.
- Context candles depend on server and session boundaries. A D1/M1 example cannot validate other period pairs or data feeds.
- Object names are scoped by a chart namespace and magic number; reusing that namespace can collide with persistent objects.
- InpDeleteObjectsOnRemoval controls prefix-based cleanup. CCanvas is always destroyed, but retained objects require deliberate namespace management.
- A performance conclusion requires completed, reproducible tests, adequate independent samples, cost assumptions, and out-of-sample evaluation.
Conclusion
In this article, we have implemented a four-candle state model that gives context, setup, and confirmation separate responsibilities. The EA classifies completed A-B candles, records the first qualifying C setup, and evaluates completed trigger bars inside D. Its chart objects and CCanvas comments make those decisions visible, while the replay and execution gates keep reconstructed signals separate from new trade requests.
You can now follow each decision from A-B classification to a D signal and distinguish that signal from an order result. We have established a concrete framework for testing our candlestick hypothesis. Compilation and recorded scenario tests are the next step before assessing its market value.
Key Lessons
| Lesson | Description |
|---|---|
| Use completed context bars to select explicit directional rules | A-B classification selects the detector’s directional comparisons. Lower-timeframe bars supply retests and structure breaks; predictive value requires separate evidence. |
| Separate context, setup, and confirmation | Giving each stage one responsibility prevents a single condition from mixing market bias, trigger detection, and confirmation. This separation also makes each rule easier to test and reuse. |
| Represent sequential logic as explicit state | A finite-state model makes allowed transitions visible and prevents an event from bypassing the required order. The principle applies to trading logic, workflow automation, protocol handling, and other event-driven programs. |
| Process completed bars once | A completed-bar timestamp gate avoids duplicate decisions, reduces tick-level noise, and makes live behavior easier to reproduce in the Strategy Tester. |
| Bound lower-timeframe history with real timestamps | Opening timestamps define context windows without assuming a fixed bar count. Separate checks are still needed for containment, session gaps, and missing history. |
| Replay state without replaying execution | The same detector can reconstruct analytical state after initialization, while a separate execution permission prevents historical signals from creating late trades. |
| Keep visualization observational | Chart objects and CCanvas report our model's state without changing classification or execution. Interface code can therefore evolve without altering the trading rules. |
| Treat a signal and an accepted order as different outcomes | A confirmed pattern is an analytical result. Permissions, ownership, spread, volume, stop distance, and server return codes independently decide whether a request proceeds. Placement and fill remain distinct outcomes. |
| Own and release chart resources explicitly | A documented prefix and magic number define cleanup scope. Their uniqueness must be managed explicitly; a shared namespace does not isolate deployments. |
Attachments
| File Name | Type | Description |
|---|---|---|
| Candlestick_Trend_Constraint_P11.mq5 | Expert Advisor | Configurable context-timeframe A-B classifier, trigger-timeframe Candle C setup scanner, Candle D state machine, visualization, replay, and optional guarded execution. |
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Features of Custom Indicators Creation
Bimodal Market Profile with Delta and Memory in MQL5
Features of Experts Advisors
Building a Prop Firm Compliance Monitor in MQL5 (Part 3): Daily Drawdown, Overall Drawdown, and Profit Targets
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use