229 lines
8.8 KiB
Markdown
229 lines
8.8 KiB
Markdown
# FEATURE_CONTRACT.md — SNIPERGOLD_ML P2 SOURCE OF TRUTH
|
|
|
|
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
|
|
|
|
---
|
|
|
|
## 0. Temporal & window contract (applies to ALL features)
|
|
|
|
```text
|
|
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.
|
|
```
|
|
|
|
---
|
|
|
|
## 1. Feature table (19 features)
|
|
|
|
### f0 — htf1_bias (D1 bias)
|
|
```text
|
|
Meaning : HTF D1 bias direction (BTTF fractal swing s=3 + break)
|
|
Source timeframe : D1
|
|
Input bars : 200 newest D1 bars ending at E_ea(t)
|
|
Lookback : 200 D1 bars
|
|
Warm-up : need < 120 -> 0 (Neutral)
|
|
Closed-bar rule : window only closed bars <= E_ea(t); loop break excludes the last window bar
|
|
Formula : BTTFBias(h,l,c,200): s=3; i in [4,198]; pivot p=i-3 (p>=3) fractal ±3;
|
|
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]
|
|
Training : tf_bias_asof(hh,hl,hc, E_ea) [P2.1 corrected]
|
|
Parity test : abs(runtime - training) <= 1e-9, all rows
|
|
```
|
|
|
|
### f1 — htf2_bias (H4 bias)
|
|
```text
|
|
Same as f0 with source timeframe H4, E_ea on H4 (period 14400).
|
|
```
|
|
|
|
### f2 — htf3_bias (H1 bias)
|
|
```text
|
|
Same as f0 with source timeframe H1, E_ea on H1 (period 3600).
|
|
```
|
|
|
|
### f3 — swing_trend
|
|
```text
|
|
Meaning : swing structure direction (BOS/CHoCH) at bar t
|
|
Source timeframe : M15
|
|
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)
|
|
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
|
|
Same as f3 with INTERNAL_LEN=5, internal=true, swing-timeline confluence.
|
|
```
|
|
|
|
### f5 — chart_bias
|
|
```text
|
|
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
|
|
```
|
|
|
|
### f6 — eq_pos_norm
|
|
```text
|
|
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
|
|
```
|
|
|
|
### f7 — sweep_dir
|
|
```text
|
|
Meaning : direction of the last liquidity grab (internal pivot)
|
|
Formula : for each internal pivot (p,lvl,isHigh), window GRAB_WINDOW=8:
|
|
bearish grab: isHigh && high[b]>lvl && close[b]<lvl -> dir=-1
|
|
bullish grab: !isHigh && low[b]<lvl && close[b]>lvl -> dir=+1
|
|
update if b > swpBar
|
|
Runtime : DetectLiquidityGrabs; Training: inn['pivots'] + window 8
|
|
```
|
|
|
|
### f8 — choch_dir
|
|
```text
|
|
Meaning : direction of the last CHoCH (internal structure)
|
|
Formula : trend reversal at a break with swing confluence allow (SwingTrendAt)
|
|
Runtime : g_chochDir; Training: inn['choch_dir']
|
|
```
|
|
|
|
### f9 — choch_confirm
|
|
```text
|
|
Meaning : CHoCH confirms the sweep
|
|
Formula : chochDir!=0 && chochBar >= swpBar && chochDir == swpDir -> 1
|
|
```
|
|
|
|
### f10 — eqh_swept (P2.2 corrected)
|
|
```text
|
|
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,
|
|
|p2-p1|>=EQ_BARS(3), |pr2-pr1| <= EQ_TOL_ATR(0.10) x ATR(t):
|
|
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
|
|
Normalization : 0/1
|
|
```
|
|
|
|
### f11 — eql_swept (P2.2 corrected)
|
|
```text
|
|
Same as f10 for lows: |pr2-pr1| <= 0.10 x ATR(t); swept if low[b] < pr2.
|
|
```
|
|
|
|
### f12 — delta_sign
|
|
```text
|
|
Meaning : body vs wick balance direction (delta proxy), 10 bars
|
|
Formula : sum (bb-ss)/tt * vol / sumVol; sign
|
|
Runtime : BTDelta(DELTA_BARS=10); Training: 10-bar loop
|
|
```
|
|
|
|
### f13 — delta_mag
|
|
```text
|
|
Formula : clamp(sum/sumVol, -1, 1); continuous
|
|
```
|
|
|
|
### f14 — dist_high_atr
|
|
```text
|
|
Meaning : distance of price to the swing high in ATR units
|
|
Formula : clamp((sw_high - price)/ATR, -10, 10); 0 if sw_high=0
|
|
```
|
|
|
|
### f15 — dist_low_atr
|
|
```text
|
|
Formula : clamp((price - sw_low)/ATR, -10, 10); 0 if sw_low=0
|
|
```
|
|
|
|
### f16 — mom20_atr
|
|
```text
|
|
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)
|
|
```
|
|
|
|
### f17 — range_atr
|
|
```text
|
|
Formula : (sw_high - sw_low)/ATR; 0 if rng<=0
|
|
```
|
|
|
|
### f18 — confluence
|
|
```text
|
|
Meaning : heuristic confluence score (0..100)
|
|
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)
|
|
```
|
|
|
|
---
|
|
|
|
## 2. MANDATORY implementation changes (results of P2.1–P2.3)
|
|
|
|
```text
|
|
RUNTIME (EA) :
|
|
- 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)
|
|
|
|
TRAINING (build_features) :
|
|
- 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]
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Parity test definition (P2.5)
|
|
|
|
```text
|
|
- timestamp parity : join runtime dump (mode 2, corrected EA) vs training feed
|
|
100% (no missing/extra rows)
|
|
- per-feature : abs(runtime - training) <= eps
|
|
f0-f5,f7-f12,f18 : eps = 1e-9 (discrete)
|
|
f6,f13-f17 : eps = 1e-6 (continuous)
|
|
- prediction parity: SGMLProb(runtime features) vs Python model output,
|
|
max/mean |dp| + mismatch count/rate (P2.6)
|
|
```
|
|
|
|
## 4. Versioning
|
|
|
|
```text
|
|
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))
|
|
docs/P2_2_EQH_EQL_FORENSIC.md (f10/f11, legacy v4.4, ATR@row + window)
|
|
docs/P2_3_CONTEXT_WINDOW.md (700-bar window, sw_high/sw_low [t-649,t-50])
|
|
EA: AlgoForge_Backtest_Baseline.mq5; Training: train_model.py build_features
|
|
```
|
|
|
|
## 5. Hash/version (filled after the P2.5 implementation)
|
|
|
|
```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)
|
|
EA runtime (fixed) : BTTFBias GetBar(need-1-i) compiled 2026-08-21 20:23 (ex5)
|
|
```
|