2026-08-21 10:01:21 +07:00
# Algo Forge — DESIGN
2026-08-21 09:40:40 +07:00
2026-08-22 17:21:46 +07:00
> Source of inspiration: SniperGold SMC Pro+ (c) Waseem Shahrukh — https://www.mql5.com/en/code/75466
2026-08-21 10:01:21 +07:00
2026-08-22 17:21:46 +07:00
Per-engine technical specification document. Project structure & status: see `PROGRESS.md` and `README.md` .
2026-08-21 10:01:21 +07:00
---
2026-08-22 17:21:46 +07:00
# Engine 1 — MTF Bar Data Collector + Cache
2026-08-21 10:01:21 +07:00
File: `MQL5\Include\AlgoForge\AF_Engine1_MTFData.mqh` — class `AFEngine1MTF` , struct `AFBar` .
2026-08-21 09:40:40 +07:00
2026-08-22 17:21:46 +07:00
## 1. Responsibilities
2026-08-21 09:40:40 +07:00
2026-08-22 17:21:46 +07:00
- 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_CURRENT` is rejected at `Register()` .
- **Closed-bar lock**: the cache contains ONLY closed bars (non-repainting).
2026-08-21 09:40:40 +07:00
## 2. API
### Lifecycle
```cpp
AFEngine1MTF e1;
2026-08-22 17:21:46 +07:00
int s1 = e1.Register(PERIOD_M15, 600); // slot index or AF_E1_ERR_SLOT
bool changed = e1.Refresh(); // call each OnCalculate/OnTick
2026-08-21 09:40:40 +07:00
```
2026-08-22 17:21:46 +07:00
### Read (idxFromRight: 0 = NEWEST closed bar)
2026-08-21 09:40:40 +07:00
```cpp
bool e1.IsReady(slot);
2026-08-22 17:21:46 +07:00
int e1.Count(slot); // number of closed bars in the cache
2026-08-21 09:40:40 +07:00
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);
```
2026-08-22 17:21:46 +07:00
### Diagnostics
2026-08-21 09:40:40 +07:00
```cpp
2026-08-22 17:21:46 +07:00
int e1.HistoryCalls(slot); // number of CopyRates executed (unit test anti-freeze)
2026-08-21 09:40:40 +07:00
int e1.RefreshCount(slot);
int e1.TotalHistoryCalls();
long e1.LastBars(slot);
```
2026-08-22 17:21:46 +07:00
## 3. Cache Mechanism
2026-08-21 09:40:40 +07:00
2026-08-22 17:21:46 +07:00
1. `Refresh()` checks `Bars(symbol, tf)` for each slot.
2. **Only if `Bars()` changed ** (or the cache is not ready + throttle `AF_E1_RETRY_SEC` )
→ `Build(slot)` is called → `CopyRates` executes.
3. `Build` : `CopyRates(symbol, tf, 0, maxBars+1, rates)` with as-series array;
the forming bar (not yet closed) is **dropped ** based on `IsBarClosed()` :
2026-08-21 09:40:40 +07:00
`barTime + PeriodSeconds(tf) <= TimeCurrent()` .
2026-08-22 17:21:46 +07:00
4. The cache is stored in series: `bars[0]` = newest closed bar. Capacity `maxBars` .
2026-08-21 09:40:40 +07:00
2026-08-22 17:21:46 +07:00
### Invariants (verified by unit tests)
2026-08-21 09:40:40 +07:00
- `Count(slot) <= Bars(symbol, tf)` .
2026-08-22 17:21:46 +07:00
- All cache bars are closed (T4).
- `HistoryCalls` increases by **exactly 1 ** per new bar per TF (T5 anti-freeze).
- Strictly decreasing time order from index 0 (T3); valid OHLC (T2).
2026-08-21 09:40:40 +07:00
2026-08-22 17:21:46 +07:00
## 4. Limits & Result Codes
2026-08-21 09:40:40 +07:00
- `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` .
2026-08-22 17:21:46 +07:00
## 5. MQL5 Implementation Notes
2026-08-21 09:40:40 +07:00
2026-08-22 17:21:46 +07:00
- **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 version` must be `X.YY` format (e.g. "1.00") to avoid warnings.
2026-08-21 09:40:40 +07:00
## 6. Unit Test
2026-08-22 17:21:46 +07:00
- Harness: `MQL5\Experts\AlgoForge_Engine1_UnitTest.mq5` (EA, log prefix `AFTEST` ).
- 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).
2026-08-21 10:01:21 +07:00
---
2026-08-22 17:21:46 +07:00
# Engine 2 — 4 Independent Signal Agents (N/C/E/P) + Fuzzy + Aggregator
2026-08-21 10:01:21 +07:00
2026-08-22 17:21:46 +07:00
Files:
- `MQL5\Include\AlgoForge\AF_Engine2_Agents.mqh` — 4 agents + fuzzy logic (classes
2026-08-21 10:01:21 +07:00
`AFAgentNarrative` , `AFAgentContext` , `AFAgentEntry` , `AFAgentPriceAction` ).
2026-08-22 17:21:46 +07:00
- `MQL5\Include\AlgoForge\AF_Engine2_Aggregator.mqh` — separate aggregator
2026-08-21 10:01:21 +07:00
(class `AFAggregator` ) + facade `AFEngine2Signals` .
2026-08-22 17:21:46 +07:00
## 7. Key Principles
1. **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).
2. **No mutual knowledge ** — the final composition is done by the **separate
aggregator**, not between agents.
3. **Dynamic-weight fuzzy logic ** — each agent uses membership functions
(`AF_MF_Tri` / `AF_MF_Trap` ) + a light Mamdani evaluator (`AFFuzzyEval` :
`buyAcc/sellAcc/wTot` , rules `Rule(buySide, fire, weight)` ); weights adapt
to market conditions measured **from the agent's own data ** (trend strength,
volatility, ranging).
4. **Non-repainting ** — all inputs are Engine-1 closed bars (closed-bar lock
guaranteed by Engine 1; Engine 2 never reads History directly).
2026-08-21 10:01:21 +07:00
## 8. Input Timeframe
2026-08-22 17:21:46 +07:00
Agent TFs are **HARDCODED ** (no manual `InpHtfS1..S4` inputs — removed) — see
macros `AF_E2_TF_S1..S4` in `AF_Defines.mqh` :
2026-08-21 10:01:21 +07:00
```
2026-08-21 15:39:52 +07:00
AF_E2_TF_S1 = H4 (S1 = Narrative / N)
AF_E2_TF_S2 = M30 (S2 = Context / C)
AF_E2_TF_S3 = M15 (S3 = Entry / E)
2026-08-21 15:44:49 +07:00
AF_E2_TF_S4 = M3 (S4 = PriceAction/P)
2026-08-21 10:01:21 +07:00
```
2026-08-22 17:21:46 +07:00
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).
2026-08-21 10:01:21 +07:00
2026-08-22 17:21:46 +07:00
## 9. Agents
2026-08-21 10:01:21 +07:00
2026-08-22 17:21:46 +07:00
| Agent | Question | Assessed elements (from its own slot) | Dynamic weights |
2026-08-21 10:01:21 +07:00
|---|---|---|---|
2026-08-22 17:21:46 +07:00
| **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 |
2026-08-21 10:01:21 +07:00
2026-08-22 17:21:46 +07:00
Per-agent output: `AFSignalOut { buy, sell, bias, confidence, dir, reason }` .
2026-08-21 10:01:21 +07:00
2026-08-22 17:21:46 +07:00
## 10. Aggregator (separate)
2026-08-21 10:01:21 +07:00
`AFAggregator::Compute(e1, slotE, n, c, e, p, out)` :
2026-08-22 17:21:46 +07:00
1. **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_*` ).
2. **Pass 2 (dynamic weights) ** : agents aligned with the majority get a 1.5× boost;
aggregate `buy/sell` = Σ (effective weight × buy/sell) / Σ weights.
3. **Final signal ** : `dir = BUY/SELL/WAIT` with threshold `AF_AGG_BUY_TH=0.20` and
minimum support `AF_AGG_MIN_SUP=0.50` .
4. **Levels ** : `entry` = close of the closed slot-E bar; `sl/tp` based on Engine-1 ATR
(`AF_AGG_SL_ATR=1.5` , `AF_AGG_TP_ATR=2.5` ) — only on BUY/SELL signals.
5. `confidence` aggregate = support × (0.7 + 0.1 × number of aligned agents).
2026-08-21 10:01:21 +07:00
Facade `AFEngine2Signals::Compute(e1, sN, sC, sE, sP, oN, oC, oE, oP, agg)` :
2026-08-22 17:21:46 +07:00
one call runs the 4 agents + aggregator (used by Engine 3 / consumers).
2026-08-21 10:01:21 +07:00
## 11. Unit Test
2026-08-22 17:21:46 +07:00
- Harness: `MQL5\Experts\AlgoForge_Engine2_UnitTest.mq5` (log prefix `AFTEST2` ).
2026-08-21 10:01:21 +07:00
- Strategy Tester (XAUUSD, **every tick ** ):
2026-08-22 17:21:46 +07:00
- 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.
2026-08-21 10:01:21 +07:00
2026-08-22 17:21:46 +07:00
## 12. Implementation Constraints (additional, Phase-2 sessions)
2026-08-21 10:01:21 +07:00
2026-08-22 17:21:46 +07:00
- `replace_text_in_file` multi-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.
2026-08-21 10:28:46 +07:00
---
2026-08-22 17:21:46 +07:00
# Engine 3 — Display (reads Engine 1 & 2 output only)
Files:
- `MQL5\Include\AlgoForge\AF_Engine2_Display.mqh` — **display-context builder **
(Engine-2 layer): struct `AFDisplayData` , `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): struct `AFRenderCfg` + `AFR_DrawAll()` / `AFR_Clear()` . NO analysis
computation; draws only from `AFDisplayData` + `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).
2026-08-21 10:28:46 +07:00
- `MQL5\Experts\AlgoForge_Engine3_UnitTest.mq5` — unit test (Strategy Tester).
2026-08-22 17:21:46 +07:00
## 13. Engine-3 Principles
2026-08-21 10:28:46 +07:00
2026-08-22 17:21:46 +07:00
1. **READS ONLY ** Engine-1 (`AFEngine1MTF` ) & Engine-2 output (`AFEngine2Signals` /
`AFAggOut` / `AFDisplayData` ). Engine 3 computes no structure/zones itself.
2. Signal–display consistency: `AF_BuildDisplayData` (Engine 2) uses the same
helpers as the agents.
3. **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.
4. Anti-freeze: Engine 1 remains the only History reader (index cache).
2026-08-21 10:28:46 +07:00
2026-08-22 17:21:46 +07:00
## 14. Display elements (equivalent to original code 75466)
2026-08-21 10:28:46 +07:00
2026-08-22 17:21:46 +07:00
| Element | Source | Description |
2026-08-21 10:28:46 +07:00
|---|---|---|
2026-08-22 17:21:46 +07:00
| 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 — default `PERIOD_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.
2026-08-21 10:28:46 +07:00
## 16. Unit Test
2026-08-22 17:21:46 +07:00
- Harness: `MQL5\Experts\AlgoForge_Engine3_UnitTest.mq5` (log prefix `AFTEST3` ).
- 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` .
2026-08-21 10:28:46 +07:00
2026-08-22 17:21:46 +07:00
## 17. Implementation Constraints (Phase 3)
2026-08-21 10:28:46 +07:00
2026-08-22 17:21:46 +07:00
- `ObjectCreate` in 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).
2026-08-21 15:21:30 +07:00
---
2026-08-22 17:21:46 +07:00
# Backtest Baseline (Phase 5) — verification & publication
2026-08-21 15:21:30 +07:00
File: `MQL5\Experts\AlgoForge_Backtest_Baseline.mq5`
+ config `MQL5\Profiles\Tester\AlgoForge_Backtest_Baseline.XAUUSD.M15.*.ini`
2026-08-22 17:21:46 +07:00
+ 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.mq5` `ComputeMLFeatures` + 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,
2026-08-21 15:21:30 +07:00
DeltaBars=10, HTF=D1/H4/H1, ConfluenceFilter=true.
2026-08-22 17:21:46 +07:00
## 19. Modes
2026-08-21 15:21:30 +07:00
2026-08-22 17:21:46 +07:00
| Mode | Function | Output |
2026-08-21 15:21:30 +07:00
|---|---|---|
2026-08-22 17:21:46 +07:00
| 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 |
2026-08-21 15:21:30 +07:00
2026-08-22 17:21:46 +07:00
Inputs: `InpMode` , `InpThreshLong/Short` (default 0.60/0.60), `InpSL_ATR=1.0` ,
`InpTP_ATR=1.5` , `InpMaxHoldBars=24` (= baseline label horizon),
2026-08-21 15:21:30 +07:00
`InpLot=0.01` , `InpMaxBars=700` .
2026-08-22 17:21:46 +07:00
## 20. Results (2026-08-21, XAUUSD M15, every tick) — HONEST
2026-08-21 15:21:30 +07:00
2026-08-22 17:21:46 +07:00
**Mode 0 (CSV/AUC)**, 2026.01.01–08.20, 14.850 bars:
2026-08-21 15:21:30 +07:00
2026-08-22 17:21:46 +07:00
| Metric | Value | Baseline freeze |
2026-08-21 15:21:30 +07:00
|---|---|---|
| AUC LONG | 0.5305 | 0.6270 |
| AUC SHORT | 0.5487 | 0.6207 |
2026-08-22 17:21:46 +07:00
| Precision LONG @0 .60 | 0.5352 (n=4454) | — |
| Precision SHORT @0 .60 | 0.5292 (n=7010) | — |
2026-08-21 15:21:30 +07:00
2026-08-22 17:21:46 +07:00
- 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.**
2026-08-21 15:21:30 +07:00
**Mode 1 (trade)**, 2026.05.01–08.20: trades=308, net=+1125.32, maxDD=1188.18,
2026-08-22 17:21:46 +07:00
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.
2026-08-21 15:21:30 +07:00
2026-08-22 17:21:46 +07:00
## 21. Constraints (Phase 5)
2026-08-21 15:21:30 +07:00
2026-08-22 17:21:46 +07:00
- 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_CSV` delimiter in this terminal = **TAB ** (not `;` /`,` ) →
`backtest_eval.py` uses `delimiter="\t"` .
- `PositionSelect` overload ambiguity on build 6093 (string vs ulong) → helper
2026-08-21 15:21:30 +07:00
`BTSelectTicket()` (loop `PositionsTotal` /`PositionGetTicket` ).
2026-08-22 17:21:46 +07:00
- OrderSend requotes (ret=10018) appear in the tester → recorded, no retry (honest).