14 KiB
Algo Forge — DESIGN
Source of inspiration: SniperGold SMC Pro+ (c) Waseem Shahrukh — https://www.mql5.com/en/code/75466
Per-engine technical specification document. Project structure & status: see PROGRESS.md and README.md.
Engine 1 — MTF Bar Data Collector + Cache
File: MQL5\Include\AlgoForge\AF_Engine1_MTFData.mqh — class AFEngine1MTF, struct AFBar.
1. Responsibilities
- Collect OHLCV bars from several explicit timeframes, as required by the N/C/E/P agents (Engine 2) and the display (Engine 3).
- Provide an index-based internal cache so data can be reused repeatedly without
repeated History reads (anti-freeze). This pattern is proven in SniperGold v4.5
(
RefreshMTFCache/BuildMTFStruct). - Chart-TF independent: all access uses an explicit symbol + explicit
ENUM_TIMEFRAMES.PERIOD_CURRENTis rejected atRegister(). - Closed-bar lock: the cache contains ONLY closed bars (non-repainting).
2. API
Lifecycle
AFEngine1MTF e1;
int s1 = e1.Register(PERIOD_M15, 600); // slot index or AF_E1_ERR_SLOT
bool changed = e1.Refresh(); // call each OnCalculate/OnTick
Read (idxFromRight: 0 = NEWEST closed bar)
bool e1.IsReady(slot);
int e1.Count(slot); // number of closed bars in the cache
bool e1.GetBar(slot, idx, AFBar &out);
bool e1.GetBarByTime(slot, datetime, AFBar &out);
int e1.FindBarIndex(slot, datetime);
double e1.Open/High/Low/Close(slot, idx);
datetime e1.Time(slot, idx);
long e1.TickVolume(slot, idx);
double e1.ATR(slot, period=14);
Diagnostics
int e1.HistoryCalls(slot); // number of CopyRates executed (unit test anti-freeze)
int e1.RefreshCount(slot);
int e1.TotalHistoryCalls();
long e1.LastBars(slot);
3. Cache Mechanism
Refresh()checksBars(symbol, tf)for each slot.- Only if
Bars()changed (or the cache is not ready + throttleAF_E1_RETRY_SEC) →Build(slot)is called →CopyRatesexecutes. Build:CopyRates(symbol, tf, 0, maxBars+1, rates)with as-series array; the forming bar (not yet closed) is dropped based onIsBarClosed():barTime + PeriodSeconds(tf) <= TimeCurrent().- The cache is stored in series:
bars[0]= newest closed bar. CapacitymaxBars.
Invariants (verified by unit tests)
Count(slot) <= Bars(symbol, tf).- All cache bars are closed (T4).
HistoryCallsincreases by exactly 1 per new bar per TF (T5 anti-freeze).- Strictly decreasing time order from index 0 (T3); valid OHLC (T2).
4. Limits & Result Codes
AF_E1_MAX_SLOTS = 8,AF_E1_MAX_BARS = 5000,AF_E1_RETRY_SEC = 5.AF_E1_OK / AF_E1_ERR_SLOT / AF_E1_ERR_NOTREADY / AF_E1_ERR_RANGE.
5. MQL5 Implementation Notes
- No array-element references in MQL5 (
T &x = arr[i]= error). All access uses direct index (m_slots[i].field). - Slots use a dynamic
m_slots[]array (a struct containing dynamic arrays is safe). #property versionmust beX.YYformat (e.g. "1.00") to avoid warnings.
6. Unit Test
- Harness:
MQL5\Experts\AlgoForge_Engine1_UnitTest.mq5(EA, log prefixAFTEST). - Run in the Strategy Tester (XAUUSD, model every tick; the "1-min OHLC" model rejects sub-chart TF requests).
- Result 2026-08-21: PASS=115070 FAIL=0 (20 days) · PASS=8870 FAIL=0 (verification).
Engine 2 — 4 Independent Signal Agents (N/C/E/P) + Fuzzy + Aggregator
Files:
MQL5\Include\AlgoForge\AF_Engine2_Agents.mqh— 4 agents + fuzzy logic (classesAFAgentNarrative,AFAgentContext,AFAgentEntry,AFAgentPriceAction).MQL5\Include\AlgoForge\AF_Engine2_Aggregator.mqh— separate aggregator (classAFAggregator) + facadeAFEngine2Signals.
7. Key Principles
- 4 INDEPENDENT agents — each agent reads Engine 1 only through one slot (timeframe) of its own. No inter-agent calls/state; all methods are stateless (pure functions of Engine-1 closed bars).
- No mutual knowledge — the final composition is done by the separate aggregator, not between agents.
- Dynamic-weight fuzzy logic — each agent uses membership functions
(
AF_MF_Tri/AF_MF_Trap) + a light Mamdani evaluator (AFFuzzyEval:buyAcc/sellAcc/wTot, rulesRule(buySide, fire, weight)); weights adapt to market conditions measured from the agent's own data (trend strength, volatility, ranging). - Non-repainting — all inputs are Engine-1 closed bars (closed-bar lock guaranteed by Engine 1; Engine 2 never reads History directly).
8. Input Timeframe
Agent TFs are HARDCODED (no manual InpHtfS1..S4 inputs — removed) — see
macros AF_E2_TF_S1..S4 in AF_Defines.mqh:
AF_E2_TF_S1 = H4 (S1 = Narrative / N)
AF_E2_TF_S2 = M30 (S2 = Context / C)
AF_E2_TF_S3 = M15 (S3 = Entry / E)
AF_E2_TF_S4 = M3 (S4 = PriceAction/P)
Consumers (indicator AF_Engine3_Display, Engine-2/3 unit tests) use these macros
directly at Register(). Analysis basis: H4→M30→M15→M3 (top-down to the chart).
9. Agents
| Agent | Question | Assessed elements (from its own slot) | Dynamic weights |
|---|---|---|---|
| N (Narrative) | "Which way is the market?" | HH/HL/LL/LH pivots → trend (+clarity), CHoCH/MSS, BOS, liquidity sweep, premium/discount | Clear trend → structure dominates; flat → zones/liquidity up |
| C (Context) | "Which zone is price in?" | OB (opposite bar before a strong move), FVG/imbalance, S/R (pivots), premium/discount | High volatility → S/R & premium/discount down, OB/FVG up |
| E (Entry) | "Is there entry confirmation?" | Sweep, CHoCH, displacement, OB/FVG zones; rule ZONE + CONFIRMATION = setup | Strong displacement → confirmation weight up |
| P (Price Action) | "When to open?" | Engulfing, pin bar, inside bar, 2-bar momentum, close position in range | Ranging → reversal patterns up; trending → continuation up |
Per-agent output: AFSignalOut { buy, sell, bias, confidence, dir, reason }.
10. Aggregator (separate)
AFAggregator::Compute(e1, slotE, n, c, e, p, out):
- Pass 1: initial bias = Σ (base weight × confidence × bias) / Σ (base weight × confidence).
Base weights: N=0.30, C=0.30, E=0.25, P=0.15 (
AF_AGG_W_*). - Pass 2 (dynamic weights): agents aligned with the majority get a 1.5× boost;
aggregate
buy/sell= Σ (effective weight × buy/sell) / Σ weights. - Final signal:
dir = BUY/SELL/WAITwith thresholdAF_AGG_BUY_TH=0.20and minimum supportAF_AGG_MIN_SUP=0.50. - Levels:
entry= close of the closed slot-E bar;sl/tpbased on Engine-1 ATR (AF_AGG_SL_ATR=1.5,AF_AGG_TP_ATR=2.5) — only on BUY/SELL signals. confidenceaggregate = support × (0.7 + 0.1 × number of aligned agents).
Facade AFEngine2Signals::Compute(e1, sN, sC, sE, sP, oN, oC, oE, oP, agg):
one call runs the 4 agents + aggregator (used by Engine 3 / consumers).
11. Unit Test
- Harness:
MQL5\Experts\AlgoForge_Engine2_UnitTest.mq5(log prefixAFTEST2). - Strategy Tester (XAUUSD, every tick):
- T1 output validity per agent · T2 independence (changing agent X's input slot → other agents unchanged) + determinism · T3 closed-bar lock · T4 non-repaint (identical output within the same bar) · T5 aggregator validity · T6 synthetic aggregator.
- Results 2026-08-21: PASS=21534 FAIL=0 (20 days) · PASS=9942 FAIL=0 (10-day verbose).
- Note: agents use tamper slots (different TFs) for the independence test; T2 auto-retries when a tamper slot is not ready.
12. Implementation Constraints (additional, Phase-2 sessions)
replace_text_in_filemulti-line edits often fail → use single-line edits.- The independence test needs a tamper slot with ≥
AF_E2_MIN_BARS(80) bars; choose a dense TF (M6/M12/M20/M30) so it is ready even on short runs.
Engine 3 — Display (reads Engine 1 & 2 output only)
Files:
MQL5\Include\AlgoForge\AF_Engine2_Display.mqh— display-context builder (Engine-2 layer): structAFDisplayData,AFDispZone,AFDispLine,AFDispPivot+AF_BuildDisplayData(). All display computation (structure, swing points, OB, FVG, premium/discount, MTF levels, agent bias) happens HERE, using the same pure analysis helpers as the N/C/E/P agents (AF_BuildSwing,AF_DetectSweep,AF_RangeStat, etc.).MQL5\Include\AlgoForge\AF_Engine3_Render.mqh— pure renderer (Engine-3 layer): structAFRenderCfg+AFR_DrawAll()/AFR_Clear(). NO analysis computation; draws only fromAFDisplayData+AFSignalOut+AFAggOut(structure, zones, signals, dashboard like the original 75466 code).MQL5\Indicators\AlgoForge\AF_Engine3_Display.mq5— indicator (chart window): inputs + OnInit/OnCalculate/OnDeinit; 0 buffers (object-only).MQL5\Experts\AlgoForge_Engine3_UnitTest.mq5— unit test (Strategy Tester).
13. Engine-3 Principles
- READS ONLY Engine-1 (
AFEngine1MTF) & Engine-2 output (AFEngine2Signals/AFAggOut/AFDisplayData). Engine 3 computes no structure/zones itself. - Signal–display consistency:
AF_BuildDisplayData(Engine 2) uses the same helpers as the agents. - Non-repainting: the indicator draws only when a new closed bar appears on the
display TF (
e1.Time(sDisp,0)changes); no redraw within the same bar. - Anti-freeze: Engine 1 remains the only History reader (index cache).
14. Display elements (equivalent to original code 75466)
| Element | Source | Description |
|---|---|---|
| BOS/CHoCH structure lines | AFDispLine (AF_BuildStructLines) |
fractal pivots, BOS/CHoCH labels |
| HH/HL/LH/LL swing points | AFDispPivot (AF_ClassifyPivots) |
bull/bear colors |
| Order Block | AFDispZone (AF_CollectOBs) |
MIT filter + dedupe + size ≥ 0.15×ATR |
| FVG | AFDispZone (AF_CollectFVG) |
MIT filter + size ≥ 0.02×ATR |
| Premium/Discount | box from swHigh/swLow + position |
premium/equilibrium/discount bands |
| MTF PDH/PDL levels | Engine-1 D1 slot (index 1) | solid lines |
| MTF PWH/PWL levels | Engine-1 W1 slot (index 1) | dashed lines |
| Entry/SL/TP signals + arrows | AFAggOut |
only when dir BUY/SELL |
| Dashboard | AFDisplayData + AFAggOut |
bias, structure, liquidity, context, levels, N/C/E/P alignment, trade setup, legend |
15. Indicator inputs
InpHtfS1..S4= agent N/C/E/P TFs (Engine 2) — default H1/H1/M15/M15.InpDispTF= structure/zone display TF — defaultPERIOD_CURRENT(= chart).- Toggles: structure, swing points, OB (+count), FVG (+count), premium/discount, MTF levels, signals, dashboard.
- LuxAlgo-style colors (same defaults as original code 75466) + transparent panel.
16. Unit Test
- Harness:
MQL5\Experts\AlgoForge_Engine3_UnitTest.mq5(log prefixAFTEST3). - Strategy Tester (XAUUSD, every tick, Visual=1 — chart objects are only created in visual mode).
- T1 display-data validity (structure/eqPos/levels/bias) · T2 non-repaint (identical
data within the same bar) · T3 closed-bar lock · T4 render creates chart objects
(prefix
AF3_) · T5 render determinism (same object count). - Results 2026-08-21: see
PROGRESS.md.
17. Implementation Constraints (Phase 3)
ObjectCreatein the Strategy Tester only works in visual mode; the test EA probes once at startup and T4/T5 are auto-skipped (not failed) when objects are unsupported.- Visual mode slows the tester (20 days every tick ≈ 10 minutes) → for quick verification run a short range (e.g. 3–5 days).
Backtest Baseline (Phase 5) — verification & publication
File: MQL5\Experts\AlgoForge_Backtest_Baseline.mq5
- config
MQL5\Profiles\Tester\AlgoForge_Backtest_Baseline.XAUUSD.M15.*.ini - evaluation
ml\backtest_eval.py.
18. Objective & Principles
- Honest backtest of the baseline strategy (MLP freeze
SniperGold_ML.mqh, AUC long 0.627 / short 0.621) net of spread. - NOT a fragile iCustom: 19 SMC features computed internally in the EA
(identical to
SniperGold_SMC_ProPlus_v4_4.mq5ComputeMLFeatures+ its dependencies), data source ONLY Engine 1 (AFEngine1MTF) — closed-bar lock, anti-freeze, consistent with the Algo Forge architecture. - Feature parameters hardcoded (
AF_BT_*= v4.4 training defaults): SwingLen=50, InternalLen=5, Lookback=600, GrabWindow=8, EQ thr=0.10/3 bars, DeltaBars=10, HTF=D1/H4/H1, ConfluenceFilter=true.
19. Modes
| Mode | Function | Output |
|---|---|---|
| 0 | CSV long/short prob per closed M15 bar | AlgoForge_bt_prob_*.csv → backtest_eval.py (AUC/precision/calibration) |
| 1 | Net-of-spread trading in the Strategy Tester | OrderSend market (ATR SL/TP, max hold); actual tester spread; OnTester summary |
Inputs: InpMode, InpThreshLong/Short (default 0.60/0.60), InpSL_ATR=1.0,
InpTP_ATR=1.5, InpMaxHoldBars=24 (= baseline label horizon),
InpLot=0.01, InpMaxBars=700.
20. Results (2026-08-21, XAUUSD M15, every tick) — HONEST
Mode 0 (CSV/AUC), 2026.01.01–08.20, 14.850 bars:
| Metric | Value | Baseline freeze |
|---|---|---|
| AUC LONG | 0.5305 | 0.6270 |
| AUC SHORT | 0.5487 | 0.6207 |
| Precision LONG @0.60 | 0.5352 (n=4454) | — |
| Precision SHORT @0.60 | 0.5292 (n=7010) | — |
- Poor calibration (prob 0.65+ → long frequency 0.545) → on the XAUUSD feed (not the XAUUSDc training feed) the baseline model is NOT calibrated. Do not claim an edge on another feed without a like-for-like gate.
Mode 1 (trade), 2026.05.01–08.20: trades=308, net=+1125.32, maxDD=1188.18, PF=1.32. Note: some signals failed to execute (requote 10018, no retry); the positive result is not significant (runtime AUC 0.53/0.55, small sample) — not an edge claim.
21. Constraints (Phase 5)
- Tester CSV is written to the agent sandbox (
Tester\Agent-*\MQL5\Files\) and reset per run → read/copy results immediately after the run (Python evaluation directly from the sandbox path). - MQL5
FILE_CSVdelimiter in this terminal = TAB (not;/,) →backtest_eval.pyusesdelimiter="\t". PositionSelectoverload ambiguity on build 6093 (string vs ulong) → helperBTSelectTicket()(loopPositionsTotal/PositionGetTicket).- OrderSend requotes (ret=10018) appear in the tester → recorded, no retry (honest).