SniperGold_ML/docs/P3_S_F7_EVENT_LIFECYCLE_FORENSIC.md

9.9 KiB

P3-S.0 F7 EVENT LIFECYCLE FORENSIC — LIQUIDITY GRAB STATE/EVENT BUG REPAIR

Date       : 2026-08-22
Session    : P3-S.0 — SAFE SEMANTIC BUG REPAIR (f7 DetectLiquidityGrabs)
Scope      : ONLY the f7 event lifecycle. f10/f11, OB, FVG, CHoCH, timeframe, ML UNCHANGED.
Checkpoint : b877fb6 (P3 Golden Dataset Phase 1) — working tree clean at start
Provenance : P2 8d330343 | contract C44CC6F2 | model 06df8452 | dataset e85a0861

A. ORIGINAL BEHAVIOR

DetectLiquidityGrabs (f7 sweep_dir) — FEATURE_CONTRACT v1.0:

reference        : internal swing pivot (fractal len=5)
trigger condition: bearish grab: isHigh && high[b]>lvl && close[b]<lvl  (buy-side swept -> -1)
                   bullish grab: !isHigh && low[b]<lvl && close[b]>lvl  (sell-side swept -> +1)
detect window    : 8 bars after the pivot (GRAB_WINDOW)
state variables  : g_swpDir, g_swpBar, g_swpLevel
reset            : g_swpDir=0; g_swpBar=-1 at the START of every bar (ComputeAll)
expiration       : NONE
emission         : g_feat[7] = g_swpDir (raw, no age bound)
consumption      : f9 (chochOK) uses g_swpBar/g_swpDir; f18 (+15); E_BUY/E_SELL stream

Measured behavior (XAUUSD M15, 2017-2026, n=197.032):

onsets            = 2.835
active bars       = 196.928 (99,95%)
repetition ratio  = 69,5x
state run median  = 94 bars
state run max     = 996 bars
NEVER resets to 0 after the first grab

B. ROOT CAUSE

1. The LEGACY state is "last-grab-ever": every bar, ComputeAll resets g_swpDir=0,
   then DetectLiquidityGrabs scans ALL internal pivots (700-bar window) and fills
   g_swpDir = the direction of the MOST RECENT grab ever — including grabs long past
   (filtered only by the pivot window [t-649, t-50], not by event age).

2. No expiration: g_feat[7] = g_swpDir is emitted without checking
   (current_bar - g_swpBar). Consequently a single onset yields TRUE for hundreds of bars.

3. Event consumers (f9-confirm stream E_BUY/E_SELL) treat the state as a repeated event
   -> P3.2.2: retention 1.5%, cluster 46 bars, serial dependence disappears only after de-overlap.

NOT a bug in the trigger condition (wick/close-back/8-bar window) — that was NOT changed.

C. WHY IT IS A BUG

Intended semantics (FEATURE_CONTRACT f7): "direction of the LAST liquidity grab (internal pivot)".
As an EVENT feature, a single onset must NOT produce TRUE for hundreds of bars.
Legacy behavior = persistent state counted as an event -> redundant event stream,
not independent (ac1 0.50 in ALL, see P3.2.2), invalid setup units.

Standard event lifecycle:
  NO_SWEEP -> SWEEP_ONSET -> EVENT_CONSUMED/EXPIRED -> NO_SWEEP

D. CORRECTED EVENT LIFECYCLE

Expiration derived from semantics ALREADY present in the implementation (not arbitrary):
  InpSeqWindow = 40 (v4.4/v4.5): "Max bars: sweep -> CHoCH -> entry must chain within".
  v4.4/v4.5 already use (curBar - g_swpBar) <= InpSeqWindow for the sweepBull/Bear signal gate.

f7 fix:
  f7[r] = g_swpDir[r]  if g_swpBar[r] >= 0 AND (r - g_swpBar[r]) <= SEQ_WINDOW (40)
          0            if expired (age > 40) / no grab

Resulting lifecycle:
  NO_SWEEP (0) -> SWEEP_ONSET (dir) -> valid up to onset+40 bars -> EXPIRED -> NO_SWEEP (0)
  A newer event (more recent grab) replaces the old event (existing semantics: b > g_swpBar).
  An event arriving within the 40-bar window "refreshes" (chain) — not one onset producing
  hundreds of bars, but several sequential onsets.

UNCHANGED:
  - trigger condition (wick + close-back), GRAB_WINDOW=8, pivot len=5
  - internal g_swpDir/g_swpBar (used by f9/f18) — remains legacy state
  - f10/f11, OB, FVG, CHoCH, M15 timeframe, ML

E. CODE CHANGE

E.1 EA runtime — Experts\AlgoForge_Backtest_Baseline.mq5 (root & publish)

// new (after AF_BT_GRAB_WIN):
#define AF_BT_SEQ_WINDOW   40      // InpSeqWindow (v4.4): sweep -> CHoCH -> entry chain window
                                   // = event validity/expiry for f7 (P3-S.0 f7 event lifecycle fix)

// replace g_feat[7] = g_swpDir; ->
// P3-S.0 f7 event lifecycle fix: sweep_dir is active only during the valid event
// (onset .. onset+SeqWindow); after expiry -> NO_SWEEP (0). Does not change the
// trigger condition/close-back/pivot; internal g_swpDir stays for f9/f18.
g_feat[7] =(g_swpBar>=0 && (tc-1-g_swpBar)<=AF_BT_SEQ_WINDOW)? g_swpDir : 0;  // sweep_dir

E.2 Indicator v4.4 — SniperGold_SMC_ProPlus_v4_4.mq5

g_feat[7] =(g_swpBar>=0 && (tc-1-g_swpBar)<=InpSeqWindow)? g_swpDir : 0;  // sweep_dir

E.3 Indicator v4.5 — SniperGold_SMC_ProPlus_v4_5.mq5

g_feat[6]=eqPos; g_feat[7]=((g_swpBar>=0 && (tc-1-g_swpBar)<=InpSeqWindow)? g_swpDir : 0); g_feat[8]=g_chochDir;

E.4 Python machine annotator — ml/p3/smc_semantic/

smc_semantic_common.py : + SEQ_WINDOW=40 ; + f7_lifecycle(sweep_dir, sweep_bar)
machine_annotator.py   : v2 uses f7_lifecycle; output machine_annotations_f7_v2.csv

Compile: EA 0 errors / v4.4 0 errors / v4.5 0 errors (MetaEditor, build 6093, AVX512).

F. REGRESSION TESTS

File: ml/p3/smc_semantic/test_f7_lifecycle.py15/15 PASS

R1 Single onset            : 1 event (not multiple)                      PASS
R1 active bounded          : active_bars <= 41                             PASS
R2 Persistent condition    : condition true 450 bars -> 1 event              PASS
R3 New independent sweep   : 2 separate events                              PASS
R4 Invalidated/expired     : f7[100]=0 (age>40), f7[10]=1                  PASS
R5 No-sweep                : 0 events                                       PASS
R6 f10 unchanged vs cache  : 0 mismatch / 4000 bars                         PASS
R6 f11 unchanged vs cache  : 0 mismatch / 4000 bars                         PASS
R6 EA source contains fix  : AF_BT_SEQ_WINDOW + (tc-1-g_swpBar)<=...      PASS
R6 v4.4 / v4.5 contain fix : PASS                                          PASS
R6 compile                 : EA/v4.4/v4.5 0 errors 0 warnings (manual)      PASS

G. BEFORE / AFTER HISTORICAL RESULTS

METRIC                       BEFORE        AFTER
onsets                       2.835         2.835 (trigger unchanged)
active bars f7 != 0          196.928       90.140
% active bars                99,95%        45,75%
run median                   94 bars       41 bars
run max                      996 bars      152 bars (dense event chain)
max per-onset lifetime       ~996          41  <= SEQ_WINDOW+1  (FIX EVIDENCE)
events removed (bars)        —             106.788

Interpretation: a single onset now yields TRUE for at most 41 bars (not hundreds). Run 152 = ~4 consecutive events in a dense cluster (not a single onset).

H. GOLDEN DATASET IMPACT

Sampling UNCHANGED (same 60 cases). Annotation rebuild:

machine_annotations_f7_v1_LEGACY.csv  (before fix — preserved)
machine_annotations_f7_v2.csv         (after fix — primary)
f7_v1_vs_v2_comparison.json

summary:
  n_cases                  = 60
  cases_changed            = 22
  cases_unchanged          = 38
  events_removed           = 22
  events_added             = 0
  persistent_state_corrected = 22
  f10_f11_unchanged        = true

Detail per stratum:
  changed : STALE_GRAB 15, EQH_SWEPT 4, EQ_MARGINAL 2, EQL_SWEPT 1
  unchanged: FRESH_GRAB 15, AGED_GRAB 10, EQ_NEARCROSS 6, EQL_SWEPT 5, EQH_SWEPT 2
  decision v2: YES 45, NO 15 (all NO = STALE_GRAB — stale state is now NO_SWEEP)

Note: some EQH/EQL/MARGINAL cases changed on the f7 FIELD (grab state expired), but machine_decision stays YES via f10/f11 — no semantic information lost.

I. EXPLICITLY UNCHANGED SEMANTICS

f10/f11 (DetectEQ)   : NOT changed — wick condition, close-back (N/A), ATR tolerance
                       0.10*ATR(row), EQ pair window [r-649, r-50] — SEMANTIC REVIEW REQUIRED
OB / FVG / CHoCH / MSS : NOT changed
timeframe            : f7 remains M15; no hidden H4/M30/M3 usage
ML                   : SniperGold_ML.mqh, _p26_corrected.mqh, train_model.py,
                       build_features_p2.py, P2.6 baseline — NOT changed
AF_DetectSweep (E2)  : OUT OF SCOPE (different implementation, not an ML primitive)
internal g_swpDir    : stays legacy (f9/f18 not changed this session)

EVIDENCE STATUS

BUG                     = CONFIRMED + FIXED (f7 event lifecycle)
SEMANTIC DEFINITION     = STILL UNDER REVIEW (f10/f11 rejection, EQ semantics, timeframe)
HUMAN ANNOTATION        = NOT STARTED (must target the corrected machine spec,
                          machine results NOT shown to annotators)

ARTIFACTS

docs/P3_S_F7_EVENT_LIFECYCLE_FORENSIC.md
ml/p3/smc_semantic/smc_semantic_common.py        (SEQ_WINDOW + f7_lifecycle)
ml/p3/smc_semantic/machine_annotator.py          (v2)
ml/p3/smc_semantic/test_f7_lifecycle.py          (R1-R6 + historical)
ml/p3/smc_semantic/output/machine_annotations_f7_v2.csv
ml/p3/smc_semantic/output/f7_v1_vs_v2_comparison.json
ml/p3/smc_semantic/output/machine_annotations_f7_v1_LEGACY.csv  (preserved)
ml/p3/smc_semantic/output/cases_meta_v1_LEGACY.json             (preserved)
Experts/AlgoForge_Backtest_Baseline.mq5          (patched, 0 errors)
Indicators/Downloads/SniperGold_SMC_ProPlus_v4_4.mq5 (patched, 0 errors)
Indicators/Downloads/SniperGold_SMC_ProPlus_v4_5.mq5 (patched, 0 errors)

HASHES BEFORE FIX (preserved)

AlgoForge_Backtest_Baseline.mq5 (root) : c3fa65846b31f92abdf286ba1b6286466eda03191f83176e808c8f65b8a6839c
SniperGold_ML.mqh                     : c0d5cae6663f457d2c87750bc8690b47ef4557d0426bfb4183280dc1d1a2f1e5
SniperGold_ML_p26_corrected.mqh       : 06df8452a112290ecb9bae1a3dbff6df492e084f8b872885d808bb0303ec6a70
SniperGold_SMC_ProPlus_v4_4.mq5       : 118ffee68760bbd53048fdfc3382a1697fa6870fb3e6d2fd1a4e22eb8e84c2d0
SniperGold_SMC_ProPlus_v4_5.mq5       : 62d25e4f9c63ca189ec366f5c4ddd061839cfdb360a6a5468db8e6db239700b4
smc_semantic_common.py                : b754abd60593d84a61147d663f673661ae2089f2c0694586ccf7c1cb25f1acf6
machine_annotator.py                  : 7b67b5dcd5ac4c0b57f8bf6fbc36ba7a60631031239ef3059590aed528a60602