2026-08-21 21:00:06 +07:00 | | | # FEATURE_CONTRACT.md — SNIPERGOLD_ML P2 SOURCE OF TRUTH
|
| | |
|
2026-08-22 17:21:46 +07:00 | | | Status: **AUTHORITATIVE (P2.4)** — one definition for MQL5 runtime and Python training.
|
| | | Version/hash: see §5 (computed during the P2.5 implementation).
|
| | | Date: 2026-08-21
|
2026-08-21 21:00:06 +07:00 | | |
|
| | | ---
|
| | |
|
2026-08-22 17:21:46 +07:00 | | | ## 0. Temporal & window contract (applies to ALL features)
|
2026-08-21 21:00:06 +07:00 | | |
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Row : M15 bar with open time t; features computed at bar CLOSE, tc = t + 900 s.
|
| | | Closed-bar : ALL inputs are only closed bars at tc (no forming bar).
|
| | | M15 window : 700-bar cache = [t-699, t]; ProcessStructure begin = max(100, total-600) = 100
|
| | | -> 600 bars analyzed; absolute valid pivots [t-649, t-50].
|
| | | HTF window : D1/H4/H1 250-bar cache; bias uses the 200 NEWEST bars ending at E_ea(t).
|
| | | E_ea(t) : last closed HTF bar at tc = searchsorted(ht, tc - period, 'right') - 1.
|
| | | ATR(t) : rolling 14 closed M15 bars ending at t (e1.ATR / atr_series).
|
| | | Normalization: features NOT normalized at the feature level; z-score with the exported
|
| | | model mean/std (SniperGold_ML.mqh) is done inside the model.
|
| | | Missing data : 0 (neutral) when input unavailable, per the runtime code.
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ---
|
| | |
|
2026-08-22 17:21:46 +07:00 | | | ## 1. Feature table (19 features)
|
2026-08-21 21:00:06 +07:00 | | |
|
| | | ### f0 — htf1_bias (D1 bias)
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : HTF D1 bias direction (BTTF fractal swing s=3 + break)
|
2026-08-21 21:00:06 +07:00 | | | Source timeframe : D1
|
2026-08-22 17:21:46 +07:00 | | | Input bars : 200 newest D1 bars ending at E_ea(t)
|
| | | Lookback : 200 D1 bars
|
2026-08-21 21:00:06 +07:00 | | | Warm-up : need < 120 -> 0 (Neutral)
|
2026-08-22 17:21:46 +07:00 | | | Closed-bar rule : window only closed bars <= E_ea(t); loop break excludes the last window bar
|
2026-08-21 21:00:06 +07:00 | | | Formula : BTTFBias(h,l,c,200): s=3; i in [4,198]; pivot p=i-3 (p>=3) fractal ±3;
|
2026-08-22 17:21:46 +07:00 | | | break when cl[i] > up (bullish) / < dn (bearish); final trend (-1/0/+1)
|
| | | Normalization : -1/0/+1 (used raw)
|
| | | Missing-data : 0 when < 120 bars
|
| | | Runtime : BTTFBias(sD1) [fixed: 200 newest cache bars]
|
2026-08-21 21:00:06 +07:00 | | | Training : tf_bias_asof(hh,hl,hc, E_ea) [P2.1 corrected]
|
2026-08-22 17:21:46 +07:00 | | | Parity test : abs(runtime - training) <= 1e-9, all rows
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f1 — htf2_bias (H4 bias)
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Same as f0 with source timeframe H4, E_ea on H4 (period 14400).
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f2 — htf3_bias (H1 bias)
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Same as f0 with source timeframe H1, E_ea on H1 (period 3600).
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f3 — swing_trend
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : swing structure direction (BOS/CHoCH) at bar t
|
2026-08-21 21:00:06 +07:00 | | | Source timeframe : M15
|
2026-08-22 17:21:46 +07:00 | | | Input bars : 700-bar cache; analysis region [t-599, t]; pivot feed [t-649, t-50]
|
| | | Lookback : 600 analysis bars (+100 cache warmup)
|
| | | Warm-up : cache bars < 100 not analyzed; MIN_BARS=160 (EA)
|
| | | Closed-bar rule : closed bars; forming bar dropped from the cache
|
| | | Formula : ProcessStructure(SWING_LEN=50, internal=false) -> final trend (-1/0/+1)
|
2026-08-21 21:00:06 +07:00 | | | Normalization : -1/0/+1
|
| | | Missing-data : 0
|
| | | Runtime : g_swTrend (ProcessStructure)
|
| | | Training : build_structure(window 700, begin=100) -> sw['trend'][-1]
|
| | | Parity test : 1e-9
|
| | | ```
|
| | |
|
| | | ### f4 — internal_trend
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Same as f3 with INTERNAL_LEN=5, internal=true, swing-timeline confluence.
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f5 — chart_bias
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : combined swing+internal bias (last break)
|
| | | Formula : if inLastBreak >= swLastBreak && inTrend!=0 -> inTrend; else swTrend;
|
| | | if 0 -> swTrend if !=0 else inTrend
|
| | | Runtime : g_bias; Training: build_structure window -> same logic
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f6 — eq_pos_norm
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : price position relative to equilibrium (swing high/low)
|
| | | Formula : rng = sw_high - sw_low; eqPos = 2*(price - sw_low)/rng - 1; 0 if rng<=0
|
| | | sw_high/sw_low : last swing HIGH/LOW pivot in [t-649, t-50]; 0 if none (P2.3)
|
| | | Normalization : continuous ([-1,1] typical); Missing: 0
|
| | | Runtime : ComputeMLFeatures with g_swHigh/g_swLow; Training: windowed pivots
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f7 — sweep_dir
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : direction of the last liquidity grab (internal pivot)
|
| | | Formula : for each internal pivot (p,lvl,isHigh), window GRAB_WINDOW=8:
|
2026-08-21 21:00:06 +07:00 | | | bearish grab: isHigh && high[b]>lvl && close[b]<lvl -> dir=-1
|
| | | bullish grab: !isHigh && low[b]<lvl && close[b]>lvl -> dir=+1
|
2026-08-22 17:21:46 +07:00 | | | update if b > swpBar
|
2026-08-21 21:00:06 +07:00 | | | Runtime : DetectLiquidityGrabs; Training: inn['pivots'] + window 8
|
| | | ```
|
| | |
|
| | | ### f8 — choch_dir
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : direction of the last CHoCH (internal structure)
|
| | | Formula : trend reversal at a break with swing confluence allow (SwingTrendAt)
|
2026-08-21 21:00:06 +07:00 | | | Runtime : g_chochDir; Training: inn['choch_dir']
|
| | | ```
|
| | |
|
| | | ### f9 — choch_confirm
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : CHoCH confirms the sweep
|
2026-08-21 21:00:06 +07:00 | | | Formula : chochDir!=0 && chochBar >= swpBar && chochDir == swpDir -> 1
|
| | | ```
|
| | |
|
| | | ### f10 — eqh_swept (P2.2 corrected)
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : equal-high level broken
|
| | | Input bars : swing pivot list window [t-649, t-50]
|
| | | Formula : for CONSECUTIVE pairs (p1,p2) in the list, both highs,
|
2026-08-21 21:00:06 +07:00 | | | |p2-p1|>=EQ_BARS(3), |pr2-pr1| <= EQ_TOL_ATR(0.10) x ATR(t):
|
2026-08-22 17:21:46 +07:00 | | | swept if any bar b in (p2, t] with high[b] > pr2
|
| | | ATR basis : ATR of the CURRENT row bar (not pivot ATR) — legacy v4.4
|
2026-08-21 21:00:06 +07:00 | | | Normalization : 0/1
|
| | | ```
|
| | |
|
| | | ### f11 — eql_swept (P2.2 corrected)
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Same as f10 for lows: |pr2-pr1| <= 0.10 x ATR(t); swept if low[b] < pr2.
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f12 — delta_sign
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : body vs wick balance direction (delta proxy), 10 bars
|
2026-08-21 21:00:06 +07:00 | | | Formula : sum (bb-ss)/tt * vol / sumVol; sign
|
2026-08-22 17:21:46 +07:00 | | | Runtime : BTDelta(DELTA_BARS=10); Training: 10-bar loop
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f13 — delta_mag
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Formula : clamp(sum/sumVol, -1, 1); continuous
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f14 — dist_high_atr
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : distance of price to the swing high in ATR units
|
| | | Formula : clamp((sw_high - price)/ATR, -10, 10); 0 if sw_high=0
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f15 — dist_low_atr
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Formula : clamp((price - sw_low)/ATR, -10, 10); 0 if sw_low=0
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f16 — mom20_atr
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : 20-interval momentum
|
| | | Formula : (price - close[t-20]) / ATR; price = c[t]; guard t>=21
|
| | | Runtime : (price - close[tcv-21])/g_atr with tc=tcv=total (parity verified 0.0000)
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f17 — range_atr
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Formula : (sw_high - sw_low)/ATR; 0 if rng<=0
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ### f18 — confluence
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | Meaning : heuristic confluence score (0..100)
|
2026-08-21 21:00:06 +07:00 | | | Formula : g_bias!=0 +10; hb==3||hr==3 +25 / hb>=2&&hr==0 +12;
|
| | | eqPos<0&&bias>0 +10; eqPos>0&&bias<0 +10; swpDir!=0 +15;
|
| | | chochDir==swpDir +15; (swpDir==1&&bias>0)||(swpDir==-1&&bias<0) +15;
|
| | | min(100, total)
|
| | | Runtime : ComputeMLFeatures; Training: confluence_feature(F)
|
| | | ```
|
| | |
|
| | | ---
|
| | |
|
2026-08-22 17:21:46 +07:00 | | | ## 2. MANDATORY implementation changes (results of P2.1–P2.3)
|
2026-08-21 21:00:06 +07:00 | | |
|
| | | ```text
|
| | | RUNTIME (EA) :
|
2026-08-22 17:21:46 +07:00 | | | - BTTFBias : replace GetBar(slot, count-1-i) -> GetBar(slot, need-1-i)
|
| | | (window = the 200 NEWEST cache bars, not the 200 oldest) [P2.1]
|
| | | - DetectEQ / window / delta / mom : UNCHANGED (already contract-conformant)
|
2026-08-21 21:00:06 +07:00 | | |
|
| | | TRAINING (build_features) :
|
2026-08-22 17:21:46 +07:00 | | | - f0-f2 : as-of CLOSE mapping (E_ea = closed HTF bar at tc), not as-of open
|
| | | with lag-1; 200-newest window ending at E_ea [P2.1]
|
| | | - f10/f11 : consecutive-list pairs + tol = EQ_TOL_ATR * A[i] (row ATR)
|
| | | + pivot window [i-649, i-50] [P2.2]
|
| | | - f3-f9, f14, f15, f17 : structure from a 700-bar cache slice ending at i, begin=100
|
| | | (sw_high/sw_low = last pivot in [i-649, i-50], 0 when empty) [P2.3]
|
2026-08-21 21:00:06 +07:00 | | | ```
|
| | |
|
| | | ---
|
| | |
|
| | | ## 3. Parity test definition (P2.5)
|
| | |
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | - timestamp parity : join runtime dump (mode 2, corrected EA) vs training feed
|
| | | 100% (no missing/extra rows)
|
2026-08-21 21:00:06 +07:00 | | | - per-feature : abs(runtime - training) <= eps
|
2026-08-22 17:21:46 +07:00 | | | f0-f5,f7-f12,f18 : eps = 1e-9 (discrete)
|
| | | f6,f13-f17 : eps = 1e-6 (continuous)
|
2026-08-21 21:00:06 +07:00 | | | - prediction parity: SGMLProb(runtime features) vs Python model output,
|
| | | max/mean |dp| + mismatch count/rate (P2.6)
|
| | | ```
|
| | |
|
| | | ## 4. Versioning
|
| | |
|
| | | ```text
|
2026-08-22 17:21:46 +07:00 | | | FEATURE_CONTRACT v1.0 (2026-08-21) — based on:
|
| | | docs/P2_1_HTF_FORENSIC.md (f0-f2, classification B, corrected tf_bias_asof(E_ea))
|
2026-08-21 21:00:06 +07:00 | | | docs/P2_2_EQH_EQL_FORENSIC.md (f10/f11, legacy v4.4, ATR@row + window)
|
2026-08-22 17:21:46 +07:00 | | | docs/P2_3_CONTEXT_WINDOW.md (700-bar window, sw_high/sw_low [t-649,t-50])
|
2026-08-21 21:00:06 +07:00 | | | EA: AlgoForge_Backtest_Baseline.mq5; Training: train_model.py build_features
|
| | | ```
|
| | |
|
2026-08-22 17:21:46 +07:00 | | | ## 5. Hash/version (filled after the P2.5 implementation)
|
2026-08-21 21:00:06 +07:00 | | |
|
| | | ```text
|
| | | Feature contract hash : C44CC6F2B740C32D06F776BD7C3E669DC5A8A6DE0484230544EBFFCF517D38DD (FEATURE_CONTRACT.md)
|
| | | Training build : 4680F9057B6EF32FE311C5BA10DAC2A243E15F624F61921557C92EEC59FCCEA7 (build_features_p2.py)
|
| | | Runtime dump (fixed) : 5F8AB5CB6EFD4E1188D9922C8050B7D1301B842B4C1AF276B98C85EF8D134CE5 (AlgoForge_bt_features_fixed_XAUUSD_M15.csv)
|
| | | Parity feature : 94CBFB0C5E8784D926C4FDA8506BF3DE470D672B68F02EC47C821D8139022F04 (parity_p2.py)
|
| | | Parity prediction : 83B00528BB02D731FA8CEA853ADBBDD745796E778B6BD4177F6BEDB4A87535E7 (parity_prediction.py)
|
2026-08-22 17:21:46 +07:00 | | | EA runtime (fixed) : BTTFBias GetBar(need-1-i) compiled 2026-08-21 20:23 (ex5)
|
2026-08-21 21:00:06 +07:00 | | | ```
|