ALGO

SPECS

Canonical Specs letti direttamente dai file reali del project ALGO.

Download

README.md

Download
# Canonical Specs Index

Canonical specs in this folder are hard-law for ALGO.

## Current B-series

- `B1_alignment_matrix.txt`
- `B2_core_overview_architecture.txt`
- `B3_ea_output_spec.txt`
- `B4_ea_ai_prompt_dev_rules.txt`
- `B5_ea_technical_template.py`
- `B6_ea_output_validator_strict.py`
- `B7_metrics_spec.txt`
- `B8_ea_generation_master_specs.txt`
- `B9_external_ai_dev_handoff_pack.txt`
- `B10_ea_canonical_examples.txt`

## Reading order for EA generation work

1. `B2_core_overview_architecture.txt`
2. `B3_ea_output_spec.txt`
3. `B4_ea_ai_prompt_dev_rules.txt`
4. `B5_ea_technical_template.py`
5. `B6_ea_output_validator_strict.py`
6. `B7_metrics_spec.txt`
7. `B8_ea_generation_master_specs.txt`
8. `B9_external_ai_dev_handoff_pack.txt`
9. `B10_ea_canonical_examples.txt`

## Role of the newer docs

- `B8`: master model for AI-driven EA generation
- `B9`: ready-to-send operational pack for external AI DEV
- `B10`: canonical worked examples to imitate

## Important note on versions

- Document version and payload `contract_version` are not the same thing.
- The B-series documents can advance for governance and clarification while the enforced EA payload contract remains `B3-V04` until explicitly changed in both spec and validator.

B1_alignment_matrix.txt

Download
============================================================
ALGO - DOCUMENT HEADER
------------------------------------------------------------
Doc ID       : B1
Title        : EA - ALIGNMENT MATRIX (ALL REQUIRED)
Filename     : B1 - EA - ALIGNMENT MATRIX (ALL REQUIRED) - V06.txt
Version      : V06
Updated      : 2026-04-11 22:35 (Europe/Rome)
Status       : READY FOR IMPLEMENTATION
Changelog    :
- V06: aggiornato l'indice canonico fino a B10 e incluso il pacchetto AI esterna.
- V05: riallineato al modello Pattern Source Family / EXEC_TF / MRE.
- V05: introdotta la risoluzione automatica dei tf del Pattern tramite Pattern Source Family.
- V05: aggiornato il contratto input EA da pattern_dfs flat a pattern_contexts per Pattern.
============================================================

B1 - EA - ALIGNMENT MATRIX (ALL REQUIRED)
Versione: V06
Status  : READY FOR IMPLEMENTATION

SCOPO
- Allineare EA, UI, API, runtime, validator, DB ed export.
- Definire cosa deve essere IDENTICO in tutti i layer.

============================================================
1) DECISION REGISTER (HARD-LAW)
============================================================
DR-01 DB WINS
- Il DB e' la source of truth per timeframe canonici, dataset, source family e contract specs.

DR-02 TIMEFRAME TOKENS CANONICI
- Token supportati: S1, M1, M3, M5, M10, M15, M30, H1, H2, H4, H6, D, W, M, Y.
- Daily canonico = D.
- Vietati alias e sinonimi.

DR-03 DATASET != TIMEFRAME
- Il timeframe non identifica da solo il dataset.
- Possono esistere piu' dataset con lo stesso timeframe.

DR-04 PATTERN SOURCE FAMILY
- La scelta dati delle Conditions avviene a livello di Pattern Source Family.
- L'utente non sceglie un dataset singolo di Pattern a tf fisso.
- Il runtime risolve i dataset richiesti da tutte le Conditions del Pattern.

DR-05 EXEC_TF
- EXEC_TF e' il timeframe operativo scelto nel run.
- Entry/Exit usano EXEC.
- MRE definisce il minimo EXEC_TF ammesso.

DR-06 EA INPUT CONTRACT CANONICO
run_backtest(symbol, timeframe, period, params, df_exec, pattern_contexts)
- timeframe = EXEC_TF del run
- df_exec = dataframe del dataset di esecuzione
- pattern_contexts = dict[pattern_id -> pattern context risolto dal runtime]

DR-07 AI_DESCRIPTION
- Nome concettuale: AI_DESCRIPTION
- Chiave output: ai_description

DR-08 ALL REQUIRED / NO EXTRA KEYS
- Ogni chiave prevista dallo schema deve esistere sempre.
- Nessuna chiave extra fuori contratto.

DR-09 DATETIME / TIMEZONE
- Input market data: dt = BAR START in America/New_York.
- Output EA: UTC ISO-8601 con Z.

DR-10 COSTI / MAE-MFE
- commission_per_trade e slippage_ticks sono roundturn per 1 contratto.
- trade.commission e trade.slippage sono totali posizione.
- MAE/MFE canonici: ticks, points, ccy_1c.

============================================================
2) MATRICE DI ALLINEAMENTO END-TO-END
============================================================
2.1 EA
- Dichiara PATTERN_REGISTRY, Variables, Constants, Pattern TF e MRE.
- Produce trades chiusi + events.
- Usa solo timeframe canonici del DB e EXEC come token logico.

2.2 UI BACKTEST
- Permette all'utente di scegliere:
  - exec_symbol
  - exec_tf
  - exec_dataset_id
  - per ogni Pattern: pattern_symbol e pattern_source_family
- Non chiede un dataset Pattern a tf fisso.

2.3 API / BACKEND
- Riceve exec_dataset_id e Pattern Source Family esplicite.
- Valida la coerenza tra source family e tf richiesti dalle Conditions.
- Passa al runtime solo dataframe risolti esplicitamente dal DB.

2.4 RUNTIME / ENGINE
- Carica df_exec dal dataset di esecuzione selezionato.
- Risolve per ogni Pattern i dataset necessari alle sue Conditions.
- Non costruisce timeframe a partire da altri timeframe.

2.5 DB
- datasets = catalogo dataset disponibili.
- prices = serie prezzi legate a dataset_id.
- source family = attributi strutturali che raggruppano i dataset coerenti di un Pattern.
- contract_specs = specifiche contrattuali.

2.6 VALIDATOR / CORE
- B6 valida schema EA e coerenza numerica.
- B7 calcola metriche solo dopo validazione.

============================================================
3) CHECKLIST DI COERENZA
============================================================
- AI_DESCRIPTION / ai_description ovunque.
- Daily canonico D ovunque.
- Nessun alias timeframe.
- Nessun fallback dataset/timeframe.
- Nessuna metriche calcolate nell'EA.
- Variables e Constants separate.
- Pattern TF derivato, non scelto manualmente come dataset singolo.
- EXEC_TF usato come nome canonico del timeframe operativo.

============================================================
4) CHECKLIST IMPLEMENTATIVA
============================================================
B2
- Glossario canonico su Pattern, Condition, Source Family, EXEC_TF, MRE.

B3
- Contratto EA aggiornato a pattern_contexts e strategy_params separati in variables/constants.

B4
- Regole AI/dev aggiornate al nuovo modello.

B5
- Template ufficiale aggiornato a B3-V04.

B6
- Validator da riallineare alla nuova struttura di registry e params.

B7
- Lessico coerente con EXEC_TF e multi-market.

B8
- Master spec AI-ready allineata al nuovo modello.

B9
- Pacchetto operativo canonico da consegnare a una AI DEV esterna.

B10
- Esempi canonici worked-example per sviluppo umano e AI.

============================================================
FINE B1
============================================================

B2_core_overview_architecture.txt

Download
============================================================
ALGO - DOCUMENT HEADER
------------------------------------------------------------
Doc ID       : B2
Title        : CORE - OVERVIEW & ARCHITECTURE (Hard-law)
Filename     : B2 - CORE - OVERVIEW & ARCHITECTURE - V10.txt
Version      : V11
Updated      : 2026-04-25 16:35 (Europe/Rome)
Status       : ACTIVE (DOCUMENTO FONDAMENTALE / HARD-LAW)
Changelog    :
- V11: formalizzati `trade_id` / `trade_seq` come identita' macchina/umana del trade; introdotte le nozioni canoniche di Logic Occurrence, provenance e selected occurrence per audit/UI.
- V10: formalizzato che il runtime CORE deve calcolare e rendere esplicito il primo `EXEC close` disponibile e il `backtestable start` dell'overlap pattern/EXEC; l'assenza di EXEC nella parte iniziale del pattern range non va trattata come errore fatale.
- V09: chiarito che il nuovo strategy_code ufficiale e' assegnato da ALGO in modo sequenziale; strategy_version esiste solo nel contesto di una strategia gia' identificata.
- V08: formalizzata la distinzione tra draft tecnico EA e registrazione ufficiale in DB, che richiede conferma esplicita dell'utente.
- V07: formalizzato che UI/API/reports non devono ricalcolare metadata canonici gia' esposti dal PATTERN_REGISTRY.
- V07: formalizzata la retrocompatibilita' report-only per run storici con metadata Pattern legacy.
- V06: formalizzato l'obbligo di metadata macchina affidabili per UI/API/runtime sui requisiti Pattern.
- V06: formalizzato il comportamento fail-closed quando i metadata canonici non sono risolvibili.
- V05: formalizzata l'identita' del run con run_id macchina + run_seq umano persistito in DB.
- V05: formalizzato che UI/API devono leggere il run_seq dal DB e non calcolare ordinali locali.
- V04: chiarita la semantica canonica di start_dt/start_price vs cert_dt/cert_price.
- V04: formalizzate onset su EXEC_TF, persistence, no-lookahead e plotting sul fenomeno reale.
- V04: formalizzate le regole UI su precisione di plotting e resolved data wins.
- V03: ridefinito il modello canonico Pattern / Condition / Parameters / EXEC_TF / MRE.
- V03: formalizzato EXEC_TF come unico termine canonico per il timeframe operativo del run.
- V03: formalizzata la Pattern Source Family come unita' di sourcing dati.
- V03: formalizzato il supporto concettuale a EA multi-pattern e multi-market.
============================================================

B2 - CORE - OVERVIEW & ARCHITECTURE (Hard-law)
Versione: V11
Status  : ACTIVE (HARD-LAW)

============================================================
0) SCOPO DEL DOCUMENTO
============================================================
Questo documento definisce:
- il glossario canonico unico di ALGO
- l'architettura end-to-end
- il contratto concettuale fra UI, API, runtime, EA, validator, CORE e DB
- le regole di governance che impediscono divergenze future

Se c'e' conflitto fra documenti:
- B2 = visione architetturale, glossario e hard-law di sistema
- B3 = contratto canonico dell'EA
- B4 = regole operative per sviluppo umano/AI
- B5 = template tecnico ufficiale
- B6 = enforcement del contratto EA
- B7 = definizione numerica del CORE

============================================================
1) GLOSSARIO / CANONICAL TOKENS
============================================================
1.1 Principio guida
- In ALGO non esistono sinonimi, alias o mapping impliciti.
- Un token canonico deve essere identico in tutti i layer che lo usano.
- Se il DB usa un token, quello e' il token canonico di progetto.

1.2 Token timeframe canonici del DB
Token supportati alla data del documento:
- S1
- M1, M3, M5, M10, M15, M30
- H1, H2, H4, H6
- D
- W
- M
- Y

Regole:
- Daily canonico = D
- Vietati: D1, 1D, DAILY e qualunque altro alias
- I token timeframe devono comparire identici in tutti i layer che li usano

1.3 Dataset
- dataset_id = identita' primaria del dataset nel DB.
- timeframe = risoluzione temporale del dataset, non la sua identita'.
- Possono esistere piu' dataset con lo stesso timeframe.

1.4 Pattern
- Un Pattern e' un gruppo logico coerente di Conditions, Entries ed Exits.
- Un Pattern non e' una singola formula.
- Un Pattern non e' un singolo timeframe.
- Un Pattern e' sia unita' logica sia unita' di sourcing dati.
- Pattern diversi possono rappresentare blocchi logici separati della stessa operazione complessiva.
- Piu' Pattern possono concorrere insieme alla readiness di una singola entry condivisa.
- La suddivisione in piu' Pattern puo' essere usata per chiarezza semantica anche quando la logica sarebbe riducibile a un solo Pattern con piu' Conditions.

1.5 Condition
- Una Condition e' una regola booleana verificabile sul mercato.
- Ogni Condition appartiene esplicitamente a un solo Pattern.
- Ogni Condition dichiara sempre il proprio tf logico.
- Una Condition puo' avere start_dt osservato su EXEC_TF e cert_dt definito sul proprio tf logico.
- Non ogni verita' logica dell'EA deve diventare automaticamente un marker di chart.
- Le Conditions destinate a comparire in audit/charttrade devono essere progettate anche come eventi visivi, non solo come regole booleane interne.
- Per ogni Condition che deve essere rappresentata in charttrade, l'EA design deve esplicitare:
  - quale fenomeno reale del mercato rappresenta
  - quale punto reale del fenomeno corrisponde a start_dt/start_price
  - quale punto di conferma corrisponde a cert_dt/cert_price
  - se il marker principale di chart deve mostrare start oppure se l'evento deve rimanere solo testuale in audit
- Se una Condition non ha una semantica visiva chiara e utile all'audit, non deve essere emessa solo per produrre marker rumorosi o fuorvianti.

1.6 Parameters
- I Parameters sono i valori usati da Conditions, Entries ed Exits.
- I Parameters si dividono in:
  - Variables = modificabili dall'utente via UI
  - Constants = visibili all'utente ma non modificabili via UI

1.7 Pattern Source Family
- La Pattern Source Family e' la scelta dati fatta per un Pattern.
- Non coincide con un singolo dataset a timeframe fisso.
- Nel DB reale attuale e' descritta tramite attributi strutturali del dataset, ad esempio:
  - vendor
  - origin
  - session
  - timezone
  - boundary
  - shift
  - calendar
  - variant
- Il runtime puo' ricevere Pattern contexts gia' espliciti oppure una Source Family strutturale da risolvere.
- In entrambi i casi, il runtime deve arrivare a dataset espliciti per tutti i tf richiesti dalle Conditions del Pattern.

1.7.1 Dataset boundary semantics
- `boundary = TRADING` indica una griglia dati ancorata alla giornata operativa di borsa/sessione.
  Per CME futures ETH/NQ con `timezone = America/New_York` e `calendar = CME_NQ_GLOBEX`, la giornata operativa
  riparte alle `18:00 ET`.
- `boundary = CALENDAR` indica una griglia dati ancorata al giorno civile/calendario, quindi midnight/civil grid
  (`00:00`, `04:00`, `08:00`, ... per H4), non alla ripartenza della sessione futures.
- `shift` e' un offset sulla griglia dichiarata da `boundary`; non deve essere usato per mascherare una boundary
  semanticamente sbagliata. Se un H4 parte da `00:00/04:00/...`, non e' `TRADING shift=120`: e' un dataset
  `CALENDAR shift=0`, salvo definizione esplicita contraria.
- Per dataset session-based futures ricostruiti da ALGO e ancorati a `18:00 ET`, usare `boundary = TRADING` e
  `shift = 0`.

1.7.2 Dataset variant semantics
- `variant = RAW` indica un dataset vendor-native/originale, non ricostruito da ALGO.
- `variant = DENSE` indica un dataset derivato/ricostruito da ALGO con griglia completa del timeframe dichiarato.
  Per ogni bucket previsto dalla griglia di `boundary/session/calendar/shift`, se il mercato e' operativo ma nel
  parent non esistono trade/barre effettive per quel bucket, ALGO puo' materializzare una barra sintetica
  zero-volume con `open = high = low = close = previous_close`. Questo riempimento e' parte esplicita del dataset
  persistito, non un fallback o carry-forward implicito fatto da EA/UI.
- `variant = SPARSE` indica un dataset derivato/ricostruito da ALGO che preserva solo barre effettivamente presenti
  nel parent o nei dati/eventi sorgente. Non densifica tutti i bucket teorici della griglia e non crea barre
  zero-volume di riempimento per i vuoti di trading.
- `variant` non deve descrivere la boundary temporale (`TRADING`/`CALENDAR`) e non deve essere usato al posto di
  `origin`. Un dataset ALGO ricostruito da M1 per H4 session-aware deve usare `origin = DERIVED`, `boundary = TRADING`
  e un `variant` coerente con la politica di densificazione scelta.

1.8 Pattern TF
- Il Pattern TF e' il piu' piccolo timeframe fra tutte le Conditions del Pattern.
- E' una proprieta' derivata.
- Serve come etichetta sintetica e informativa.
- Non e' un input dati che l'utente deve scegliere.

1.9 EXEC_TF
- EXEC_TF e' il timeframe operativo scelto dall'utente in fase di backtest.
- Serve per entry, exit, fill e rilevazioni intrabar quando richieste dalla logica.
- EXEC_TF non sostituisce il tf logico delle Conditions.

1.10 MRE
- MRE = Minimum Required EXEC.
- E' il minimo livello di precisione operativa richiesto dalla logica dell'EA o del Pattern.
- L'utente puo' scegliere un EXEC_TF uguale o piu' fine di MRE.
- L'utente non puo' scegliere un EXEC_TF piu' grossolano di MRE.

1.11 AI_DESCRIPTION / ai_description
- AI_DESCRIPTION = nome concettuale / nome costante nel codice EA.
- ai_description = chiave top-level nell'output EA.
- Significato: spiegazione umana, semplice e leggibile della strategia.

1.12 PATTERN_REGISTRY
- Registro macchina di pattern, conditions, entries, exits, parameters e semantica eventi dichiarati dall'EA.
- Deve essere coerente con gli event_id emessi nei trades.
- Non e' testo libero: e' struttura dati canonica.
- Le informazioni che UI, API e runtime usano per capire quali Pattern siano attivi, quali tf siano richiesti, quale MRE si applichi e quali context/dataset servano devono essere disponibili in forma macchina affidabile e deterministica.
- Quando un metadata canonico e' gia' esposto in modo esplicito nel PATTERN_REGISTRY (ad esempio pattern_tf o mre), i consumer downstream non devono ricalcolarlo da strutture piu' deboli come Conditions, testo libero o naming convenzionale.
- Non e' ammesso dipendere solo da parsing fragile del file EA, da testo libero o da deduzioni implicite per abilitare/disabilitare campi canonici del backtest.

1.13 contract_specs
- Fonte DB delle specifiche contrattuali del simbolo.
- Campi rilevanti: tick_size, point_value, currency, exchange.
- Questi valori governano PNL, slippage, MAE/MFE currency-based.

1.14 strategy_code / strategy_version
- `strategy_code` ufficiale di un nuovo EA e' assegnato da ALGO in modo sequenziale.
- AI e sviluppatore non devono inventare, proporre o promuovere unilateralmente un nuovo `strategy_code` canonico.
- `strategy_version` ha senso solo nel contesto di una strategia gia' identificata da ALGO tramite `strategy_code`.
- Se si lavora su una nuova versione di una strategia esistente, il `strategy_code` deve essere gia' noto e confermato; solo allora si puo' parlare di `strategy_version`.
- Fino ad assegnazione ufficiale da ALGO, si possono usare solo nomi descrittivi provvisori non canonici nelle discussioni e nei draft.

1.14-bis draft tecnico EA vs registrazione ufficiale
- Un draft tecnico EA puo' essere implementato, validato e proposto senza essere ancora promosso a registrazione ufficiale.
- La registrazione ufficiale in DB di un nuovo EA richiede: assegnazione del `strategy_code` da parte di ALGO e conferma esplicita dell'utente su naming e testi ufficiali.
- L'aggiornamento ufficiale di Name, Description, AI Description o strategy_version di una strategia gia' esistente richiede conferma esplicita dell'utente.
- In assenza di conferma esplicita, il sistema puo' preparare artefatti tecnici e testi proposti, ma non deve considerarli identita' ufficiale gia' approvata.

1.15 start_dt / start_price
- start_dt e start_price rappresentano il primo onset valido del fenomeno osservato.
- start non coincide automaticamente con la barra di certificazione.
- Per eventi strutturali, start deve rappresentare il punto strutturale reale del fenomeno.

1.16 cert_dt / cert_price
- cert_dt e cert_price rappresentano la conferma canonica dell'evento.
- cert puo' appartenere a una barra successiva rispetto a start.
- start e cert devono avere semantica esplicita e non intercambiabile.

1.17 run_id / run_seq / trade_id / trade_seq
- run_id = identificatore macchina persistito del backtest run.
- run_seq = identificatore umano sequenziale persistito del backtest run.
- run_seq non e' un numero calcolato lato UI, non e' un ordinamento locale e non e' un alias derivato.
- Se il run e' persistito in DB, run_id e run_seq devono essere letti dal DB e riportati in modo coerente in tutte le pagine e API che mostrano il run.
- La UI puo' usare run_seq come label umana del run, ma non deve inventarlo, ricalcolarlo o sostituirlo con ordinali locali.
- trade_id = identificatore macchina persistito del trade dentro il run.
- trade_seq = identificatore umano sequenziale persistito del trade dentro il run.
- trade_seq non e' un numero calcolato lato UI, non e' un ordinamento locale e non e' un alias derivato da trade_id.
- Se il trade e' persistito in DB, trade_id e trade_seq devono essere letti dal DB e riportati in modo coerente in tutte le pagine e API che mostrano quel trade.
- La UI puo' usare trade_seq come label umana del trade, ma non deve inventarlo, ricalcolarlo o rinumerarlo localmente.

1.18 Logic Occurrence
- Una Logic Occurrence e' un'occorrenza concreta, identificabile e auditabile di una Condition, Pattern, Entry, Exit o Position State.
- L'entita' astratta (ad esempio `P1C1`) e la sua occorrenza concreta non sono la stessa cosa.
- entity_id = regola astratta definita nel PATTERN_REGISTRY.
- occurrence_id = caso concreto osservato sul mercato durante il run.
- Una Logic Occurrence puo' essere candidata, certificata, selezionata, superseded o invalidata.

1.19 Occurrence Dependency / Provenance
- Una Logic Occurrence puo' dipendere esplicitamente da una o piu' altre occurrences.
- La provenance deve essere machine-readable, non ricostruita per inferenza debole lato UI.
- Se una occorrenza usa un anchor precedente per confronto o conferma, il legame deve essere esplicito via occurrence_id.

1.20 Selected Occurrence / Used-by-trade
- Non ogni occorrenza visibile sul chart e' automaticamente quella usata dal trade.
- Se una occorrenza e' causalmente rilevante per il trade, il payload deve poter dichiarare in modo esplicito se e' stata usata dal trade e da quale trade_seq / trade_id.
- Lo scopo e' permettere audit visivo immediato di "cosa e' successo, dove, quando e con quale riferimento".

============================================================
2) COS'E' ALGO (E COSA NON E')
============================================================
ALGO e' una piattaforma che:
- esegue backtest tramite EA che producono trades chiusi + events
- valida l'output EA
- calcola metriche, equity, drawdown e analytics nel CORE
- presenta risultati in UI e salva dati in DB in modo coerente

ALGO NON e':
- un posto dove l'EA calcola analytics o metriche CORE
- un sistema che prova a far funzionare contratti incompleti
- un framework che ammette output quasi giusti o naming inventati

============================================================
3) PRINCIPI NON NEGOZIABILI (HARD-LAW)
============================================================
P-01 Separazione dei ruoli
- EA = descrive cosa e' successo.
- CORE = calcola cosa significa.

P-02 DB wins
- Il DB e' la source of truth per timeframe reali, dataset reali, source family e contract specs.
- Il DB e' anche la source of truth per l'identita' persistita del run (`run_id`, `run_seq`).

P-03 Output contract strict
- L'output EA deve rispettare B3.
- Zero extra keys non approvate.

P-04 Zero fallback / zero alias / zero normalizzazioni
- Nessuna ricostruzione implicita di timeframe.
- Nessuna conversione implicita di token.
- Nessun dataset sostitutivo mostrato in preview.

P-05 Determinismo
- Stessi input -> stessi output.

P-06 No lookahead
- L'EA usa solo dati disponibili fino al timestamp logico del segnale.

P-07 Trades sempre chiusi
- Nessuna posizione aperta nell'output EA.

P-08 Timezone globale
- Input market data: dt = BAR START in America/New_York.
- Output EA: UTC ISO-8601 con Z.
- CORE: opera in UTC.
- UI/API che mostrano market data o trade persistiti non devono convertire gli orari verso timezone locali del browser o dell'utente: devono mostrare il timestamp DB cosi' come rappresenta la semantica canonica del dataset/oggetto persistito.
- Per dataset futures session-based `D/W/M` con vendor rows date-only, `prices.dt` non deve essere una fake civil-midnight label: deve rappresentare il vero bar start operativo della barra higher-TF secondo la semantica di sessione/exchange adottata.

P-09 Pattern-level sourcing
- La scelta dati dei Pattern avviene a livello di Pattern Source Family, non a livello di singola Condition.
- Tutte le Conditions di un Pattern devono usare la stessa Pattern Source Family.

P-10 Multi-market consentito
- Uno stesso EA puo' contenere Pattern su symbol diversi.
- La sincronizzazione fra Pattern avviene tramite dt.

P-11 Onset reale prima della certificazione
- Un evento puo' iniziare su EXEC_TF e certificare sul proprio tf logico.
- Il sistema deve distinguere sempre onset osservato e conferma canonica.

P-12 Persistence esplicita
- Se la logica di una Condition richiede persistence fino alla certificazione, l'EA deve verificarla esplicitamente.
- Un onset intrabar non persistente non puo' essere promosso implicitamente a Condition certificata.

P-13 Metadata canonici machine-readable
- I requisiti canonici di un EA che governano UI/API/runtime devono essere determinabili in forma macchina affidabile.
- Questo include almeno: Pattern in uso, pattern_tf, MRE, source family richiesta e necessita' di pattern_contexts/dataset espliciti.
- Un EA non e' considerato UI-runnable se questi metadata non sono risolvibili in modo deterministico.
- Se il metadata canonico e' gia' persistito nel run o dichiarato nel PATTERN_REGISTRY, UI/API/reports devono leggerlo da quella fonte esplicita e non ricalcolarlo localmente.

P-14 Fail-closed sui metadata irrisolti
- Se UI o API non riescono a determinare in modo affidabile un requisito canonico del run, il sistema deve fallire in modo esplicito.
- E' vietato mostrare "NOT IN USE", abilitare submit o costruire payload incompleti quando il Pattern esiste ma i suoi metadata non sono stati risolti correttamente.
- Meglio un errore esplicito di metadata non risolti che una UI apparentemente valida ma semanticamente falsa.

============================================================
4) CONTRATTO DI BACKTEST (UI -> API -> RUNTIME)
============================================================
Un backtest ALGO e' definito almeno da:
- exec_symbol
- exec_tf
- exec_dataset_id
- period
- params strategici
- pattern_contexts

Ogni pattern_context contiene almeno:
- pattern_id
- pattern_symbol
- e uno fra:
  - datasets_by_tf
  - source_family

Regole:
- La UI/API non devono usare campi top-level legacy come pattern_timeframe o pattern_dataset_id.
- pattern_contexts e' il contratto canonico del backtest per i contesti Pattern.
- datasets_by_tf e' una dichiarazione esplicita di dataset per tf all'interno di un Pattern context.
- source_family e' una dichiarazione strutturale che il runtime puo' usare per risolvere i dataset richiesti dalle Conditions del Pattern.
- Il runtime deve risolvere automaticamente i dataset richiesti dalle Conditions del Pattern quando il context non li dichiara gia' tutti in modo esplicito.
- Se manca anche uno solo dei dataset richiesti da un Pattern, il backtest deve fallire con errore esplicito.
- La UI puo' abilitare o disabilitare i controlli del backtest solo sulla base di metadata canonici machine-readable affidabili.
- Se i metadata necessari a determinare un pattern_context non sono risolti, la UI deve bloccare il submit e mostrare un errore esplicito di metadata canonici non risolti.

============================================================
5) ARCHITETTURA END-TO-END
============================================================
5.1 Data Layer
- Espone datasets, prices, source families e contract_specs.
- prices e' sempre legato a dataset_id.
- Il DB deve permettere di risalire dalla Pattern Source Family ai dataset necessari per i tf richiesti.

5.2 UI Backtest
- Mostra algoritmi, EXEC_TF disponibili e contesti Pattern coerenti con il contratto `pattern_contexts`.
- Non usa campi legacy top-level per i Pattern.
- Deve mostrare errori chiari se il contesto Pattern non copre tutti i tf richiesti dalle Conditions.
- Quando mostra un run salvato, deve usare il `run_seq` persistito in DB come label umana coerente cross-page.

5.3 API / Backend
- Riceve exec_tf, exec_dataset_id e pattern_contexts.
- Valida coerenza symbol/dataset/source_family dei Pattern contexts.
- Risolve i dataset effettivi richiesti dalle Conditions.
- Non corregge silenziosamente payload incoerenti.
- Deve persistere e restituire `run_id` e `run_seq` come identita' ufficiali del run.

5.4 Runtime / Engine
- Carica df_exec dal dataset operativo selezionato.
- Per ogni Pattern risolve i dataframe necessari alle sue Conditions partendo dalla Pattern Source Family.
- Deve calcolare in modo esplicito il primo `EXEC close` disponibile e il `backtestable start` coerente con l'overlap fra dataset logici Pattern ed EXEC.
- Deve rendere questo overlap disponibile al runtime/EA e al result payload come metadata di audit, cosi' il sistema non resta cieco sui tratti non ancora eseguibili.
- Se il pattern range inizia prima del primo `EXEC close` disponibile, quel tratto iniziale e' bootstrap/range truncation, non un errore fatale del backtest.
- Invoca l'EA con i contesti Pattern gia' risolti.
- Non effettua fallback, alias o ricostruzioni implicite.

5.5 EA Layer
- Usa df_exec e i contesti Pattern passati dal runtime.
- Produce output B3-compatible.
- Espone PATTERN_REGISTRY e ai_description.
- Non calcola metriche CORE.

5.6 Validation Layer
- Valida schema, coerenza numerica, registry, eventi, datetime e token timeframe.

5.7 CORE / Analytics
- Usa solo trades validati.
- Calcola holding time, equity, returns, metrics, drawdown.

5.8 UI / Audit Visualization
- Il chart deve plottare il fenomeno nella sua posizione reale di start, non su una barra tecnica arbitraria di conferma.
- Le viste testuali di audit possono mostrare sia start sia cert.
- Se il run e' risolto da dati persistiti, resolved data wins.
- La UI non deve reintrodurre fallback a sorgenti piu' deboli quando il dato risolto e' disponibile.
- La UI non deve calcolare, inferire o rinumerare localmente il run quando `run_seq` persistito e' disponibile.
- La UI non deve calcolare, inferire o rinumerare localmente il trade quando `trade_seq` persistito e' disponibile.
- Reports/UI/API non devono ricalcolare pattern_tf o altri metadata Pattern se il run persistito li espone gia' nel PATTERN_REGISTRY; per run storici legacy e' ammessa solo una retrocompatibilita' report-only esplicita, senza promuovere i campi legacy a nuovo contratto canonico.
- Se il timeframe corrente della chart e' piu' fine di EXEC_TF, la UI non deve fingere una precisione di fill superiore a quella reale del run.
- La qualita' della visualizzazione dipende da una progettazione esplicita degli eventi gia' in fase di definizione dell'EA.
- Prima dell'implementazione di un nuovo EA, ideatore e sviluppatore devono concordare quali Conditions / Patterns / Entries / Exits siano davvero utili da rappresentare in charttrade.
- Sono vietati marker di chart aggiunti senza una semantica auditabile chiara oppure ancorati a punti che non rappresentano il fenomeno reale osservato.
- Se il payload espone Logic Occurrences, la UI deve preferire quelle per spiegare nessi causali, anchor selezionati e occorrenze usate dal trade; non deve ricostruire silenziosamente questi legami da soli marker o da naming convenzionale.
- Un Logic Indicator orientato all'audit utente non deve limitarsi a stati booleani quando la lettura causale richiede di distinguere occorrenze diverse della stessa entita' astratta.

============================================================
6) ERROR SEMANTICS
============================================================
Il sistema deve fermarsi con errore esplicito quando:
- exec_dataset_id e' mancante o incoerente
- pattern_contexts e' mancante o incoerente quando richiesto dall'EA
- manca uno dei dataset richiesti dalle Conditions di un Pattern
- l'EA usa token timeframe non canonici
- l'output EA viola B3/B6

Sono vietate le correzioni silenziose.

============================================================
7) GOVERNANCE DOCUMENTALE
============================================================
- B2 contiene il glossario canonico e i principi di sistema.
- B3 contiene la spec EA canonica.
- B4 contiene le regole per scrivere/correggere EA.
- B5 e' il template tecnico ufficiale.
- B6 e' il validator ufficiale.
- B7 e' la spec numerica del CORE.

Regole:
- Ogni modifica di naming o contratto richiede aggiornamento coerente di B2/B3/B4/B5/B6/B7/B8.
- Se cambia il modello di sourcing Pattern o EXEC_TF, il primo documento da aggiornare e' B2.

============================================================
FINE B2
============================================================

B3_ea_output_spec.txt

Download
============================================================
ALGO - DOCUMENT HEADER
------------------------------------------------------------
Doc ID       : B3
Title        : EA - OUTPUT SPEC (CANONICAL, ALL REQUIRED)
Filename     : B3 - EA - OUTPUT SPEC (CANONICAL, ALL REQUIRED) - V09.txt
Version      : V18
Updated      : 2026-04-25 16:35 (Europe/Rome)
Status       : SINGLE SOURCE OF TRUTH (EA CONTRACT)
Changelog    :
- V18: introdotta l'estensione canonica opzionale `logic_occurrences` per audit occurrence-based, provenance esplicita e collegamento tra occurrences e trade; chiarita la non inferibilita' locale delle occorrenze selezionate.
- V17: introdotta l'estensione canonica `logic_state` per esporre serie booleane state-based bar-per-bar di Conditions, Pattern e Position State; chiarito che i consumer che richiedono state-based logic non devono inferirla implicitamente da eventi sparsi.
- V16: chiarito esplicitamente che `mre` e' il minimo EXEC ammesso del Pattern e puo' quindi essere uguale o piu' fine del `pattern_tf`; vietato interpretarlo come vincolo a restare sullo stesso tf logico del Pattern.
- V15: formalizzato che un evento logico puo' opzionalmente esporre in `meta` un `runtime_anchor_dt` operativo distinto dal proprio `cert_dt` logico; per dataset BAR_START puo' opzionalmente esporre anche `logical_bar_start_dt` per evitare ambiguita' di lookup sul close anchor.
- V14: reso hard-law che il mapping logical->EXEC non puo' usare exact-match su logical close anchor; il criterio canonico e' "latest EXEC close anchor <= logical close anchor" e il CORE puo' rifiutare script non conformi.
- V13: formalizzato che se il logical close anchor precede il primo close disponibile del dataset EXEC caricato nel run, il segnale va trattato come bootstrap/range truncation e saltato, non come errore fatale.
- V12: chiarito esplicitamente che per dataset etichettati BAR_START il campo `dt` grezzo non puo' essere usato come logical close anchor; `cert_dt` e mapping logico -> EXEC devono usare il close anchor canonico della barra logica.
- V11: formalizzato che una condizione o conferma su sottostante esterno deve usare un proprio pattern_context/pattern_symbol separato; vietato comprimere dati multi-underlying in colonne extra del dataframe del simbolo tradato.
- V10: formalizzato che PATTERN_REGISTRY deve essere staticamente ispezionabile dai consumer ALGO; ammessi letterali puri e riferimenti a costanti top-level staticamente risolvibili, vietata costruzione dinamica.
- V09: formalizzato che il mapping barra logica -> EXEC deve usare l'anchor temporale canonico della barra logica e non la sola coincidenza di data calendario; chiarita la gestione dei daily/session-based bars etichettati su close di sessione e giorni non-trading.
- V08: formalizzata la struttura obbligatoria dettagliata di ai_description, incluse tabelle Markdown per Variables e Constants e sezione ChartTrade Semantics.
- V07: chiarito che per eventi/condizioni certificati al close della barra logica, cert_dt deve rappresentare il close logico e non lo start della barra.
- V06: formalizzato che i consumer devono leggere pattern_tf dal PATTERN_REGISTRY invece di ricalcolarlo quando gia' presente.
- V06: formalizzata la retrocompatibilita' report-only sui campi legacy timeframes / meta.pattern_timeframe per run storici.
- V05: chiarita la semantica obbligatoria di start vs cert negli events.
- V05: formalizzate onset su EXEC, persistence, no-lookahead e plotting sul fenomeno reale.
- V04: introdotto il modello Pattern Source Family / Pattern TF derivato / EXEC_TF / MRE.
- V04: distinta la semantica di Variables e Constants dentro i Parameters.
- V04: aggiornata la firma concettuale dell'EA con pattern_contexts invece di pattern_dfs flat.
- V04: esplicitata la semantica start_dt vs cert_dt per le Conditions.
============================================================

B3 - EA - OUTPUT SPEC (CANONICAL, ALL REQUIRED)
Versione: V18
Status  : SINGLE SOURCE OF TRUTH (EA CONTRACT)

============================================================
0) SCOPO
============================================================
Questo documento definisce il contratto canonico dell'EA in ALGO:
- input contract
- naming top-level
- PATTERN_REGISTRY
- semantica di Pattern, Conditions, Parameters, EXEC_TF e MRE
- schema trade/event
- regole su datetime e output hygiene

============================================================
1) HARD-LAW
============================================================
- Zero optional sul contratto pubblico salvo ove esplicitato.
- Zero extra keys non approvate.
- Zero fallback.
- Zero alias.
- Zero normalizzazioni.
- Token timeframe identici a quelli del DB.
- ai_description sempre presente.
- pattern_registry sempre presente.
- trades sempre chiusi.
- datetime output sempre UTC ISO-8601 con Z.

============================================================
2) INPUT CONTRACT CANONICO DELL'EA
============================================================
Firma canonica logica:
run_backtest(symbol, timeframe, period, params, df_exec, pattern_contexts)

Semantica:
- symbol: simbolo operativo del run
- timeframe: EXEC_TF del run
- period: periodo logico del run
- params: parametri strategici pubblici
- df_exec: dataframe del dataset operativo di esecuzione
- pattern_contexts: dict che mappa pattern_id -> contesto dati del Pattern

Ogni pattern_context deve contenere almeno:
- pattern_id
- pattern_symbol
- pattern_tf
- source_family
- dfs_by_tf

Regole:
- pattern_tf e' derivato dal Pattern Registry ed e' il piu' piccolo tf fra le Conditions del Pattern.
- dfs_by_tf deve contenere tutti i dataframe richiesti dalle Conditions del Pattern.
- Il runtime passa dataframe gia' risolti dal DB; l'EA non deduce dataset_id.
- L'EA non costruisce timeframe a partire da df_exec.
- Se una parte della logica usa un sottostante esterno o un simbolo diverso dal simbolo tradato del run, quel blocco deve vivere in un pattern_context separato con proprio pattern_id e pattern_symbol.
- Non e' consentito comprimere dati multi-underlying nel dataframe del simbolo tradato tramite colonne synthetic extra tipo `vix_close`.

============================================================
3) TIMEFRAME TOKENS CANONICI
============================================================
Token ammessi alla data di questa spec:
- S1
- M1, M3, M5, M10, M15, M30
- H1, H2, H4, H6
- D
- W
- M
- Y
- EXEC

Regole:
- Daily canonico = D
- EXEC e' ammesso solo come tf logico per events/registry di entry ed exit o rilevazioni intrabar.
- EXEC non e' un token timeframe DB e non puo' essere usato come timeframe di dataset.

============================================================
4) TOP-LEVEL OUTPUT SCHEMA (ALL REQUIRED)
============================================================
Chiavi top-level obbligatorie:
- contract_id
- contract_version
- generated_at_utc
- symbol
- timeframe
- period
- strategy_name
- strategy_version
- strategy_code
- strategy_type
- strategy_params
- pattern_registry
- ai_description
- trades
- build
- meta

Chiavi top-level opzionali approvate:
- logic_state
- logic_occurrences

Valori canonici:
- contract_id      = "ALGO_EA_OUTPUT"
- contract_version = "B3-V04"
- strategy_type    = "EA"
- timeframe        = EXEC_TF del run

Nota importante:
- La versione di questo documento e la `contract_version` del payload non sono la stessa cosa.
- Questo documento puo' evolvere per chiarimenti, governance, retrocompatibilita' e semantica operativa senza cambiare necessariamente il payload contract.
- Fino a modifica esplicita dello schema payload e del validator, `contract_version` resta `B3-V04`.

============================================================
5) STRATEGY_PARAMS
============================================================
strategy_params deve contenere almeno:
- variables
- constants

variables deve contenere almeno:
- initial_capital
- position_size
- commission_per_trade
- slippage_ticks

constants deve contenere almeno:
- tick_size
- point_value

Regole:
- Variables = parametri modificabili via UI.
- Constants = parametri visibili ma non modificabili via UI.
- strategy_params non deve contenere chiavi runtime interne.
- Il DB continua a governare i contract specs reali quando previsti dalle policy di runtime.

============================================================
6) PATTERN_REGISTRY
============================================================
pattern_registry e' obbligatorio e contiene:
- registry_version
- parameters
- patterns
- meta

parameters contiene:
- variables
- constants

Ogni pattern contiene almeno:
- pattern_id
- pattern_symbol
- title
- summary
- pattern_tf
- mre
- conditions
- entries
- exits
- completion_rule

Ogni condition contiene almeno:
- condition_id
- description
- easy_language
- tf
- certification_rule

Ogni entry contiene almeno:
- entry_id
- description
- easy_language
- tf
- fill_policy

Ogni exit contiene almeno:
- exit_id
- description
- easy_language
- tf
- fill_policy

Regole:
- pattern_tf e' il piu' piccolo tf fra le Conditions del Pattern.
- mre e' il Minimum Required EXEC del Pattern.
- `mre` puo' essere uguale o piu' fine del `pattern_tf`.
- Un Pattern con `pattern_tf = D` puo' dichiarare, ad esempio, `mre = D`, `H1` o `M1` se la logica operativa richiede un EXEC piu' preciso.
- `mre` non puo' essere piu' grossolano del `pattern_tf`.
- Gli event_id emessi nei trades devono esistere nel pattern_registry.
- Tutti i tf non-EXEC devono usare solo token timeframe canonici del DB.
- `pattern_registry` deve essere machine-readable e staticamente ispezionabile dai consumer ALGO.
- Sono ammessi:
  - dict/list/tuple/constant literal puri
  - riferimenti a costanti top-level staticamente risolvibili, per esempio `PATTERN_TF = "D"` e poi `"pattern_tf": PATTERN_TF`
- Non sono ammessi:
  - helper function calls
  - dict merge dinamici
  - string formatting runtime
  - imported symbols non staticamente risolti
  - costruzione del registry dipendente dal runtime
- I consumer downstream (UI/API/reports/audit) devono leggere pattern_tf dal campo esplicito del PATTERN_REGISTRY quando presente; non devono ricalcolarlo dalle Conditions se il metadata canonico e' gia' disponibile.
- In retrocompatibilita' report-only, run storici possono ancora esporre timeframes o meta.pattern_timeframe; questi campi legacy non sostituiscono il contratto canonico ma possono essere letti solo per visualizzare correttamente dati gia' persistiti.
- In fase di design dell'EA, il pattern_registry deve riflettere solo eventi con semantica auditabile chiara.
- Le Conditions pensate per comparire in charttrade devono essere definite anche in termini di utilita' visiva: fenomeno osservato, punto reale di start e punto canonico di conferma.
- Non devono essere emesse Conditions solo per "riempire" il chart con marker se quelle Conditions non aggiungono chiarezza auditiva al trade.

============================================================
7) AI_DESCRIPTION
============================================================
- ai_description deve esistere sempre.
- Deve essere testo umano strutturato, dettagliato e auditabile.
- Non puo' essere una descrizione libera troppo breve o vaga.
- Deve includere obbligatoriamente queste sezioni, nello stesso campo `ai_description`:
  - `Strategy Overview`
  - `Pattern Logic`
  - `Detailed Condition Semantics`
  - `Variables`
  - `Constants`
  - `ChartTrade Semantics`
  - `Operational Notes`
- La sezione `Variables` deve contenere una tabella Markdown con colonne:
  - `Variable`
  - `Meaning`
  - `Effect on strategy`
- La sezione `Constants` deve contenere una tabella Markdown con colonne:
  - `Constant`
  - `Meaning`
  - `Effect on execution / PnL`
- La sezione `Pattern Logic` deve spiegare almeno:
  - Pattern
  - Conditions
  - Entry
  - Exit
  - tf logici
  - `pattern_tf`
  - `MRE`
- La sezione `Detailed Condition Semantics` deve spiegare ogni Condition presente nel PATTERN_REGISTRY con una sottosezione identificabile dal suo `condition_id` canonico, per esempio `P1C1`.
- Per ogni Condition deve dichiarare almeno:
  - formula o regola operativa in linguaggio auditabile
  - parametri/default coinvolti
  - regola esatta di pass/fail, inclusa la strictness degli operatori (`>`, `>=`, `<`, `<=`)
  - significato di mercato della Condition
  - ruolo della Condition dentro il Pattern
  - warm-up/data availability quando rilevante
  - onset/start e certificazione quando rilevanti per charttrade o audit
- La sezione `ChartTrade Semantics` deve spiegare almeno:
  - quali eventi sono visibili
  - quali eventi non sono visibili
  - perche' ogni evento visibile merita di comparire
  - dove viene ancorato sul chart
  - come leggere `start_dt/start_price` vs `cert_dt/cert_price`
  - eventuale mapping tra barra logica e barra `EXEC`
- La sezione `Operational Notes` deve spiegare almeno:
  - single-position o multi-position
  - force-exit se presente
  - eventuali assunzioni runtime rilevanti

============================================================
8) TRADE SCHEMA (ALL REQUIRED)
============================================================
Ogni trade deve contenere:
- id
- side
- qty
- entry_dt
- entry_price
- entry_bar
- exit_dt
- exit_price
- exit_bar
- stop_price
- mae_ticks
- mfe_ticks
- mae_points
- mfe_points
- mae_ccy_1c
- mfe_ccy_1c
- pnl_gross
- commission
- slippage
- pnl_net
- events
- meta

Regole:
- stop_price puo' essere null.
- events deve essere lista non vuota.
- meta deve esistere sempre.

============================================================
9) EVENT SCHEMA (ALL REQUIRED)
============================================================
Ogni event deve contenere:
- event_id
- event_type
- tf
- start_dt
- start_price
- cert_tf
- cert_dt
- cert_price
- meta

Event type ammessi:
- CONDITION
- PATTERN
- ENTRY
- EXIT

Regole:
- Una Condition puo' avere start_dt su EXEC e cert_dt sul suo tf logico.
- ENTRY ed EXIT usano canonically tf = EXEC.
- PATTERN usa il tf della condition finale che ne certifica il completamento.
- trade.events deve includere almeno CONDITION, PATTERN, ENTRY, EXIT.
- start_dt/start_price rappresentano sempre il primo onset valido del fenomeno.
- cert_dt/cert_price rappresentano sempre la conferma canonica dell'evento.
- start e cert non sono intercambiabili e non devono essere collassati in un solo timestamp/prezzo.
- Se un evento o una Condition certifica al close della barra logica e non esiste un onset precedente distinto, sia start_dt sia cert_dt possono coincidere con il close logico della barra; usare lo start della barra come cert_dt in quel caso e' semanticamente scorretto.
- Se una Condition viene osservata intrabar su EXEC ma richiede persistence fino alla close del proprio tf logico, l'EA deve scartare l'onset non persistente.
- L'EA non puo' usare lookahead per anticipare certificazioni, entry o sopravvivenza futura di un setup.
- Gli eventi destinati alla rappresentazione in charttrade devono essere scelti intenzionalmente durante la progettazione dell'EA.
- Se un evento non ha una collocazione visiva chiara sul fenomeno reale, e' preferibile non emetterlo come marker chart-oriented.

============================================================
10) DATETIME / TIMEZONE
============================================================
Input data:
- df_exec.dt e pattern_contexts[*].dfs_by_tf[*].dt sono BAR START in America/New_York.
- Regola canonica aggiuntiva:
  alcuni dataset logici di Pattern, soprattutto daily/session-based, possono essere etichettati dal DB con il timestamp canonico del close logico della sessione invece che con un puro bar-start calendariale.
- In questi casi l'EA non deve reinterpretare quel timestamp usando sola aritmetica di calendario o semplice uguaglianza di `date`.

Output data:
- generated_at_utc, entry_dt, exit_dt, start_dt, cert_dt sono UTC ISO-8601 con Z.

============================================================
11) COSTI / PNL
============================================================
Per ogni trade:
- commission = variables.commission_per_trade * qty
- slippage   = variables.slippage_ticks * constants.tick_size * constants.point_value * qty
- pnl_net    = pnl_gross - commission - slippage

============================================================
12) MAE / MFE (CANONICO)
============================================================
Campi obbligatori:
- mae_ticks
- mfe_ticks
- mae_points
- mfe_points
- mae_ccy_1c
- mfe_ccy_1c

Identita' obbligatorie:
- mae_points  = mae_ticks * tick_size
- mfe_points  = mfe_ticks * tick_size
- mae_ccy_1c  = mae_points * point_value
- mfe_ccy_1c  = mfe_points * point_value

============================================================
13) BUILD / META
============================================================
build e' obbligatorio e deve contenere almeno:
- algo_version
- runtime_version
- ea_template_version
- validator_version
- git_commit
- python_version
- build_machine
- dataset_timezone
- output_timezone
- dt_convention

meta e' obbligatorio.
- Serve solo per metadata ammessi dal contratto.
- Non deve diventare un contenitore di chiavi arbitrarie fuori controllo.
- Per gli eventi logici (`CONDITION` / `PATTERN`) e' ammesso, quando necessario, esporre in `event.meta`:
  - `runtime_anchor_dt`
  - `runtime_anchor_price`
  - `logical_bar_start_dt`
- Questi campi servono solo a chiarire:
  - l'anchor runtime operativo distinto dalla certificazione logica
  - il bar-start reale del dataset logico quando `cert_dt` usa il close anchor canonico e il dataset e' etichettato `BAR_START`
- Questi campi non sostituiscono `start_dt/start_price` o `cert_dt/cert_price`; li completano.

============================================================
13-bis) LOGIC_STATE (OPTIONAL EXTENSION, STATE-BASED LOGIC)
============================================================
`logic_state` e' l'estensione canonica usata per esporre stati booleani bar-per-bar di:
- Conditions
- Pattern
- Position State

Scopo:
- fornire ai consumer ALGO una rappresentazione state-based esplicita e auditabile
- evitare inferenze implicite o ricostruzioni silenziose a partire da eventi sparsi
- permettere al Logic Indicator di leggere uno stato booleano reale sul tf sorgente

Se presente, `logic_state` deve essere un dict con:
- schema_version
- series
- meta

Valori canonici:
- `logic_state.schema_version = "LS-V01"`

Ogni elemento di `logic_state.series` deve contenere:
- state_id
- state_type
- state_tf
- visual_preset
- points
- meta

`state_type` ammessi:
- CONDITION
- PATTERN
- POSITION

Semantica:
- `state_id` identifica l'entita' logica.
  - per `CONDITION` deve corrispondere a un `condition_id` del `pattern_registry`
  - per `PATTERN` deve corrispondere a un `pattern_id` del `pattern_registry`
  - per `POSITION` identifica uno stato posizione esplicito, ad esempio `POS`
- `state_tf` e' il tf sorgente reale dello stato
  - usa token timeframe canonici
  - puo' essere `EXEC` per stati di posizione o altri stati che vivono sul tf operativo
- `visual_preset` dichiara la semantica visuale canonica dello stato
- `points` e' la serie booleana esplicita sul tf sorgente

Ogni punto di `points` deve contenere:
- dt
- value

Regole:
- `dt` deve essere UTC ISO-8601 con Z.
- `dt` rappresenta il BAR_START canonico della barra del `state_tf` a cui il boolean si riferisce.
- `value` deve essere boolean puro (`true` / `false`).
- La serie deve essere ordinata cronologicamente in modo strettamente crescente.
- Non e' ammesso carry-forward implicito.
- Non e' ammessa semantica "sottintesa": se una barra del range esportato deve risultare falsa, il punto deve esistere con `value = false`.
- Il consumer che richiede state-based logic non deve ricostruirla implicitamente da soli eventi runtime se `logic_state` manca.
- `logic_state` non sostituisce `events`: lo integra.
- `logic_state` non deve contenere metriche, alias o stati derivati non auditabili.
- Lo stato posizione canonico (`POSITION`) deve rappresentare la realta' operativa della posizione sul tf sorgente reale, tipicamente `EXEC`.

============================================================
13-ter) LOGIC_OCCURRENCES (OPTIONAL EXTENSION, OCCURRENCE-BASED AUDIT)
============================================================
`logic_occurrences` e' l'estensione canonica usata per esporre occorrenze discrete e relazioni causali di:
- Conditions
- Pattern
- Entry
- Exit
- Position State

Scopo:
- fornire ai consumer ALGO una rappresentazione occurrence-based esplicita e auditabile
- distinguere l'entita' astratta dalla sua occorrenza concreta sul mercato
- permettere al Logic Indicator e alle viste audit di capire quale anchor / candidate / confirmation sia stata davvero usata
- evitare inferenze silenziose a partire da soli marker prezzo, naming convenzionale o eventi sparsi

Se presente, `logic_occurrences` deve essere un dict con:
- schema_version
- items
- meta

Valori canonici:
- `logic_occurrences.schema_version = "LO-V01"`

Ogni elemento di `logic_occurrences.items` deve contenere:
- occurrence_id
- entity_id
- entity_type
- logic_label_short
- logic_label_long
- semantic_role
- status
- occurrence_tf
- start_dt
- start_price
- cert_dt
- cert_price
- depends_on_occurrence_ids
- used_by_trade_ids
- meta

`entity_type` ammessi:
- CONDITION
- PATTERN
- ENTRY
- EXIT
- POSITION

`semantic_role` ammessi:
- anchor
- candidate
- confirmation
- filter
- gate
- pattern_complete
- entry_trigger
- exit_trigger
- position_state
- invalidation
- replacement

`status` ammessi:
- candidate
- surviving_candidate
- certified
- selected
- superseded
- invalidated

Semantica:
- `occurrence_id` identifica un caso concreto osservato nel run e deve essere univoco nel payload.
- `entity_id` identifica la regola astratta:
  - per `CONDITION` deve corrispondere a un `condition_id` del `pattern_registry`
  - per `PATTERN` deve corrispondere a un `pattern_id` del `pattern_registry`
  - per `ENTRY` deve corrispondere a un `entry_id` del `pattern_registry`
  - per `EXIT` deve corrispondere a un `exit_id` del `pattern_registry`
  - per `POSITION` identifica uno stato posizione esplicito, ad esempio `POS`
- `logic_label_short` e `logic_label_long` servono alla leggibilita' utente e non sostituiscono `entity_id`.
- `occurrence_tf` e' il tf sorgente reale dell'occorrenza e usa token timeframe canonici.
- `start_dt/start_price` e `cert_dt/cert_price` mantengono la stessa semantica canonica degli `events`.
- `depends_on_occurrence_ids` esplicita la provenance dell'occorrenza:
  - deve essere lista, anche vuota
  - i riferimenti devono puntare a `occurrence_id` esistenti nello stesso blocco
- `used_by_trade_ids` esplicita quali trade usano causalmente quella occorrenza:
  - deve essere lista, anche vuota
  - ogni id deve corrispondere a un `trade.id` del payload
- `meta` puo' contenere dettagli aggiuntivi auditabili, per esempio:
  - `trade_seqs`
  - `supersedes_occurrence_id`
  - `invalidates_occurrence_id`
  - `runtime_anchor_dt`
  - `runtime_anchor_price`

Relazione con `logic_state`:
- `logic_state` descrive stati booleani densi bar-per-bar.
- `logic_occurrences` descrive occorrenze discrete, selezioni causali e relazioni tra occorrenze.
- I due blocchi sono complementari e non si sostituiscono a vicenda.
- Quando il Logic Indicator deve spiegare quale occorrenza concreta e' stata usata dal trade, i consumer devono preferire `logic_occurrences` a ricostruzioni implicite.

============================================================
14) VISUALIZATION / AUDIT SEMANTICS
============================================================
Regole canoniche:
- Il marker principale di chart per un event deve rappresentare start_dt/start_price.
- cert_dt/cert_price appartengono alle viste testuali di audit o a pannelli secondari.
- Per eventi strutturali, il marker deve stare sul punto strutturale reale del fenomeno, non sulla successiva barra di conferma.
- Se il run e' visualizzato su un timeframe piu' fine di EXEC_TF, la UI non deve inventare precisione di plotting superiore a quella realmente disponibile.
- La selezione degli eventi da rappresentare in charttrade deve essere decisa in fase di creazione dell'EA, non lasciata implicita.
- Per ogni Condition / Pattern / Entry / Exit che si vuole vedere in charttrade, il design dell'EA deve stabilire ex ante:
  - perche' quell'evento e' utile all'audit umano
  - quale punto reale del fenomeno deve essere plottato
  - se la conferma deve rimanere solo nella vista testuale
- Se un Pattern vive su un tf logico piu' grosso di EXEC, il mapping verso EXEC deve usare l'anchor temporale canonico della barra logica (`cert_dt` o altro anchor dichiarato), non la sola coincidenza di data calendario.
- Per dataset daily/session-based, l'EA non deve assumere che la barra logica daily cada sempre su un giorno trading di calendario.
- E' ammesso che una barra daily logicamente valida sia etichettata con un timestamp che cade su sabato, domenica o altro giorno non-trading di calendario, se quello e' il timestamp canonico del dataset risolto dal DB.
- In questi casi il mapping corretto verso EXEC deve essere definito rispetto al tempo canonico della barra logica, ad esempio "ultimo EXEC close <= logical close anchor", e non rispetto a `trade_date == daily_date`.
- Fare affidamento a un exact-match del tipo `exec_by_anchor.get(logical_close_anchor)` e' un anti-pattern hard-fail: rompe su intraday EXEC, holiday sessions, session split e dataset close-anchored.
- Il CORE puo' rifiutare in fase di registrazione/update/run gli EA che implementano o descrivono un mapping logical->EXEC basato su exact-match dell'anchor.
- Fare affidamento alla sola coincidenza `date(logical_bar) == date(exec_bar)` e' un anti-pattern, perche' rompe su sessioni overnight, weekend labels e dataset close-anchored.
- Se il dataset della barra logica e' etichettato `BAR_START`, usare il `dt` grezzo della barra come `logical close anchor` e' un anti-pattern: il close anchor canonico va derivato dalla barra stessa secondo il suo tf/session semantics e poi usato sia per `cert_dt` sia per il mapping logico -> EXEC.
- Per dataset futures higher-TF session-based (`D/W/M`) con source vendor date-only, il DB puo' persistere `dt` come true bar start operativo della barra e non come label midnight di calendario; gli EA e i consumer devono trattare quel `dt` come source of truth del bar start.
- Se il `logical close anchor` di un segnale cade prima del primo close disponibile nel dataset EXEC realmente caricato per il run, l'EA deve trattare quel tratto come bootstrap/range truncation e saltare il segnale; non deve hard-failare il backtest.
- Se il payload espone `logic_occurrences`, i consumer non devono ignorare relazioni come `depends_on_occurrence_ids` o `used_by_trade_ids` e sostituirle con inferenze locali piu' deboli.
- Una occorrenza visibile sul chart non deve essere assunta implicitamente come quella usata dal trade se il payload distingue tra occorrenze candidate, selezionate, superseded o invalidate.

============================================================
15) ERROR SEMANTICS
============================================================
Se manca un dataframe richiesto da una Condition o da un Pattern:
- il sistema deve fallire con errore esplicito.
- l'EA non deve ricostruire timeframe.
- il runtime non deve sostituire source family o dataset.

============================================================
FINE B3
============================================================

B4_ea_ai_prompt_dev_rules.txt

Download
============================================================
ALGO - DOCUMENT HEADER
------------------------------------------------------------
Doc ID       : B4
Title        : EA - AI PROMPT & DEV RULES (CANONICAL)
Filename     : B4 - EA - AI PROMPT & DEV RULES (CANONICAL) - V12.txt
Version      : V24
Updated      : 2026-04-25 16:35 (Europe/Rome)
Status       : ACTIVE (CANONICAL / NON NEGOZIABILE)
Changelog    :
- V24: introdotte regole canoniche su `logic_occurrences`, provenance/selection delle occorrenze e obbligo di usare `trade_seq` / `run_seq` persistiti invece di rinumerazioni locali.
- V23: introdotto l'obbligo canonico di `logic_state` per i nuovi EA che vogliono Logic Indicator state-based; vietata l'inferenza implicita di boolean bar-per-bar a partire da soli eventi sparsi.
- V22: chiarito esplicitamente che validator e tooling non devono rifiutare `mre` piu' fini del `pattern_tf`; `MRE` e' il minimo EXEC ammesso, non un vincolo a restare sul tf logico del Pattern.
- V21: formalizzato che, se un evento logico mantiene `cert_dt` sul tf logico ma richiede anche un anchor operativo runtime distinto, il draft deve esporre tale anchor in `event.meta` in modo esplicito e coerente.
- V20: formalizzato che la scheda pre-registrazione deve includere obbligatoriamente una tabella riassuntiva canonica in Markdown, piu' una versione testo monospaziata human-readable quando la leggibilita' della tabella Markdown non e' sufficiente.
- V19: formalizzato che ogni Condition/Pattern/Entry/Exit deve dichiarare onset_dt, cert_dt e preset di stato visuale per il logic-indicator; la scheda canonica e' obbligatoria prima della registrazione ufficiale.
- V18: introdotta la regola R-35 che promuove il "Linear Pointer Engine" come implementazione operativa preferita per il mapping logico->EXEC, subordinata alla certificazione di equivalenza col Full Scan.
- V17: reso esplicito che la AI non deve mai generare mapping logical->EXEC per exact-match dell'anchor; il CORE puo' rifiutare script che usano o descrivono questa semantica.
- V16: formalizzato che se il logical close anchor cade prima del primo close disponibile del dataset EXEC caricato, il draft deve trattarlo come bootstrap/range truncation e saltarlo, non generare un hard-fail.
- V15: chiarito esplicitamente che su dataset BAR_START il `dt` grezzo della barra logica non puo' essere usato come close anchor per `cert_dt` o per il mapping verso EXEC.
- V14: formalizzato che le strategie multi-underlying devono usare pattern_context separati per simbolo/underlying; vietato modellare una conferma esterna come colonna aggiunta al dataframe del simbolo tradato.
- V13: formalizzato che PATTERN_REGISTRY deve essere staticamente ispezionabile dagli strumenti ALGO; ammessi riferimenti a costanti top-level staticamente risolvibili, vietata costruzione dinamica del metadata canonico.
- V12: formalizzata la regola che il mapping logico -> EXEC non puo' basarsi sulla sola coincidenza di data calendario; introdotta la gestione canonica di daily/session anchors e dataset etichettati su close di sessione.
- V11: resa obbligatoria una AI_DESCRIPTION strutturata e dettagliata, con tabelle Markdown per Variables/Constants e sezione ChartTrade Semantics motivata.
- V10: formalizzato che MRE non implica EXEC obbligatoriamente uguale al tf logico; EXEC puo' essere uguale o piu' fine.
- V10: formalizzato che i draft tecnici devono accettare i parametri pubblici nel formato runtime corrente e produrre strategy_params canonico nested.
- V10: formalizzato che pattern_symbol nel registry di draft va risolto runtime-aware e che le strategie con potenziale posizione residua devono prevedere force-exit a fine dati.
- V10: chiarito che per condizioni certificate al close della barra, cert_dt deve rappresentare il close logico della barra e non il suo start.
- V09: chiarito che il nuovo strategy_code ufficiale e' assegnato da ALGO in modo sequenziale e non puo' essere deciso dall'AI; strategy_version si usa solo su strategy_code gia' assegnato.
- V08: formalizzato che nome ufficiale, description, ai_description, strategy_code/version e registrazione DB finale di un nuovo EA richiedono conferma esplicita dell'utente.
- V08: distinto draft tecnico validato da promozione ufficiale/registrazione definitiva dell'EA.
- V07: formalizzato che i metadata canonici usati da UI/API/runtime devono essere machine-readable e fail-closed se irrisolti.
- V06: chiarite start/cert, persistence, no-lookahead e plotting sul fenomeno reale.
- V06: esplicitata la regola resolved data wins e il divieto di falsa precisione in visualizzazione.
============================================================

B4 - EA - AI PROMPT & DEV RULES (CANONICAL)
Versione: V24
Status  : ACTIVE (CANONICAL)

============================================================
1) SCOPO
============================================================
Questo documento definisce le regole operative per chi scrive, corregge o genera Expert Advisors per ALGO.
Vale sia per sviluppo umano sia per AI.

============================================================
2) REGOLE HARD-LAW
============================================================
R-01 Usa B3 come contratto canonico dell'output.
R-02 Usa il DB como source of truth per timeframe, dataset, source family e contract specs.
R-03 Vietato introdurre fallback.
R-04 Vietato introdurre alias, normalizzazioni o mapping impliciti.
R-05 Nessuna deduzione arbitraria.
R-06 AI_DESCRIPTION obbligatorio.
R-07 PATTERN_REGISTRY obbligatorio.
R-08 EA = trades + events, NON metrics.
R-09 MAE/MFE canonici per 1 contratto.
R-10 Nessuna posizione aperta nell'output finale.
R-11 Datetime e timezone non negoziabili.
R-12 Nessuna compatibilita' implicita o non dichiarata.
R-13 Nessun TF globale unico dell'EA.
R-14 Ogni Condition dichiara esplicitamente il proprio tf.
R-15 La scelta dati delle Conditions avviene a livello di Pattern Source Family, non di Condition singola.
R-16 EXEC_TF e' una scelta del run; l'EA puo' dichiarare MRE ma non puo' imporre un EXEC_TF unico assoluto.
R-17 In fase di sviluppo AI di un EA, un draft tecnico puo' essere implementato e validato prima della promozione ufficiale, ma Name, Description e AI Description definitivi richiedono conferma esplicita dell'utente.
R-18 Il salvataggio o aggiornamento ufficiale dell'EA in ALGOS/DB puo' avvenire solo dopo verifica esplicita, assegnazione coerente dell'identita' ufficiale da parte di ALGO e conferma esplicita dell'utente.
R-19 Se anche una sola Condition non dichiara un tf canonico DB, l'EA non deve essere salvato in DB: errore esplicito immediato.
R-20 start_dt/start_price e cert_dt/cert_price devono avere semantica esplicita e distinta.
R-21 Vietato usare lookahead per anticipare entry, exit, certificazioni o sopravvivenza futura di un setup.
R-22 Per un nuovo EA, `strategy_code` ufficiale e' assegnato da ALGO in modo sequenziale: AI e sviluppatore non devono inventarlo o anticiparlo. `strategy_version` si usa solo quando esiste gia' uno `strategy_code` ufficiale assegnato da ALGO.
R-23 I metadata canonici che governano UI/API/runtime devono essere disponibili in forma macchina affidabile e deterministica.
R-24 Se pattern attivi, pattern_tf, MRE o requisiti di pattern_context non sono risolvibili in modo affidabile, l'EA non e' UI-runnable e il sistema deve fallire in modo esplicito.
R-25 MRE indica il minimo EXEC ammesso, non l'unico EXEC ammesso: un EA con MRE=D non deve forzare `timeframe == "D"` se puo' operare correttamente anche con EXEC piu' fini supportati dal runtime.
R-25-bis Validator, runtime e tooling non devono interpretare `mre` come vincolo a restare sul `pattern_tf`: un Pattern `D` con `mre = M1` e' semanticamente lecito se la logica operativa richiede almeno `M1`.
R-26 Un draft tecnico deve essere compatibile con il formato runtime pubblico corrente dei params: puo' accettare input flat e/o nested, ma deve sempre emettere `strategy_params` nel formato canonico nested `variables/constants`.
R-27 Nel PATTERN_REGISTRY di un draft tecnico, `pattern_symbol` non deve essere un alias di mercato statico tipo `SPY/ES/MES`: deve essere risolto runtime-aware a partire dal simbolo reale del run.
R-28 Se la logica dell'EA puo' lasciare una posizione aperta a fine dati, il draft deve includere una force-exit esplicita a fine serie.
R-29 Per una condizione certificata al close della barra logica, `cert_dt` deve rappresentare il close logico di quella barra; usare lo start della barra come `cert_dt` e' semanticamente scorretto.
R-30 L'`AI_DESCRIPTION` deve essere strutturata, dettagliata e contenere obbligatoriamente una tabella Markdown per `Variables`, una tabella Markdown per `Constants`, una sezione `Detailed Condition Semantics` con una scheda auditabile per ogni `condition_id` canonico e una sezione `ChartTrade Semantics` con spiegazioni e motivazioni auditabili.
R-31 Il mapping tra barra logica del Pattern e barra `EXEC` non puo' basarsi sulla sola coincidenza di data calendario; deve usare l'anchor temporale canonico della barra logica.
R-32 Per dataset daily/session-based e' lecito che il timestamp canonico della barra logica cada su un giorno non-trading di calendario; l'EA non deve trattarlo come errore solo perche' non esiste una barra `EXEC` con la stessa `date`.
R-32-bis Se ALGO adotta per dataset futures higher-TF (`D/W/M`) la semantica `prices.dt = true bar start`, AI e sviluppatore non devono reintrodurre label midnight fittizie, conversioni browser-local o anchor operativi derivati da timestamp non piu' canonici.
R-33 Il `PATTERN_REGISTRY` deve essere staticamente ispezionabile dagli strumenti ALGO. Sono ammessi letterali puri e riferimenti a costanti top-level staticamente risolvibili; la costruzione dinamica del registry e' vietata.
R-34 Se una strategia usa un sottostante esterno o un simbolo diverso da quello tradato, la logica esterna deve vivere in un proprio Pattern / pattern_context con proprio `pattern_symbol`; e' vietato infilare quei dati come colonna extra del dataframe del simbolo tradato.
R-35 Per prestazioni ottimali su timeframe intraday e dataset estesi, l'implementazione operativa preferita del mapping logico->EXEC e' il "Linear Pointer Engine" (O(N+M)). L'uso di scansioni complete (Full Scan O(N*M)) e' ammesso solo per audit, debugging o laddove la densita' dei dati non rappresenti un collo di bottiglia. Ogni implementazione pointer-based deve essere certificata per equivalenza semantica col Full Scan.
R-36 Prima della registrazione ufficiale di un nuovo EA, ogni Condition / Pattern / Entry / Exit deve avere una scheda canonica che dichiari esplicitamente: significato operativo, onset_dt, cert_dt, tf di onset, tf di cert e preset di stato visuale nel logic-indicator.
R-37 Nel lessico canonico, `start_dt` del payload runtime rappresenta l'`onset_dt`: cioe' il momento in cui l'entita' nasce operativamente sul mercato. `cert_dt` rappresenta invece il momento di certificazione sul tf logico/pattern. I due campi non sono intercambiabili.
R-38 Il preset di stato visuale del logic-indicator e' parte del contratto dell'EA e deve essere deciso prima del salvataggio in DB. I preset canonici ammessi sono: `event_only`, `until_cert`, `until_entry`, `until_exit`, `while_setup_active`, `while_position_open`, `sticky_historical`, `boolean_bar_by_bar`, `custom_rule`.
R-39 La scheda pre-registrazione deve contenere obbligatoriamente una tabella riassuntiva canonica che, per ogni Condition / Pattern / Entry / Exit / Position State rilevante, distingua chiaramente almeno: categoria, significato operativo, tf logico reale, tf con cui l'entita' appare nel payload runtime corrente, motivazione di eventuali differenze e preset di stato visuale.
R-40 La tabella riassuntiva canonica deve esistere almeno in formato Markdown dentro la scheda principale. Se la densita' di colonne o testo rende il Markdown poco leggibile, deve esistere anche una seconda copia human-readable in testo monospaziato (`.txt`) coerente con la versione Markdown; le due versioni devono descrivere la stessa semantica e non possono divergere.
R-41 Se un evento logico (`CONDITION` / `PATTERN`) conserva `start_dt/cert_dt` sul proprio tf logico ma necessita anche di un anchor operativo runtime distinto per cronologia, plotting o fill-mapping, il draft deve esporre quell'anchor in `event.meta` usando nomi espliciti e auditabili come `runtime_anchor_dt`, `runtime_anchor_price`, ed eventualmente `logical_bar_start_dt` quando il dataset logico e' `BAR_START`.
R-42 Se un EA nuovo o aggiornato dichiara supporto al Logic Indicator in modalita' state-based, il payload deve esporre esplicitamente il blocco `logic_state` canonico; non e' ammesso demandare al consumer la ricostruzione implicita dello stato booleano bar-per-bar.
R-43 Il blocco `logic_state` deve esportare serie booleane dense e auditabili sul tf sorgente reale per ogni Condition / Pattern / Position State che il Logic Indicator deve rappresentare in modalita' state-based.
R-44 In assenza di `logic_state`, un consumer che richiede la visualizzazione state-based non deve inferire o normalizzare silenziosamente il booleano a partire da soli eventi sparsi; deve fallire in modo esplicito oppure limitarsi alla sola modalita' event-based dichiarata.
R-45 Se la leggibilita' auditiva di una strategia dipende dal distinguere occorrenze diverse della stessa entita' astratta, il draft deve esporre esplicitamente `logic_occurrences`; non basta affidarsi a soli `events` o a `logic_state`.
R-46 `logic_occurrences` deve essere progettato in modo generalizzabile e non strategy-specific hardcoded: la AI deve modellare occurrence, ruolo semantico, stato lifecycle, dipendenze e collegamento ai trade, senza assumere che concetti come `SL1/SL2` siano universali.
R-47 Se una occorrenza visibile sul chart non e' necessariamente quella usata dal trade, il draft deve dichiarare in modo esplicito la provenance e la selezione tramite campi occurrence-based auditabili come `depends_on_occurrence_ids`, `used_by_trade_ids` e metadata equivalenti approvati.
R-48 Se il run/trade e' persistito in DB con identificatori umani sequenziali (`run_seq`, `trade_seq`), UI/API/ChartTrade devono leggerli dal dato persistito e non rinumerare localmente run o trade.

============================================================
3) MODELLO CONCETTUALE DA USARE
============================================================
... (resto del file invariato)

B5_ea_technical_template.py

Download
# ===========================================================
# ALGO - DOCUMENT HEADER
# -----------------------------------------------------------
# Doc ID      : B5
# Title       : EA - TECHNICAL TEMPLATE (ALL REQUIRED)
# Filename    : B5 - EA - TECHNICAL TEMPLATE (ALL REQUIRED) - V09.py
# Version     : V10
# Updated     : 2026-04-25 16:35 (Europe/Rome)
# Status      : TEMPLATE TECNICO (CANONICAL / NON NEGOZIABILE)
# Changelog   :
#  - V10: introdotto il blocco canonico opzionale `logic_occurrences` con helper minimi per audit occurrence-based.
#  - V09: introdotto il blocco canonico opzionale `logic_state` con helper minimi per serie booleane state-based.
#  - V08: introdotto il "Linear Pointer Engine" come implementazione preferita per il mapping logical->EXEC; 
#         la funzione full-scan resta come riferimento semantico per audit e debugging.
#  - V07: aggiunti helper canonici per logical close anchor e mapping robusto verso EXEC; vietato exact-match dell'anchor logico.
#  - V06: resa prescrittiva la shape dell'AI_DESCRIPTION con sezioni obbligatorie e tabelle Markdown per Variables/Constants.
# ===========================================================

"""
B5 - EA - TECHNICAL TEMPLATE (ALL REQUIRED) - V10

USO
- Starter ufficiale per implementare un EA compatibile con ALGO vNext.
- Implementa SOLO la logica di trading dentro run_backtest().
- Usa il Linear Pointer Engine per prestazioni ottimali su timeframe intraday.

HARD-LAW
- Output: solo le chiavi previste da B3.
- Niente extra keys non approvate.
- Mapping logical->EXEC: vietato exact-match; usare "latest EXEC close anchor <= logical close anchor".
- Linear Pointer Engine: e' l'implementazione operativa preferita (O(N+M)).
- Full Scan: e' la fonte di verita' semantica per audit e certificazione di equivalenza.
- Prerequisiti: dataset ordinati cronologicamente e timezone coerenti.
"""

from __future__ import annotations

from datetime import datetime, timezone
from typing import Any, Dict, List, Literal

import pandas as pd


CONTRACT_ID = "ALGO_EA_OUTPUT"
CONTRACT_VERSION = "B3-V04"
LOGIC_STATE_SCHEMA_VERSION = "LS-V01"
LOGIC_OCCURRENCES_SCHEMA_VERSION = "LO-V01"

Side = Literal["LONG", "SHORT"]
EventType = Literal["CONDITION", "PATTERN", "ENTRY", "EXIT"]

CANONICAL_TIMEFRAMES = {
    "S1", "M1", "M3", "M5", "M10", "M15", "M30",
    "H1", "H2", "H4", "H6", "D", "W", "M", "Y", "EXEC",
}

DEFAULT_PARAMS: Dict[str, Any] = {
    "variables": {
        "initial_capital": 100000.0,
        "position_size": 1.0,
        "commission_per_trade": 0.0,
        "slippage_ticks": 0,
    },
    "constants": {
        "tick_size": 0.25,
        "point_value": 50.0,
    },
}

PATTERN_REGISTRY: Dict[str, Any] = {
    "registry_version": CONTRACT_VERSION,
    "parameters": {
        "variables": {
            "initial_capital": "UI editable",
            "position_size": "UI editable",
            "commission_per_trade": "UI editable",
            "slippage_ticks": "UI editable",
        },
        "constants": {
            "tick_size": "Visible, not editable",
            "point_value": "Visible, not editable",
        },
    },
    "patterns": [
        {
            "pattern_id": "P1",
            "pattern_symbol": "RUNTIME_SYMBOL",
            "title": "Example Pattern",
            "summary": "Replace with real strategy logic.",
            "pattern_tf": "M15",
            "mre": "M15",
            "conditions": [
                {
                    "condition_id": "P1C1",
                    "description": "Example condition",
                    "easy_language": "Close > Open;",
                    "tf": "M15",
                    "certification_rule": "Close of the M15 bar",
                }
            ],
            "entries": [
                {
                    "entry_id": "P1E1",
                    "description": "Example entry",
                    "easy_language": "Buy this bar on close;",
                    "tf": "EXEC",
                    "fill_policy": "EXEC close",
                }
            ],
            "exits": [
                {
                    "exit_id": "P1X1",
                    "description": "Example exit",
                    "easy_language": "Sell this bar on close;",
                    "tf": "EXEC",
                    "fill_policy": "EXEC close",
                }
            ],
            "completion_rule": "Pattern completes when P1C1 certifies.",
        }
    ],
    "meta": {},
}

AI_DESCRIPTION = """## Strategy Overview
Describe the strategy in natural language and explain the market idea clearly.

## Pattern Logic
- Pattern(s):
- Conditions:
- Entry:
- Exit:
- Logical timeframes:
- pattern_tf:
- MRE:

## Detailed Condition Semantics
### `P1C1` - Condition title
Formula:
- Exact formula or operational rule.

Parameters/defaults:
- Declare every parameter/default used by the condition.

Pass/fail rule:
- Declare the exact boolean rule and operator strictness.

Market meaning:
- Explain what the condition means in market terms.

Role inside the Pattern:
- Explain why this condition exists and what it filters or confirms.

Data availability / certification:
- Explain warm-up, onset/start, cert, and chart/audit semantics when relevant.

## Variables
| Variable | Meaning | Effect on strategy |
|---|---|---|
| initial_capital | Starting capital used by the runtime. | Affects downstream CORE metrics, not the signal logic. |
| position_size | Contracts or units traded per position. | Scales trade exposure and PnL. |
| commission_per_trade | Commission per contract/trade. | Reduces net PnL. |
| slippage_ticks | Slippage expressed in ticks. | Worsens effective fills and net PnL. |

## Constants
| Constant | Meaning | Effect on execution / PnL |
|---|---|---|
| tick_size | Minimum price increment from contract specs. | Governs tick/point conversion and slippage math. |
| point_value | Monetary value of one point from contract specs. | Governs PnL and MAE/MFE currency conversion. |

## ChartTrade Semantics
- Visible events:
- Non-visible events:
- Why each visible event deserves to be shown:
- Chart anchor for each visible event:
- Interpretation of start_dt/start_price vs cert_dt/cert_price:
- Mapping between logical bar and EXEC bar, if applicable:

## Operational Notes
- Position model:
- Force-exit behavior:
- Runtime assumptions:
"""


def _to_utc_iso(x: Any) -> str:
    ts = pd.Timestamp(x)
    if ts.tzinfo is None:
        ts = ts.tz_localize("America/New_York", nonexistent="shift_forward")
    return ts.tz_convert("UTC").isoformat().replace("+00:00", "Z")


def _runtime_pattern_registry(symbol: str) -> Dict[str, Any]:
    registry = dict(PATTERN_REGISTRY)
    patterns = []
    for pattern in PATTERN_REGISTRY.get("patterns", []):
        runtime_pattern = dict(pattern)
        runtime_pattern["pattern_symbol"] = str(symbol)
        patterns.append(runtime_pattern)
    registry["patterns"] = patterns
    return registry


def _merge_params(params: Dict[str, Any] | None) -> Dict[str, Any]:
    merged: Dict[str, Any] = {}
    if isinstance(DEFAULT_PARAMS.get("variables"), dict):
        merged.update(DEFAULT_PARAMS["variables"])
    if isinstance(DEFAULT_PARAMS.get("constants"), dict):
        merged.update(DEFAULT_PARAMS["constants"])

    src = dict(params or {})
    if isinstance(src.get("variables"), dict):
        merged.update(src["variables"])
    if isinstance(src.get("constants"), dict):
        merged.update(src["constants"])
    merged.update({k: v for k, v in src.items() if k not in {"variables", "constants"}})
    return merged


def _build_public_strategy_params(params: Dict[str, Any]) -> Dict[str, Any]:
    return {
        "variables": {
            "initial_capital": float(params["initial_capital"]),
            "position_size": float(params["position_size"]),
            "commission_per_trade": float(params["commission_per_trade"]),
            "slippage_ticks": int(params["slippage_ticks"]),
        },
        "constants": {
            "tick_size": float(params["tick_size"]),
            "point_value": float(params["point_value"]),
        },
    }


def _timeframe_close_offset(tf: str) -> pd.Timedelta:
    token = str(tf or "").strip().upper()
    mapping = {
        "S1": pd.Timedelta(seconds=1),
        "M1": pd.Timedelta(minutes=1),
        "M3": pd.Timedelta(minutes=3),
        "M5": pd.Timedelta(minutes=5),
        "M10": pd.Timedelta(minutes=10),
        "M15": pd.Timedelta(minutes=15),
        "M30": pd.Timedelta(minutes=30),
        "H1": pd.Timedelta(hours=1),
        "H2": pd.Timedelta(hours=2),
        "H4": pd.Timedelta(hours=4),
        "H6": pd.Timedelta(hours=6),
        "D": pd.Timedelta(days=1),
    }
    if token not in mapping:
        raise ValueError(f"Unsupported timeframe for close offset: {tf!r}")
    return mapping[token]


def _logical_close_anchor_series(df: pd.DataFrame, tf: str) -> pd.Series:
    starts = pd.to_datetime(df["dt"], errors="raise")
    token = str(tf or "").strip().upper()
    if token == "D":
        next_starts = starts.shift(-1)
        return next_starts.fillna(starts + pd.Timedelta(days=1))
    return starts + _timeframe_close_offset(token)


def _resolve_exec_row_at_or_before_logical_anchor(
    exec_rows: List[Dict[str, Any]],
    logical_close_anchor: pd.Timestamp,
    first_exec_close_anchor: pd.Timestamp,
) -> Dict[str, Any] | None:
    """
    SOURCE OF TRUTH SEMANTICS (Full Scan).
    Used for audit, debugging and certification of equivalence.
    Complexity: O(M)
    """
    if logical_close_anchor < first_exec_close_anchor:
        return None
    candidate: Dict[str, Any] | None = None
    for row in exec_rows:
        # Assumes row["close_anchor_ts"] is already present and UTC
        row_anchor = pd.Timestamp(row["close_anchor_ts"])
        if row_anchor <= logical_close_anchor:
            candidate = row
            continue
        break
    if candidate is None:
        raise ValueError(f"No EXEC close anchor <= logical close anchor {logical_close_anchor.isoformat()}")
    return candidate


def make_event(
    *,
    event_id: str,
    event_type: EventType,
    tf: str,
    start_dt: Any,
    start_price: float,
    cert_tf: str,
    cert_dt: Any,
    cert_price: float,
    meta: Dict[str, Any] | None = None,
) -> Dict[str, Any]:
    return {
        "event_id": str(event_id),
        "event_type": str(event_type),
        "tf": str(tf),
        "start_dt": _to_utc_iso(start_dt),
        "start_price": float(start_price),
        "cert_tf": str(cert_tf),
        "cert_dt": _to_utc_iso(cert_dt),
        "cert_price": float(cert_price),
        "meta": dict(meta or {}),
    }


def build_base_output(
    *,
    symbol: str,
    timeframe: str,
    period: str,
    strategy_name: str,
    strategy_version: str,
    strategy_code: str,
    strategy_params: Dict[str, Any],
) -> Dict[str, Any]:
    return {
        "contract_id": CONTRACT_ID,
        "contract_version": CONTRACT_VERSION,
        "generated_at_utc": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
        "symbol": str(symbol),
        "timeframe": str(timeframe),
        "period": str(period),
        "strategy_name": str(strategy_name),
        "strategy_version": str(strategy_version),
        "strategy_code": str(strategy_code),
        "strategy_type": "EA",
        "strategy_params": dict(strategy_params),
        "pattern_registry": _runtime_pattern_registry(symbol),
        "ai_description": AI_DESCRIPTION,
        "trades": [],
        "logic_state": {
            "schema_version": LOGIC_STATE_SCHEMA_VERSION,
            "series": [],
            "meta": {},
        },
        "logic_occurrences": {
            "schema_version": LOGIC_OCCURRENCES_SCHEMA_VERSION,
            "items": [],
            "meta": {},
        },
        "build": {
            "algo_version": "",
            "runtime_version": "",
            "ea_template_version": "V10",
            "validator_version": "",
            "git_commit": "",
            "python_version": "",
            "build_machine": "",
            "dataset_timezone": "America/New_York",
            "output_timezone": "UTC",
            "dt_convention": "BAR_START",
        },
        "meta": {},
    }


def make_logic_state_point(*, dt: Any, value: bool) -> Dict[str, Any]:
    return {
        "dt": _to_utc_iso(dt),
        "value": bool(value),
    }


def make_logic_state_series(
    *,
    state_id: str,
    state_type: str,
    state_tf: str,
    visual_preset: str,
    points: List[Dict[str, Any]],
    meta: Dict[str, Any] | None = None,
) -> Dict[str, Any]:
    return {
        "state_id": str(state_id),
        "state_type": str(state_type),
        "state_tf": str(state_tf),
        "visual_preset": str(visual_preset),
        "points": list(points),
        "meta": dict(meta or {}),
    }


def make_logic_occurrence(
    *,
    occurrence_id: str,
    entity_id: str,
    entity_type: str,
    logic_label_short: str,
    logic_label_long: str,
    semantic_role: str,
    status: str,
    occurrence_tf: str,
    start_dt: Any,
    start_price: float,
    cert_dt: Any,
    cert_price: float,
    depends_on_occurrence_ids: List[str] | None = None,
    used_by_trade_ids: List[int] | None = None,
    meta: Dict[str, Any] | None = None,
) -> Dict[str, Any]:
    return {
        "occurrence_id": str(occurrence_id),
        "entity_id": str(entity_id),
        "entity_type": str(entity_type),
        "logic_label_short": str(logic_label_short),
        "logic_label_long": str(logic_label_long),
        "semantic_role": str(semantic_role),
        "status": str(status),
        "occurrence_tf": str(occurrence_tf),
        "start_dt": _to_utc_iso(start_dt),
        "start_price": float(start_price),
        "cert_dt": _to_utc_iso(cert_dt),
        "cert_price": float(cert_price),
        "depends_on_occurrence_ids": list(depends_on_occurrence_ids or []),
        "used_by_trade_ids": list(used_by_trade_ids or []),
        "meta": dict(meta or {}),
    }


def run_backtest(
    symbol: str,
    timeframe: str,
    period: str,
    params: Dict[str, Any],
    df_exec: pd.DataFrame,
    pattern_contexts: Dict[str, Dict[str, Any]],
) -> Dict[str, Any]:
    if df_exec is None or df_exec.empty:
        raise ValueError("df_exec is empty")
    if not isinstance(pattern_contexts, dict) or not pattern_contexts:
        raise ValueError("pattern_contexts must be a non-empty dict")

    merged_params = _merge_params(params)
    strategy_params = _build_public_strategy_params(merged_params)
    result = build_base_output(
        symbol=symbol,
        timeframe=timeframe,
        period=period,
        strategy_name="PROPOSED_DRAFT_STRATEGY",
        strategy_version="UNASSIGNED_VERSION",
        strategy_code="UNASSIGNED_BY_ALGO",
        strategy_params=strategy_params,
    )

    # 1. Prepare EXEC rows with anchors
    # (Note: In a real EA, you'd add close_anchor_ts to df_exec first)
    exec_rows = df_exec.to_dict("records")
    num_exec = len(exec_rows)
    first_exec_anchor = pd.Timestamp(exec_rows[0].get("close_anchor_ts", exec_rows[0]["dt"]))

    # 2. PREFERRED OPTIMIZED IMPLEMENTATION: Linear Pointer Engine
    # Complexity: O(N+M) ammortized.
    exec_ptr = 0
    
    # Example Loop over logical timeframe (e.g. D or H4 from pattern_contexts)
    # for _, logical_row in df_pattern.iterrows():
    #     logical_anchor = logical_row["close_anchor_ts"]
    #
    #     # Linear mapping logic
    #     best_exec_idx = -1
    #     while exec_ptr < num_exec:
    #         row_anchor = pd.Timestamp(exec_rows[exec_ptr]["close_anchor_ts"])
    #         if row_anchor <= logical_anchor:
    #             best_exec_idx = exec_ptr
    #             exec_ptr += 1
    #         else:
    #             break
    #     
    #     # Partial Reset: allows the same EXEC bar for multiple logical bars if anchors overlap.
    #     exec_ptr = max(0, best_exec_idx)
    #     
    #     if best_exec_idx == -1:
    #         exec_row = None # Bootstrap / Truncation
    #     else:
    #         exec_row = exec_rows[best_exec_idx]

    return result

B6_ea_output_validator_strict.py

Download
# =============================================================
# ALGO - DOCUMENT HEADER
# -------------------------------------------------------------
# Doc ID       : B6
# Title        : CORE - EA OUTPUT VALIDATOR STRICT (ALL REQUIRED)
# Filename     : B6 - CORE - EA OUTPUT VALIDATOR STRICT (ALL REQUIRED) - V06.py
# Version      : V08
# Updated      : 2026-04-25 16:35 (Europe/Rome)
# Status       : ACTIVE (ENFORCEMENT / NON NEGOZIABILE)
# Changelog    :
#  - V08: introdotto supporto validator al blocco opzionale `logic_occurrences` per audit occurrence-based e provenance esplicita.
#  - V07: introdotto supporto validator al blocco opzionale `logic_state` per serie booleane state-based di Condition, Pattern e Position State.
#  - V06: corretto il controllo su `pattern.mre`: MRE e' il minimo EXEC ammesso, quindi puo' essere uguale o piu' fine del `pattern_tf`; il validator non deve rifiutare pattern daily con MRE intraday.
#  - V05: introdotto supporto validator a `meta.runtime_anchor_dt` opzionale per eventi logici, cosi' la cronologia runtime puo' distinguersi dal timestamp logico di certificazione.
#  - V04: chiarita la differenza tra versione del documento e contract_version del payload validato.
#  - V03: allineato a B3-V04 e B2-V03.
#  - V03: strategy_params separati in variables/constants.
#  - V03: pattern_registry allineato a Pattern TF, MRE e Pattern Symbol.
#  - V03: deep validation aggiornata da pattern_dfs flat a pattern_contexts.
# =============================================================

from __future__ import annotations

from typing import Any, Dict, Optional, Set

import pandas as pd
from zoneinfo import ZoneInfo


CONTRACT_ID = "ALGO_EA_OUTPUT"
CONTRACT_VERSION = "B3-V04"  # Payload contract version currently enforced by validator.
DATASET_TZ = ZoneInfo("America/New_York")

ALLOWED_SIDES = {"LONG", "SHORT"}
ALLOWED_EVENT_TYPES = {"CONDITION", "PATTERN", "ENTRY", "EXIT"}
CANONICAL_TIMEFRAMES = {
    "S1", "M1", "M3", "M5", "M10", "M15", "M30",
    "H1", "H2", "H4", "H6",
    "D", "W", "M", "Y", "EXEC",
}
DB_TIMEFRAMES = CANONICAL_TIMEFRAMES - {"EXEC"}

EXTRA_FORBIDDEN_TOP_LEVEL_KEYS: Set[str] = {
    "equity", "returns", "drawdown", "metrics", "stats", "logs", "debug", "warning", "error"
}

TOP_LEVEL_KEYS: Set[str] = {
    "contract_id",
    "contract_version",
    "generated_at_utc",
    "symbol",
    "timeframe",
    "period",
    "strategy_name",
    "strategy_version",
    "strategy_code",
    "strategy_type",
    "strategy_params",
    "pattern_registry",
    "ai_description",
    "trades",
    "build",
    "meta",
}
OPTIONAL_TOP_LEVEL_KEYS: Set[str] = {"logic_state", "logic_occurrences"}

LOGIC_STATE_SCHEMA_VERSION = "LS-V01"
REQUIRED_LOGIC_STATE_KEYS: Set[str] = {"schema_version", "series", "meta"}
REQUIRED_LOGIC_STATE_SERIES_KEYS: Set[str] = {
    "state_id",
    "state_type",
    "state_tf",
    "visual_preset",
    "points",
    "meta",
}
REQUIRED_LOGIC_STATE_POINT_KEYS: Set[str] = {"dt", "value"}
ALLOWED_LOGIC_STATE_TYPES = {"CONDITION", "PATTERN", "POSITION"}
ALLOWED_VISUAL_PRESETS = {
    "event_only",
    "until_cert",
    "until_entry",
    "until_exit",
    "while_setup_active",
    "while_position_open",
    "sticky_historical",
    "boolean_bar_by_bar",
    "custom_rule",
}
LOGIC_OCCURRENCES_SCHEMA_VERSION = "LO-V01"
REQUIRED_LOGIC_OCCURRENCES_KEYS: Set[str] = {"schema_version", "items", "meta"}
REQUIRED_LOGIC_OCCURRENCE_ITEM_KEYS: Set[str] = {
    "occurrence_id",
    "entity_id",
    "entity_type",
    "logic_label_short",
    "logic_label_long",
    "semantic_role",
    "status",
    "occurrence_tf",
    "start_dt",
    "start_price",
    "cert_dt",
    "cert_price",
    "depends_on_occurrence_ids",
    "used_by_trade_ids",
    "meta",
}
ALLOWED_LOGIC_OCCURRENCE_TYPES = {"CONDITION", "PATTERN", "ENTRY", "EXIT", "POSITION"}
ALLOWED_LOGIC_OCCURRENCE_ROLES = {
    "anchor",
    "candidate",
    "confirmation",
    "filter",
    "gate",
    "pattern_complete",
    "entry_trigger",
    "exit_trigger",
    "position_state",
    "invalidation",
    "replacement",
}
ALLOWED_LOGIC_OCCURRENCE_STATUS = {
    "candidate",
    "surviving_candidate",
    "certified",
    "selected",
    "superseded",
    "invalidated",
}

REQUIRED_BUILD_KEYS: Set[str] = {
    "algo_version",
    "runtime_version",
    "ea_template_version",
    "validator_version",
    "git_commit",
    "python_version",
    "build_machine",
    "dataset_timezone",
    "output_timezone",
    "dt_convention",
}

REQUIRED_STRATEGY_PARAM_ROOT_KEYS: Set[str] = {"variables", "constants"}
REQUIRED_VARIABLE_KEYS: Set[str] = {
    "initial_capital",
    "position_size",
    "commission_per_trade",
    "slippage_ticks",
}
REQUIRED_CONSTANT_KEYS: Set[str] = {"tick_size", "point_value"}

REQUIRED_REGISTRY_KEYS: Set[str] = {"registry_version", "parameters", "patterns", "meta"}
REQUIRED_REGISTRY_PARAMETER_KEYS: Set[str] = {"variables", "constants"}
REQUIRED_PATTERN_KEYS: Set[str] = {
    "pattern_id",
    "pattern_symbol",
    "title",
    "summary",
    "pattern_tf",
    "mre",
    "conditions",
    "entries",
    "exits",
    "completion_rule",
}
REQUIRED_CONDITION_KEYS: Set[str] = {"condition_id", "description", "easy_language", "tf", "certification_rule"}
REQUIRED_ENTRY_KEYS: Set[str] = {"entry_id", "description", "easy_language", "tf", "fill_policy"}
REQUIRED_EXIT_KEYS: Set[str] = {"exit_id", "description", "easy_language", "tf", "fill_policy"}

REQUIRED_TRADE_KEYS: Set[str] = {
    "id", "side", "qty",
    "entry_dt", "entry_price", "entry_bar",
    "exit_dt", "exit_price", "exit_bar",
    "stop_price",
    "mae_ticks", "mfe_ticks", "mae_points", "mfe_points", "mae_ccy_1c", "mfe_ccy_1c",
    "pnl_gross", "commission", "slippage", "pnl_net",
    "events", "meta",
}

REQUIRED_EVENT_KEYS: Set[str] = {
    "event_id", "event_type", "tf",
    "start_dt", "start_price",
    "cert_tf", "cert_dt", "cert_price",
    "meta",
}


def _ensure(cond: bool, msg: str) -> None:
    if not cond:
        raise ValueError(msg)


def _float_close(a: float, b: float, tol: float = 1e-8) -> bool:
    return abs(float(a) - float(b)) <= tol


def _is_utc_iso_datetime(s: Any) -> bool:
    if not isinstance(s, str) or not s.strip():
        return False
    try:
        ts = pd.Timestamp(s)
    except Exception:
        return False
    if ts.tzinfo is None:
        return False
    try:
        return ts.tz_convert("UTC").utcoffset() == pd.Timedelta(0)
    except Exception:
        return False


def _df_dt_to_utc_series(df: pd.DataFrame) -> pd.Series:
    _ensure("dt" in df.columns, "df must have column 'dt'")
    s = pd.to_datetime(df["dt"], errors="coerce")
    _ensure(not s.isna().all(), "df.dt could not be parsed to datetime")
    out = []
    for v in s:
        if pd.isna(v):
            out.append(pd.NaT)
            continue
        ts = pd.Timestamp(v)
        if ts.tzinfo is None:
            ts = ts.tz_localize(DATASET_TZ)
        out.append(ts.tz_convert("UTC"))
    return pd.Series(out, index=df.index)


def _event_runtime_dt(event: Dict[str, Any]) -> pd.Timestamp:
    meta = event.get("meta")
    if isinstance(meta, dict):
        runtime_anchor_dt = meta.get("runtime_anchor_dt")
        if runtime_anchor_dt is not None:
            _ensure(
                _is_utc_iso_datetime(runtime_anchor_dt),
                f"event.meta.runtime_anchor_dt must be UTC ISO-8601 string (...Z) when provided: {event.get('event_id')}",
            )
            return pd.Timestamp(runtime_anchor_dt).tz_convert("UTC")
    return pd.Timestamp(event["cert_dt"]).tz_convert("UTC")


def _event_logical_bar_start_dt(event: Dict[str, Any]) -> Optional[pd.Timestamp]:
    meta = event.get("meta")
    if not isinstance(meta, dict):
        return None
    logical_bar_start_dt = meta.get("logical_bar_start_dt")
    if logical_bar_start_dt is None:
        return None
    _ensure(
        _is_utc_iso_datetime(logical_bar_start_dt),
        f"event.meta.logical_bar_start_dt must be UTC ISO-8601 string (...Z) when provided: {event.get('event_id')}",
    )
    return pd.Timestamp(logical_bar_start_dt).tz_convert("UTC")


def _parse_tf_to_offset(tf: str):
    token = str(tf or "").strip().upper()
    _ensure(token in DB_TIMEFRAMES, f"Invalid DB timeframe token: {tf!r}")
    mapping = {
        "S1": pd.Timedelta(seconds=1),
        "M1": pd.Timedelta(minutes=1),
        "M3": pd.Timedelta(minutes=3),
        "M5": pd.Timedelta(minutes=5),
        "M10": pd.Timedelta(minutes=10),
        "M15": pd.Timedelta(minutes=15),
        "M30": pd.Timedelta(minutes=30),
        "H1": pd.Timedelta(hours=1),
        "H2": pd.Timedelta(hours=2),
        "H4": pd.Timedelta(hours=4),
        "H6": pd.Timedelta(hours=6),
        "D": pd.Timedelta(days=1),
        "W": pd.DateOffset(weeks=1),
        "M": pd.DateOffset(months=1),
        "Y": pd.DateOffset(years=1),
    }
    return mapping[token]


def _lookup_close_at_event_time(close_map: Dict[pd.Timestamp, float], event_dt_utc: pd.Timestamp, tf_dur, context: str) -> float:
    if event_dt_utc in close_map:
        return close_map[event_dt_utc]
    try:
        dt_minus = event_dt_utc - tf_dur
    except Exception as exc:
        raise ValueError(f"Cannot compute tf offset for {context}: {exc}") from exc
    if dt_minus in close_map:
        return close_map[dt_minus]
    raise ValueError(f"Cannot map {context} dt={event_dt_utc} to any bar start (dt or dt-tf).")


def _lookup_close_for_event(event: Dict[str, Any], close_map: Dict[pd.Timestamp, float], event_dt_utc: pd.Timestamp, tf_dur, context: str) -> float:
    logical_bar_start_dt = _event_logical_bar_start_dt(event)
    if logical_bar_start_dt is not None:
        if logical_bar_start_dt in close_map:
            return close_map[logical_bar_start_dt]
        raise ValueError(f"Cannot map {context} logical_bar_start_dt={logical_bar_start_dt} to any known bar start.")
    return _lookup_close_at_event_time(close_map, event_dt_utc, tf_dur, context)


def validate_output_strict(result: Dict[str, Any]) -> None:
    _ensure(isinstance(result, dict), "Result must be a dict.")

    keys = set(result.keys())
    missing = sorted(TOP_LEVEL_KEYS - keys)
    _ensure(not missing, f"Missing required top-level keys: {missing}")
    unknown = sorted(keys - TOP_LEVEL_KEYS - OPTIONAL_TOP_LEVEL_KEYS)
    _ensure(not unknown, f"Unknown top-level keys (forbidden): {unknown}")
    forbidden = sorted(keys & EXTRA_FORBIDDEN_TOP_LEVEL_KEYS)
    _ensure(not forbidden, f"Forbidden top-level keys present: {forbidden}")

    _ensure(result["contract_id"] == CONTRACT_ID, "contract_id mismatch.")
    _ensure(result["contract_version"] == CONTRACT_VERSION, "contract_version mismatch.")
    _ensure(_is_utc_iso_datetime(result["generated_at_utc"]), "generated_at_utc must be UTC ISO-8601 string (...Z).")

    for k in ["symbol", "timeframe", "period", "strategy_name", "strategy_version", "strategy_code", "strategy_type"]:
        _ensure(isinstance(result[k], str), f"{k} must be string.")
        if k != "period":
            _ensure(bool(result[k].strip()), f"{k} must be non-empty string.")

    _ensure(result["timeframe"] in DB_TIMEFRAMES, f"timeframe must be one of {sorted(DB_TIMEFRAMES)}")
    _ensure(result["strategy_type"] == "EA", "strategy_type must be 'EA'.")
    _ensure(isinstance(result["strategy_params"], dict), "strategy_params must be dict.")
    _ensure(isinstance(result["pattern_registry"], dict), "pattern_registry must be dict.")
    _ensure(isinstance(result["ai_description"], str), "ai_description must be string.")
    _ensure(isinstance(result["trades"], list), "trades must be list.")
    _ensure(isinstance(result["build"], dict), "build must be dict.")
    _ensure(isinstance(result["meta"], dict), "meta must be dict.")

    build = result["build"]
    miss_build = sorted(REQUIRED_BUILD_KEYS - set(build.keys()))
    _ensure(not miss_build, f"Missing build keys: {miss_build}")
    for bk in REQUIRED_BUILD_KEYS:
        _ensure(isinstance(build[bk], str), f"build.{bk} must be string.")
    _ensure(build["dataset_timezone"] == "America/New_York", "build.dataset_timezone must be 'America/New_York'.")
    _ensure(build["output_timezone"] == "UTC", "build.output_timezone must be 'UTC'.")
    _ensure(build["dt_convention"] == "BAR_START", "build.dt_convention must be 'BAR_START'.")

    sp = result["strategy_params"]
    miss_sp_root = sorted(REQUIRED_STRATEGY_PARAM_ROOT_KEYS - set(sp.keys()))
    _ensure(not miss_sp_root, f"Missing required strategy_params root keys: {miss_sp_root}")
    extra_sp_root = sorted(set(sp.keys()) - REQUIRED_STRATEGY_PARAM_ROOT_KEYS)
    _ensure(not extra_sp_root, f"Forbidden strategy_params root keys: {extra_sp_root}")
    _ensure(isinstance(sp["variables"], dict), "strategy_params.variables must be dict")
    _ensure(isinstance(sp["constants"], dict), "strategy_params.constants must be dict")

    variables = sp["variables"]
    constants = sp["constants"]
    miss_vars = sorted(REQUIRED_VARIABLE_KEYS - set(variables.keys()))
    miss_consts = sorted(REQUIRED_CONSTANT_KEYS - set(constants.keys()))
    _ensure(not miss_vars, f"Missing strategy_params.variables keys: {miss_vars}")
    _ensure(not miss_consts, f"Missing strategy_params.constants keys: {miss_consts}")
    reserved_vars = sorted(k for k in variables.keys() if isinstance(k, str) and k.startswith("_"))
    reserved_consts = sorted(k for k in constants.keys() if isinstance(k, str) and k.startswith("_"))
    _ensure(not reserved_vars, f"strategy_params.variables contains forbidden reserved keys: {reserved_vars}")
    _ensure(not reserved_consts, f"strategy_params.constants contains forbidden reserved keys: {reserved_consts}")

    _ensure(float(variables["initial_capital"]) > 0, "strategy_params.variables.initial_capital must be > 0")
    _ensure(float(variables["position_size"]) > 0, "strategy_params.variables.position_size must be > 0")
    _ensure(float(variables["commission_per_trade"]) >= 0, "strategy_params.variables.commission_per_trade must be >= 0")
    _ensure(float(constants["tick_size"]) > 0, "strategy_params.constants.tick_size must be > 0")
    _ensure(float(constants["point_value"]) > 0, "strategy_params.constants.point_value must be > 0")
    st = variables["slippage_ticks"]
    _ensure(isinstance(st, (int, float)), "strategy_params.variables.slippage_ticks must be numeric")
    _ensure(float(st).is_integer(), "strategy_params.variables.slippage_ticks must be integer-valued")

    _validate_pattern_registry(result["pattern_registry"])
    if "logic_state" in result:
        _validate_logic_state(result["logic_state"], result["pattern_registry"])
    if "logic_occurrences" in result:
        _validate_logic_occurrences(result["logic_occurrences"], result["pattern_registry"], result["trades"])

    seen_trade_ids = set()
    tick_size = float(constants["tick_size"])
    point_value = float(constants["point_value"])

    for t in result["trades"]:
        _ensure(isinstance(t, dict), "Each trade must be dict.")
        tkeys = set(t.keys())
        miss_t = sorted(REQUIRED_TRADE_KEYS - tkeys)
        _ensure(not miss_t, f"Trade missing required keys: {miss_t}")
        extra_t = sorted(tkeys - REQUIRED_TRADE_KEYS)
        _ensure(not extra_t, f"Trade has forbidden extra keys: {extra_t}")
        _ensure(isinstance(t["meta"], dict), "trade.meta must be dict.")
        _ensure(isinstance(t["id"], int) and t["id"] > 0, "trade.id must be int > 0")
        _ensure(t["id"] not in seen_trade_ids, f"Duplicate trade.id: {t['id']}")
        seen_trade_ids.add(t["id"])
        _ensure(t["side"] in ALLOWED_SIDES, f"trade.side must be one of {sorted(ALLOWED_SIDES)}")
        _ensure(isinstance(t["qty"], (int, float)) and float(t["qty"]) > 0, "trade.qty must be > 0")
        _ensure(isinstance(t["entry_bar"], int) and t["entry_bar"] >= 0, "trade.entry_bar must be int >= 0")
        _ensure(isinstance(t["exit_bar"], int) and t["exit_bar"] >= 0, "trade.exit_bar must be int >= 0")
        _ensure(t["entry_bar"] < t["exit_bar"], "trade.entry_bar must be < trade.exit_bar")
        _ensure(_is_utc_iso_datetime(t["entry_dt"]), "trade.entry_dt must be UTC ISO-8601 string (...Z).")
        _ensure(_is_utc_iso_datetime(t["exit_dt"]), "trade.exit_dt must be UTC ISO-8601 string (...Z).")
        entry_dt = pd.Timestamp(t["entry_dt"]).tz_convert("UTC")
        exit_dt = pd.Timestamp(t["exit_dt"]).tz_convert("UTC")
        _ensure(entry_dt < exit_dt, "trade.entry_dt must be < trade.exit_dt")

        for pk in ["entry_price", "exit_price"]:
            _ensure(isinstance(t[pk], (int, float)) and float(t[pk]) > 0, f"trade.{pk} must be numeric > 0")
        spv = t["stop_price"]
        _ensure((spv is None) or isinstance(spv, (int, float)), "trade.stop_price must be numeric or null")

        for mk in ["mae_ticks", "mfe_ticks", "mae_points", "mfe_points", "mae_ccy_1c", "mfe_ccy_1c"]:
            _ensure(isinstance(t[mk], (int, float)), f"trade.{mk} must be numeric")
            _ensure(float(t[mk]) >= 0, f"trade.{mk} must be >= 0")

        _ensure(_float_close(float(t["mae_points"]), float(t["mae_ticks"]) * tick_size), "MAE identity failed: mae_points != mae_ticks * tick_size")
        _ensure(_float_close(float(t["mfe_points"]), float(t["mfe_ticks"]) * tick_size), "MFE identity failed: mfe_points != mfe_ticks * tick_size")
        _ensure(_float_close(float(t["mae_ccy_1c"]), float(t["mae_points"]) * point_value), "MAE currency identity failed: mae_ccy_1c != mae_points * point_value")
        _ensure(_float_close(float(t["mfe_ccy_1c"]), float(t["mfe_points"]) * point_value), "MFE currency identity failed: mfe_ccy_1c != mfe_points * point_value")

        for pk in ["pnl_gross", "commission", "slippage", "pnl_net"]:
            _ensure(isinstance(t[pk], (int, float)), f"trade.{pk} must be numeric")
        _ensure(float(t["commission"]) >= 0, "trade.commission must be >= 0")
        _ensure(float(t["slippage"]) >= 0, "trade.slippage must be >= 0")

        qty = float(t["qty"])
        commission_expected = float(variables["commission_per_trade"]) * qty
        slippage_expected = float(variables["slippage_ticks"]) * tick_size * point_value * qty
        _ensure(_float_close(float(t["commission"]), commission_expected, tol=1e-6), "commission mismatch")
        _ensure(_float_close(float(t["slippage"]), slippage_expected, tol=1e-6), "slippage mismatch")
        _ensure(_float_close(float(t["pnl_net"]), float(t["pnl_gross"]) - float(t["commission"]) - float(t["slippage"]), tol=1e-6), "pnl_net identity failed")

        events = t["events"]
        _ensure(isinstance(events, list) and events, "trade.events must be non-empty list")
        event_ids = set()
        for e in events:
            _ensure(isinstance(e, dict), "Each event must be dict.")
            ekeys = set(e.keys())
            miss_e = sorted(REQUIRED_EVENT_KEYS - ekeys)
            _ensure(not miss_e, f"Event missing required keys: {miss_e}")
            extra_e = sorted(ekeys - REQUIRED_EVENT_KEYS)
            _ensure(not extra_e, f"Event has forbidden extra keys: {extra_e}")
            _ensure(isinstance(e["meta"], dict), "event.meta must be dict")
            _ensure(isinstance(e["event_id"], str) and e["event_id"].strip(), "event_id must be non-empty string")
            _ensure(e["event_id"] not in event_ids, f"Duplicate event_id in same trade: {e['event_id']}")
            event_ids.add(e["event_id"])
            _ensure(e["event_type"] in ALLOWED_EVENT_TYPES, f"event_type must be one of {sorted(ALLOWED_EVENT_TYPES)}")
            _ensure(isinstance(e["tf"], str) and e["tf"].strip(), "event.tf must be non-empty string")
            _ensure(isinstance(e["cert_tf"], str) and e["cert_tf"].strip(), "event.cert_tf must be non-empty string")
            _ensure(e["tf"] in CANONICAL_TIMEFRAMES, f"event.tf must be canonical token: {e['tf']}")
            _ensure(e["cert_tf"] in CANONICAL_TIMEFRAMES, f"event.cert_tf must be canonical token: {e['cert_tf']}")
            _ensure(_is_utc_iso_datetime(e["start_dt"]), "event.start_dt must be UTC ISO-8601 string (...Z).")
            _ensure(_is_utc_iso_datetime(e["cert_dt"]), "event.cert_dt must be UTC ISO-8601 string (...Z).")
            sdt = pd.Timestamp(e["start_dt"]).tz_convert("UTC")
            cdt = pd.Timestamp(e["cert_dt"]).tz_convert("UTC")
            _ensure(sdt <= cdt, "event.start_dt must be <= event.cert_dt")
            _ensure(isinstance(e["start_price"], (int, float)) and float(e["start_price"]) > 0, "event.start_price must be > 0")
            _ensure(isinstance(e["cert_price"], (int, float)) and float(e["cert_price"]) > 0, "event.cert_price must be > 0")

        pattern_events = [e for e in events if e["event_type"] == "PATTERN"]
        entry_events = [e for e in events if e["event_type"] == "ENTRY"]
        exit_events = [e for e in events if e["event_type"] == "EXIT"]
        condition_events = [e for e in events if e["event_type"] == "CONDITION"]
        _ensure(pattern_events, "Trade must include at least one PATTERN event.")
        _ensure(entry_events, "Trade must include at least one ENTRY event.")
        _ensure(exit_events, "Trade must include at least one EXIT event.")
        _ensure(condition_events, "Trade must include at least one CONDITION event.")
        _ensure(all(e["tf"] == "EXEC" for e in entry_events), "All ENTRY events must use tf=EXEC")
        _ensure(all(e["tf"] == "EXEC" for e in exit_events), "All EXIT events must use tf=EXEC")

        p_last = max(_event_runtime_dt(e) for e in pattern_events)
        e_first = min(pd.Timestamp(e["cert_dt"]).tz_convert("UTC") for e in entry_events)
        x_first = min(pd.Timestamp(e["cert_dt"]).tz_convert("UTC") for e in exit_events)
        _ensure(p_last <= e_first, "PATTERN runtime/cert anchor must be <= ENTRY cert_dt")
        _ensure(e_first <= x_first, "ENTRY cert_dt must be <= EXIT cert_dt")

        _crosscheck_events_vs_registry(events, result["pattern_registry"])


def _validate_pattern_registry(reg: Dict[str, Any]) -> None:
    _ensure(isinstance(reg, dict), "pattern_registry must be dict")
    keys = set(reg.keys())
    miss = sorted(REQUIRED_REGISTRY_KEYS - keys)
    _ensure(not miss, f"pattern_registry missing required keys: {miss}")
    extra = sorted(keys - REQUIRED_REGISTRY_KEYS)
    _ensure(not extra, f"pattern_registry has forbidden extra keys: {extra}")
    _ensure(isinstance(reg["registry_version"], str) and reg["registry_version"].strip(), "pattern_registry.registry_version must be non-empty string")
    _ensure(reg["registry_version"] == CONTRACT_VERSION, "pattern_registry.registry_version must match contract version")
    _ensure(isinstance(reg["parameters"], dict), "pattern_registry.parameters must be dict")
    _ensure(isinstance(reg["patterns"], list), "pattern_registry.patterns must be list")
    _ensure(isinstance(reg["meta"], dict), "pattern_registry.meta must be dict")

    reg_param_keys = set(reg["parameters"].keys())
    miss_reg_params = sorted(REQUIRED_REGISTRY_PARAMETER_KEYS - reg_param_keys)
    _ensure(not miss_reg_params, f"pattern_registry.parameters missing keys: {miss_reg_params}")

    for p in reg["patterns"]:
        _ensure(isinstance(p, dict), "Each pattern must be dict")
        pkeys = set(p.keys())
        miss_p = sorted(REQUIRED_PATTERN_KEYS - pkeys)
        _ensure(not miss_p, f"pattern missing keys: {miss_p}")
        extra_p = sorted(pkeys - REQUIRED_PATTERN_KEYS)
        _ensure(not extra_p, f"pattern has extra keys: {extra_p}")
        _ensure(isinstance(p["pattern_id"], str) and p["pattern_id"].strip(), "pattern.pattern_id must be non-empty string")
        _ensure(isinstance(p["pattern_symbol"], str) and p["pattern_symbol"].strip(), "pattern.pattern_symbol must be non-empty string")
        _ensure(isinstance(p["title"], str), "pattern.title must be string")
        _ensure(isinstance(p["summary"], str), "pattern.summary must be string")
        _ensure(isinstance(p["completion_rule"], str) and p["completion_rule"].strip(), "pattern.completion_rule must be non-empty string")
        _ensure(isinstance(p["conditions"], list), "pattern.conditions must be list")
        _ensure(isinstance(p["entries"], list), "pattern.entries must be list")
        _ensure(isinstance(p["exits"], list), "pattern.exits must be list")
        _ensure(isinstance(p["pattern_tf"], str) and p["pattern_tf"] in DB_TIMEFRAMES, "pattern.pattern_tf must be canonical DB timeframe")
        _ensure(isinstance(p["mre"], str) and p["mre"] in DB_TIMEFRAMES, "pattern.mre must be canonical DB timeframe")

        condition_tfs = []
        for c in p["conditions"]:
            _ensure(isinstance(c, dict), "condition must be dict")
            ckeys = set(c.keys())
            miss_c = sorted(REQUIRED_CONDITION_KEYS - ckeys)
            _ensure(not miss_c, f"condition missing keys: {miss_c}")
            extra_c = sorted(ckeys - REQUIRED_CONDITION_KEYS)
            _ensure(not extra_c, f"condition has extra keys: {extra_c}")
            _ensure(isinstance(c["condition_id"], str) and c["condition_id"].strip(), "condition_id must be non-empty string")
            _ensure(isinstance(c["tf"], str) and c["tf"] in DB_TIMEFRAMES, f"condition.tf must be canonical DB timeframe: {c.get('tf')}")
            condition_tfs.append(c["tf"])

        _ensure(bool(condition_tfs), f"pattern {p['pattern_id']} must contain at least one condition")
        derived_pattern_tf = min(condition_tfs, key=_timeframe_rank)
        _ensure(p["pattern_tf"] == derived_pattern_tf, f"pattern_tf must match smallest condition tf for pattern {p['pattern_id']}")
        _ensure(
            _timeframe_rank(p["mre"]) >= _timeframe_rank(p["pattern_tf"]),
            f"pattern.mre cannot be more coarse than pattern_tf for pattern {p['pattern_id']}",
        )

        for e in p["entries"]:
            _ensure(isinstance(e, dict), "entry must be dict")
            ekeys = set(e.keys())
            miss_e = sorted(REQUIRED_ENTRY_KEYS - ekeys)
            _ensure(not miss_e, f"entry missing keys: {miss_e}")
            extra_e = sorted(ekeys - REQUIRED_ENTRY_KEYS)
            _ensure(not extra_e, f"entry has extra keys: {extra_e}")
            _ensure(isinstance(e["entry_id"], str) and e["entry_id"].strip(), "entry_id must be non-empty string")
            _ensure(e["tf"] == "EXEC", f"entry.tf must be EXEC for entry {e['entry_id']}")

        for x in p["exits"]:
            _ensure(isinstance(x, dict), "exit must be dict")
            xkeys = set(x.keys())
            miss_x = sorted(REQUIRED_EXIT_KEYS - xkeys)
            _ensure(not miss_x, f"exit missing keys: {miss_x}")
            extra_x = sorted(xkeys - REQUIRED_EXIT_KEYS)
            _ensure(not extra_x, f"exit has extra keys: {extra_x}")
            _ensure(isinstance(x["exit_id"], str) and x["exit_id"].strip(), "exit_id must be non-empty string")
            _ensure(x["tf"] == "EXEC", f"exit.tf must be EXEC for exit {x['exit_id']}")


def _validate_logic_state(logic_state: Dict[str, Any], reg: Dict[str, Any]) -> None:
    _ensure(isinstance(logic_state, dict), "logic_state must be dict when present")
    keys = set(logic_state.keys())
    missing = sorted(REQUIRED_LOGIC_STATE_KEYS - keys)
    _ensure(not missing, f"logic_state missing required keys: {missing}")
    extra = sorted(keys - REQUIRED_LOGIC_STATE_KEYS)
    _ensure(not extra, f"logic_state has forbidden extra keys: {extra}")
    _ensure(
        logic_state["schema_version"] == LOGIC_STATE_SCHEMA_VERSION,
        f"logic_state.schema_version must be {LOGIC_STATE_SCHEMA_VERSION!r}",
    )
    _ensure(isinstance(logic_state["series"], list), "logic_state.series must be list")
    _ensure(isinstance(logic_state["meta"], dict), "logic_state.meta must be dict")

    pattern_ids = set()
    condition_ids = set()
    for pattern in reg.get("patterns", []):
        pattern_ids.add(pattern["pattern_id"])
        for condition in pattern.get("conditions", []):
            condition_ids.add(condition["condition_id"])

    seen_state_keys: Set[tuple[str, str]] = set()
    for series in logic_state["series"]:
        _ensure(isinstance(series, dict), "Each logic_state series must be dict")
        skeys = set(series.keys())
        miss = sorted(REQUIRED_LOGIC_STATE_SERIES_KEYS - skeys)
        _ensure(not miss, f"logic_state series missing required keys: {miss}")
        extra = sorted(skeys - REQUIRED_LOGIC_STATE_SERIES_KEYS)
        _ensure(not extra, f"logic_state series has forbidden extra keys: {extra}")
        _ensure(isinstance(series["state_id"], str) and series["state_id"].strip(), "logic_state.state_id must be non-empty string")
        _ensure(series["state_type"] in ALLOWED_LOGIC_STATE_TYPES, f"logic_state.state_type invalid: {series['state_type']!r}")
        _ensure(series["state_tf"] in CANONICAL_TIMEFRAMES, f"logic_state.state_tf must be canonical token: {series['state_tf']!r}")
        _ensure(
            isinstance(series["visual_preset"], str) and series["visual_preset"] in ALLOWED_VISUAL_PRESETS,
            f"logic_state.visual_preset invalid: {series['visual_preset']!r}",
        )
        _ensure(isinstance(series["points"], list), "logic_state.points must be list")
        _ensure(isinstance(series["meta"], dict), "logic_state.series.meta must be dict")

        state_key = (str(series["state_type"]), str(series["state_id"]))
        _ensure(state_key not in seen_state_keys, f"Duplicate logic_state series for {state_key}")
        seen_state_keys.add(state_key)

        if series["state_type"] == "CONDITION":
            _ensure(series["state_id"] in condition_ids, f"logic_state CONDITION state_id not in pattern_registry.conditions: {series['state_id']}")
        elif series["state_type"] == "PATTERN":
            _ensure(series["state_id"] in pattern_ids, f"logic_state PATTERN state_id not in pattern_registry.patterns: {series['state_id']}")

        prev_dt: Optional[pd.Timestamp] = None
        for point in series["points"]:
            _ensure(isinstance(point, dict), "Each logic_state point must be dict")
            pkeys = set(point.keys())
            miss_point = sorted(REQUIRED_LOGIC_STATE_POINT_KEYS - pkeys)
            _ensure(not miss_point, f"logic_state point missing required keys: {miss_point}")
            extra_point = sorted(pkeys - REQUIRED_LOGIC_STATE_POINT_KEYS)
            _ensure(not extra_point, f"logic_state point has forbidden extra keys: {extra_point}")
            _ensure(_is_utc_iso_datetime(point["dt"]), "logic_state point.dt must be UTC ISO-8601 string (...Z)")
            _ensure(isinstance(point["value"], bool), "logic_state point.value must be boolean")
            current_dt = pd.Timestamp(point["dt"]).tz_convert("UTC")
            if prev_dt is not None:
                _ensure(current_dt > prev_dt, "logic_state points must be strictly increasing by dt")
            prev_dt = current_dt


def _validate_logic_occurrences(
    logic_occurrences: Dict[str, Any],
    reg: Dict[str, Any],
    trades: list[Dict[str, Any]],
) -> None:
    _ensure(isinstance(logic_occurrences, dict), "logic_occurrences must be dict when present")
    keys = set(logic_occurrences.keys())
    missing = sorted(REQUIRED_LOGIC_OCCURRENCES_KEYS - keys)
    _ensure(not missing, f"logic_occurrences missing required keys: {missing}")
    extra = sorted(keys - REQUIRED_LOGIC_OCCURRENCES_KEYS)
    _ensure(not extra, f"logic_occurrences has forbidden extra keys: {extra}")
    _ensure(
        logic_occurrences["schema_version"] == LOGIC_OCCURRENCES_SCHEMA_VERSION,
        f"logic_occurrences.schema_version must be {LOGIC_OCCURRENCES_SCHEMA_VERSION!r}",
    )
    _ensure(isinstance(logic_occurrences["items"], list), "logic_occurrences.items must be list")
    _ensure(isinstance(logic_occurrences["meta"], dict), "logic_occurrences.meta must be dict")

    pattern_ids = set()
    condition_ids = set()
    entry_ids = set()
    exit_ids = set()
    for pattern in reg.get("patterns", []):
        pattern_ids.add(pattern["pattern_id"])
        for condition in pattern.get("conditions", []):
            condition_ids.add(condition["condition_id"])
        for entry in pattern.get("entries", []):
            entry_ids.add(entry["entry_id"])
        for exit_ in pattern.get("exits", []):
            exit_ids.add(exit_["exit_id"])

    trade_ids = {int(t["id"]) for t in trades}
    seen_occurrence_ids: Set[str] = set()

    for item in logic_occurrences["items"]:
        _ensure(isinstance(item, dict), "Each logic_occurrences item must be dict")
        ikeys = set(item.keys())
        miss = sorted(REQUIRED_LOGIC_OCCURRENCE_ITEM_KEYS - ikeys)
        _ensure(not miss, f"logic_occurrence item missing required keys: {miss}")
        extra = sorted(ikeys - REQUIRED_LOGIC_OCCURRENCE_ITEM_KEYS)
        _ensure(not extra, f"logic_occurrence item has forbidden extra keys: {extra}")

        occurrence_id = item["occurrence_id"]
        _ensure(isinstance(occurrence_id, str) and occurrence_id.strip(), "logic_occurrence.occurrence_id must be non-empty string")
        _ensure(occurrence_id not in seen_occurrence_ids, f"Duplicate logic_occurrence.occurrence_id: {occurrence_id!r}")
        seen_occurrence_ids.add(occurrence_id)

        entity_type = item["entity_type"]
        entity_id = item["entity_id"]
        _ensure(entity_type in ALLOWED_LOGIC_OCCURRENCE_TYPES, f"logic_occurrence.entity_type invalid: {entity_type!r}")
        _ensure(isinstance(entity_id, str) and entity_id.strip(), "logic_occurrence.entity_id must be non-empty string")
        if entity_type == "CONDITION":
            _ensure(entity_id in condition_ids, f"logic_occurrence.entity_id not found in registry conditions: {entity_id!r}")
        elif entity_type == "PATTERN":
            _ensure(entity_id in pattern_ids, f"logic_occurrence.entity_id not found in registry patterns: {entity_id!r}")
        elif entity_type == "ENTRY":
            _ensure(entity_id in entry_ids, f"logic_occurrence.entity_id not found in registry entries: {entity_id!r}")
        elif entity_type == "EXIT":
            _ensure(entity_id in exit_ids, f"logic_occurrence.entity_id not found in registry exits: {entity_id!r}")

        _ensure(isinstance(item["logic_label_short"], str) and item["logic_label_short"].strip(), "logic_occurrence.logic_label_short must be non-empty string")
        _ensure(isinstance(item["logic_label_long"], str) and item["logic_label_long"].strip(), "logic_occurrence.logic_label_long must be non-empty string")
        _ensure(item["semantic_role"] in ALLOWED_LOGIC_OCCURRENCE_ROLES, f"logic_occurrence.semantic_role invalid: {item['semantic_role']!r}")
        _ensure(item["status"] in ALLOWED_LOGIC_OCCURRENCE_STATUS, f"logic_occurrence.status invalid: {item['status']!r}")
        _ensure(item["occurrence_tf"] in CANONICAL_TIMEFRAMES, f"logic_occurrence.occurrence_tf must be canonical token: {item['occurrence_tf']!r}")
        _ensure(_is_utc_iso_datetime(item["start_dt"]), f"logic_occurrence.start_dt must be UTC ISO-8601 (...Z): {item['start_dt']!r}")
        _ensure(_is_utc_iso_datetime(item["cert_dt"]), f"logic_occurrence.cert_dt must be UTC ISO-8601 (...Z): {item['cert_dt']!r}")
        _ensure(isinstance(item["start_price"], (int, float)), "logic_occurrence.start_price must be numeric")
        _ensure(isinstance(item["cert_price"], (int, float)), "logic_occurrence.cert_price must be numeric")
        _ensure(isinstance(item["depends_on_occurrence_ids"], list), "logic_occurrence.depends_on_occurrence_ids must be list")
        _ensure(isinstance(item["used_by_trade_ids"], list), "logic_occurrence.used_by_trade_ids must be list")
        _ensure(isinstance(item["meta"], dict), "logic_occurrence.meta must be dict")
        start_dt = pd.Timestamp(item["start_dt"]).tz_convert("UTC")
        cert_dt = pd.Timestamp(item["cert_dt"]).tz_convert("UTC")
        _ensure(start_dt <= cert_dt, "logic_occurrence.start_dt must be <= cert_dt")

        for dep_id in item["depends_on_occurrence_ids"]:
            _ensure(isinstance(dep_id, str) and dep_id.strip(), "logic_occurrence.depends_on_occurrence_ids items must be non-empty strings")
        for trade_id in item["used_by_trade_ids"]:
            _ensure(isinstance(trade_id, int), "logic_occurrence.used_by_trade_ids items must be int")
            _ensure(trade_id in trade_ids, f"logic_occurrence.used_by_trade_ids references unknown trade.id: {trade_id}")

    all_occurrence_ids = {item["occurrence_id"] for item in logic_occurrences["items"]}
    for item in logic_occurrences["items"]:
        for dep_id in item["depends_on_occurrence_ids"]:
            _ensure(dep_id in all_occurrence_ids, f"logic_occurrence depends on unknown occurrence_id: {dep_id!r}")


def _timeframe_rank(tf: str) -> int:
    order = ["Y", "M", "W", "D", "H6", "H4", "H2", "H1", "M30", "M15", "M10", "M5", "M3", "M1", "S1"]
    token = str(tf or "").upper()
    _ensure(token in order, f"Unsupported timeframe rank token: {tf!r}")
    return order.index(token)


def _crosscheck_events_vs_registry(events: list, reg: Dict[str, Any]) -> None:
    pattern_ids = set()
    condition_ids = set()
    entry_ids = set()
    exit_ids = set()
    for p in reg.get("patterns", []):
        pattern_ids.add(p["pattern_id"])
        for c in p["conditions"]:
            condition_ids.add(c["condition_id"])
        for e in p["entries"]:
            entry_ids.add(e["entry_id"])
        for x in p["exits"]:
            exit_ids.add(x["exit_id"])

    for ev in events:
        et = ev["event_type"]
        eid = ev["event_id"]
        if et == "PATTERN":
            _ensure(eid in pattern_ids, f"Event id not found in pattern_registry.pattern_id: {eid}")
        elif et == "CONDITION":
            _ensure(eid in condition_ids, f"Event id not found in pattern_registry.condition_id: {eid}")
        elif et == "ENTRY":
            _ensure(eid in entry_ids, f"Event id not found in pattern_registry.entry_id: {eid}")
        elif et == "EXIT":
            _ensure(eid in exit_ids, f"Event id not found in pattern_registry.exit_id: {eid}")


def validate_output_with_data(
    result: Dict[str, Any],
    df_exec: pd.DataFrame,
    pattern_contexts: Optional[Dict[str, Dict[str, Any]]] = None,
) -> None:
    """Strict validation + deep price validation using df_exec and pattern_contexts."""
    validate_output_strict(result)
    _ensure(df_exec is not None and not df_exec.empty, "df_exec must not be empty for deep validation.")

    d = df_exec.copy()
    d["__dt_utc__"] = _df_dt_to_utc_series(d)
    d["close"] = pd.to_numeric(d["close"], errors="coerce")
    d = d.dropna(subset=["__dt_utc__", "close"])
    exec_close = {pd.Timestamp(x): float(c) for x, c in zip(d["__dt_utc__"], d["close"])}
    exec_bar_dur = _parse_tf_to_offset(result["timeframe"])

    p_close: Dict[str, Dict[pd.Timestamp, float]] = {}
    for _, context in (pattern_contexts or {}).items():
        if not isinstance(context, dict):
            continue
        dfs_by_tf = context.get("dfs_by_tf")
        if not isinstance(dfs_by_tf, dict):
            continue
        for tf, dfp in dfs_by_tf.items():
            tf_token = str(tf)
            if tf_token in p_close:
                continue
            _ensure(dfp is not None and not dfp.empty, f"pattern_contexts dfs_by_tf[{tf_token}] must not be empty.")
            hp = dfp.copy()
            hp["__dt_utc__"] = _df_dt_to_utc_series(hp)
            hp["close"] = pd.to_numeric(hp["close"], errors="coerce")
            hp = hp.dropna(subset=["__dt_utc__", "close"])
            p_close[tf_token] = {pd.Timestamp(x): float(c) for x, c in zip(hp["__dt_utc__"], hp["close"])}

    for t in result["trades"]:
        for e in t["events"]:
            sdt = pd.Timestamp(e["start_dt"]).tz_convert("UTC")
            sp = float(e["start_price"])
            tf = str(e["tf"])
            if tf != "EXEC" and tf in p_close:
                tf_dur = _parse_tf_to_offset(tf)
                expected_sp = _lookup_close_for_event(e, p_close[tf], sdt, tf_dur, f"start_dt tf={tf}")
            else:
                expected_sp = _lookup_close_at_event_time(exec_close, sdt, exec_bar_dur, "start_dt execution")
            _ensure(_float_close(expected_sp, sp, tol=1e-6), f"start_price mismatch tf={tf} dt={sdt}")

            cdt = pd.Timestamp(e["cert_dt"]).tz_convert("UTC")
            cp = float(e["cert_price"])
            ctf = str(e["cert_tf"])
            if ctf != "EXEC" and ctf in p_close:
                ctf_dur = _parse_tf_to_offset(ctf)
                expected_cp = _lookup_close_for_event(e, p_close[ctf], cdt, ctf_dur, f"cert_dt tf={ctf}")
            else:
                expected_cp = _lookup_close_at_event_time(exec_close, cdt, exec_bar_dur, "cert_dt execution")
            _ensure(_float_close(expected_cp, cp, tol=1e-6), f"cert_price mismatch tf={ctf} dt={cdt}")

B7_metrics_spec.txt

Download
============================================================
ALGO - DOCUMENT HEADER
------------------------------------------------------------
Doc ID       : B7
Title        : CORE - METRICS SPEC (CORE-ONLY / Fonte di verita' numerica)
Filename     : B7 - CORE - METRICS SPEC (CORE-ONLY) - V06.txt
Version      : V06
Updated      : 2026-04-25 16:35 (Europe/Rome)
Status       : ACTIVE (CORE-ONLY)
Changelog    :
- V06: chiarito che identificatori umani persistiti come `run_seq` / `trade_seq` hanno valore di audit/UI identity e non alterano la numerica CORE delle metriche.
- V05: canonizzate account_size_required / recommended su base realized-path MAE + max_dd_tctc.
- V04: allineato il lessico a EXEC_TF e MRE.
- V04: esplicitato che i trades validati possono provenire da Pattern multi-market sincronizzati via dt.
============================================================

B7 - CORE - METRICS SPEC (CORE-ONLY / Fonte di verita' numerica)
Versione: V06
Status  : ACTIVE (CORE-ONLY)

============================================================
SCOPO
============================================================
- Definire le metriche di performance/rischio calcolate dal CORE ALGO.
- Stabilire la fonte di verita' numerica dopo la validazione B6.

============================================================
PRINCIPIO FONDAMENTALE
============================================================
Tutte le metriche in ALGO:
- sono calcolate DOPO la validazione B6
- usano solo trades validati e metadata canonici
- non dipendono da logica analytics calcolata nell'EA

============================================================
DATI DI INPUT PER LE METRICHE
============================================================
Il CORE usa esclusivamente:
- lista trades validati
- strategy_params.variables.initial_capital
- contract_specs del simbolo quando servono informazioni contrattuali/currency
- timeline dei trades

Regole:
- il timeframe top-level del trade output rappresenta EXEC_TF del run
- eventuali Pattern multi-market sono gia' stati sincronizzati a livello EA/runtime tramite dt
- il CORE non ricostruisce la logica dei Pattern
- eventuali identificatori umani persistiti come `run_seq` o `trade_seq` hanno valore di audit/UI identity e non modificano la numerica CORE delle metriche

============================================================
PNL & COSTI
============================================================
Per ogni trade i:
- pnl_gross_i
- commission_i
- slippage_i
- pnl_net_i

Identity check:
pnl_net_i = pnl_gross_i - commission_i - slippage_i

============================================================
EQUITY CURVE
============================================================
E(0) = initial_capital
E(exit_dt_i) = E(exit_dt_{i-1}) + pnl_net_i

============================================================
HOLDING TIME (CANONICO)
============================================================
Fonte canonica:
- holding time per trade = exit_dt - entry_dt

Regole:
- bars held devono essere calcolati da entry_bar / exit_bar sul dataset EXEC del run
- time held e bars held devono risultare coerenti con EXEC_TF

============================================================
MAE / MFE E CURRENCY GOVERNANCE
============================================================
Misure canoniche in output EA:
- mae_ticks, mfe_ticks
- mae_points, mfe_points
- mae_ccy_1c, mfe_ccy_1c

Regole:
- *_ccy_1c e' sempre per 1 contratto
- eventuali totali di posizione sono derivati dal CORE:
  - *_ccy_total = *_ccy_1c * qty

============================================================
RETURNS, PERFORMANCE E RISCHIO
============================================================
Il CORE calcola in modo deterministico:
- Net Profit
- Gross Profit / Gross Loss
- Profit Factor
- Average Trade
- Win Rate
- Avg Win / Avg Loss
- Expectancy se prevista
- CAGR se definito per il periodo
- periodic average returns quando implementate e versionate:
  - daily
  - weekly
  - monthly
  - yearly
- dispersione trade-level quando implementata e versionata:
  - std dev trade profit
- Max Drawdown
- Drawdown duration
- Sharpe / Sortino / altre metriche se implementate e versionate

============================================================
ACCOUNT SIZE / CAPITAL ADEQUACY
============================================================
Metriche canoniche:
- account_size_required
- account_size_recommended
- return_on_account_required
- return_on_account_recommended

Definizioni:
- per ogni trade i:
  - pnl_before_i = somma dei pnl_net_j per tutti i trade chiusi con j < i
  - mae_total_i = abs(qty_i) * mae_ccy_1c_i
  - capital_required_candidate_i = max(0, mae_total_i - pnl_before_i)
- account_size_required = max(capital_required_candidate_i) su tutti i trade

Interpretazione:
- account_size_required = capitale iniziale minimo necessario per riprodurre il path storico realizzato
  senza che l'open equity vada sotto zero durante la peggiore escursione avversa intratrade.
- la metrica usa il path reale dei trade e le MAE canoniche validate.
- la metrica NON usa il nozionale del contratto e NON usa stime implicite di broker margin.

Buffer raccomandato:
- max_dd_tctc = max drawdown trade-close-to-trade-close
- account_size_recommended = account_size_required + abs(max_dd_tctc)

Returns collegati:
- return_on_account_required = net_profit / account_size_required
- return_on_account_recommended = net_profit / account_size_recommended

Note:
- eventuali metriche di esposizione nozionale o proxy di costo posizione non definiscono account size.
- eventuali metriche broker-margin richiedono contract_specs espliciti dedicati e non vanno inferite.

============================================================
FINE B7
============================================================

B8_ea_generation_master_specs.txt

Download
============================================================
ALGO - DOCUMENT HEADER
------------------------------------------------------------
Doc ID       : B8
Title        : EA Generation - Master Specs
Filename     : B8 - EA Generation - Master Specs - V03.txt
Version      : V18
Updated      : 2026-04-25 16:35 (Europe/Rome)
Status       : ACTIVE (CANONICAL / AI-READY)
Scope        : SINGLE-SOURCE DOCUMENT FOR AI-DRIVEN EA GENERATION
Changelog    :
- V18: formalizzato che i setup che dipendono da anchor/occurrences selezionate devono emettere `logic_occurrences`; chiarito il collegamento tra occurrence, trade.id e trade_seq.
- V17: formalizzato che, quando un evento logico mantiene il proprio timestamp di certificazione sul tf logico ma richiede anche un anchor runtime distinto, la AI deve esporre tale anchor in `event.meta` in modo esplicito.
- V16: resa obbligatoria nella scheda pre-registrazione una tabella riassuntiva canonica in Markdown; quando il Markdown non e' abbastanza leggibile, va prodotta anche una versione `.txt` monospaziata human-readable coerente con la stessa semantica.
- V15: resa obbligatoria la scheda EA pre-registrazione con onset_dt/cert_dt e preset di stato visuale per Conditions/Pattern/Entry/Exit.
- V14: introdotta l'istruzione alla AI di utilizzare nativamente il "Linear Pointer Engine" (O(N+M)) per il mapping logico->EXEC in tutti i nuovi EA, mantenendo la conformità semantica col Full Scan.
- V13: vietato generare mapping logical->EXEC con exact-match dell'anchor; la AI deve usare latest EXEC close anchor <= logical close anchor e il CORE puo' rifiutare output/script non conformi.
- V12: formalizzato che se il logical close anchor precede il primo close disponibile del dataset EXEC caricato nel run, la AI deve trattarlo come bootstrap/range truncation e saltare il segnale.
- V11: chiarito esplicitamente che su dataset BAR_START la AI non puo' usare il `dt` grezzo della barra logica come close anchor per `cert_dt` o per il mapping verso EXEC.
- V10: formalizzato che le strategie multi-underlying devono essere modellate con pattern_context separati per simbolo; vietato comprimere conferme esterne in colonne extra del dataframe principale.
- V09: formalizzato che il PATTERN_REGISTRY deve essere staticamente consumabile dagli strumenti ALGO; ammessi riferimenti a costanti top-level staticamente risolvibili, vietata costruzione dinamica.
============================================================

B8 - EA Generation - Master Specs
Versione: V18
Status  : ACTIVE (CANONICAL)

...

============================================================
9) DEFAULT CANONICI DI TRADUZIONE STRATEGICA
============================================================
Se il brief non specifica diversamente, la AI DEV deve usare questi default:

...

- MRE e Performance:
  - MRE indica il minimo EXEC ammesso.
  - Per garantire scalabilita' su timeframe intraday (H4, H1, etc.), la AI deve implementare il mapping logico->EXEC utilizzando il "Linear Pointer Engine" (complessita' O(N+M)) come mostrato nel template B5.
  - L'uso di scansioni complete (Full Scan O(N*M)) e' sconsigliato per la produzione operativa.
  - Il mapping deve rispettare la regola: "latest EXEC close anchor <= logical close anchor".
  - La AI non deve mai usare exact-match dell'anchor logico.
  - Se il dataset logico e' etichettato `BAR_START`, la AI deve prima derivare il close anchor canonico.
  - Se il logical close anchor cade prima del primo close disponibile nel dataset EXEC, la AI deve saltare il segnale (bootstrap/truncation).

- Scheda canonica pre-registrazione:
  - Prima di proporre la registrazione ufficiale di un nuovo EA, la AI deve produrre una scheda completa per ogni `Condition`, `Pattern`, `Entry` ed `Exit`.
  - Per ogni entita' la scheda deve dichiarare almeno: `significato operativo`, `onset_dt`, `cert_dt`, `onset_tf`, `cert_tf`, `preset di stato visuale logic-indicator`.
  - La scheda deve contenere obbligatoriamente una tabella riassuntiva canonica in Markdown che permetta di confrontare in modo rapido tutte le entita' principali.
  - La tabella riassuntiva deve distinguere chiaramente almeno: `entita'`, `categoria`, `significato operativo`, `tf logico reale`, `tf payload runtime corrente`, `motivo di eventuali differenze`, `riferimenti semantici salvati nei meta` se presenti, `preset di stato visuale`.
  - Se la tabella Markdown risulta poco leggibile per numero di colonne o lunghezza del testo, la AI deve produrre anche una copia `.txt` monospaziata, ottimizzata per l'occhio umano, semanticamente coerente con la versione Markdown.
  - Nel payload runtime corrente, `start_dt` equivale semanticamente a `onset_dt`.
  - Se `start_dt/cert_dt` restano sul tf logico ma la piattaforma richiede anche un anchor operativo distinto, la AI deve esporre quell'anchor in `event.meta` con nomi espliciti e auditabili, per esempio `runtime_anchor_dt`, `runtime_anchor_price`, `logical_bar_start_dt`.
  - Se la leggibilita' del setup dipende dal distinguere occorrenze concrete della stessa entita' astratta, la AI deve emettere anche il blocco `logic_occurrences`.
  - `logic_occurrences` deve modellare almeno: `occurrence_id`, `entity_id`, `entity_type`, label corte/lunghe, ruolo semantico, stato lifecycle, dipendenze da altre occorrenze e collegamento ai trade.
  - La AI non deve hardcodare il modello su nomi strategy-specific come `SL1/SL2`: deve usare un lessico generalizzabile di occurrence, anchor, confirmation, gate, selected, superseded, invalidated.
  - Se l'utente deve poter capire quale occorrenza ha generato il trade mostrato nella colonna `#`, la AI deve prevedere il collegamento esplicito tra occurrence e trade tramite `trade.id` nel payload e `trade_seq` quando tale identita' umana e' disponibile lato persistenza/UI.
  - Il preset di stato visuale deve essere scelto tra:
    - `event_only`
    - `until_cert`
    - `until_entry`
    - `until_exit`
    - `while_setup_active`
    - `while_position_open`
    - `sticky_historical`
    - `custom_rule`
  - Se il caso richiede `custom_rule`, la AI deve descrivere esplicitamente la regola di spegnimento (`becomes empty when ...`).

... (resto del file invariato)

B9_external_ai_dev_handoff_pack.txt

Download
============================================================
ALGO - DOCUMENT HEADER
------------------------------------------------------------
Doc ID       : B9
Title        : EA - External AI Dev Handoff Pack
Filename     : B9 - EA - External AI Dev Handoff Pack - V01.txt
Version      : V06
Updated      : 2026-04-20 19:45 (Europe/Rome)
Status       : ACTIVE (CANONICAL / PROMPT-READY)
Scope        : READY-TO-USE PROMPT FOR EXTERNAL AI AGENTS
Changelog    :
- V06: il prompt canonico richiede ora che gli eventi logici con anchor operativo distinto espongano quell'anchor in `event.meta` in modo esplicito e auditabile.
- V05: il prompt canonico richiede ora che la scheda pre-registrazione includa una tabella riassuntiva canonica in Markdown e, quando necessario per leggibilita', anche una copia `.txt` monospaziata human-readable.
- V04: il prompt canonico richiede ora la scheda EA pre-registrazione con onset_dt/cert_dt e preset di stato visuale per Conditions/Pattern/Entry/Exit.
- V03: aggiornato il prompt universale con l'obbligo di implementare il "Linear Pointer Engine" (O(N+M)) per il mapping logico->EXEC, garantendo scalabilita' intraday.
- V02: aggiunto il principio che questo prompt fa parte del corpus canonico.
- V01: creazione del pacchetto di handoff iniziale.
============================================================

B9 - EA - External AI Dev Handoff Pack
Versione: V06
Status  : ACTIVE (CANONICAL)

...

============================================================
3) CANONICAL PROMPT (READY-TO-USE)
============================================================
Copia e incolla il testo seguente per istruire una AI DEV esterna:

--- INIZIO PROMPT ---
Sei una AI esperta in Python e Algorithmic Trading per il sistema ALGO.
Il tuo compito e' scrivere un Expert Advisor (EA) seguendo rigorosamente le Canonical Specs.

REGOLE HARD-LAW:
1. Usa B3 come contratto di output (JSON-compatible dict).
2. Usa B5 (Template V08) come base per il codice.
3. PERFORMANCE: Per il mapping tra barra logica (Pattern) e barra operativa (EXEC), devi implementare obbligatoriamente un "Linear Pointer Engine" (complessita' ammortizzata O(N+M)) come mostrato nel B5. Evita scansioni complete O(N*M) che rallentano i dataset intraday.
4. MAPPING: Il criterio deve essere "latest EXEC close anchor <= logical close anchor". Mai usare exact-match su ancore temporali.
5. BOOTSTRAP: Se il logical close anchor cade prima del primo dato EXEC disponibile, quel segnale va saltato (bootstrap/range truncation).
6. AI_DESCRIPTION: Deve essere strutturata con tabelle Variables/Constants e sezione ChartTrade Semantics dettagliata.
7. PATTERN_REGISTRY: Deve essere staticamente ispezionabile (no dynamic construction).
8. NO CORE METRICS: Non calcolare equity, drawdown o win rate. L'EA produce solo trades chiusi + events.
9. Prima di proporre la registrazione ufficiale, devi produrre una scheda EA completa per ogni Condition / Pattern / Entry / Exit con: significato operativo, onset_dt, cert_dt, onset_tf, cert_tf e preset di stato visuale logic-indicator.
10. La scheda deve includere obbligatoriamente una tabella riassuntiva canonica in Markdown che confronti in modo chiaro tutte le entita' rilevanti.
11. La tabella deve distinguere almeno: entita', categoria, significato operativo, tf logico reale, tf payload runtime corrente, motivo di eventuali differenze e preset di stato visuale.
12. Se la tabella Markdown non e' abbastanza leggibile per densita' o larghezza, devi produrre anche una copia `.txt` monospaziata, human-readable, coerente con la stessa semantica.
13. Nel payload runtime corrente, `start_dt` equivale semanticamente a `onset_dt`.
14. Il preset di stato visuale deve essere scelto tra: `event_only`, `until_cert`, `until_entry`, `until_exit`, `while_setup_active`, `while_position_open`, `sticky_historical`, `custom_rule`.
15. Se un evento logico mantiene `cert_dt` sul tf logico ma richiede anche un anchor runtime distinto per cronologia, plotting o fill-mapping, devi esporre quell'anchor in `event.meta` con nomi espliciti come `runtime_anchor_dt`, `runtime_anchor_price`, `logical_bar_start_dt` quando necessario.

... (resto del prompt invariato)
--- FINE PROMPT ---

... (resto del file invariato)

B10_ea_canonical_examples.txt

Download
============================================================
ALGO - DOCUMENT HEADER
------------------------------------------------------------
Doc ID       : B10
Title        : EA Canonical Examples
Filename     : B10 - EA Canonical Examples - V02.txt
Version      : V09
Updated      : 2026-04-25 16:35 (Europe/Rome)
Status       : ACTIVE (CANONICAL / EXAMPLES)
Scope        : WORKED EXAMPLES FOR HUMAN AND EXTERNAL AI DEV
Changelog    :
- V09: aggiunto esempio canonico occurrence-based su doppio swing per chiarire anchor selezionati, provenance e collegamento ai trade.
- V08: aggiunto l'esempio canonico di evento logico con `runtime_anchor_dt` distinto dalla certificazione logica e `logical_bar_start_dt` per dataset `BAR_START`.
- V07: aggiunto l'esempio canonico della tabella riassuntiva pre-registrazione e la regola pratica Markdown + `.txt` human-readable quando la tabella e' troppo densa.
- V06: aggiunto anti-esempio esplicito di mapping logical->EXEC basato su exact-match dell'anchor, vietato dalle canonical e rifiutabile dal CORE.
- V05: aggiunto il principio che il prompt universale di B9 fa parte del corpus canonico e non deve restare in istruzioni esterne o temporanee.
- V04: aggiunto esempio canonico della struttura obbligatoria di AI_DESCRIPTION con tabelle parametri e ChartTrade Semantics.
- V03: aggiunti esempi espliciti su MRE vs EXEC, pattern_symbol runtime-aware, params runtime current-shape e force-exit a fine dati.
- V02: aggiunto esempio esplicito di risposta corretta vs risposta sbagliata di una AI DEV esterna.
============================================================

B10 - EA Canonical Examples
Versione: V09
Status  : ACTIVE (CANONICAL / EXAMPLES)

============================================================
1) SCOPO
============================================================
Questo documento contiene esempi canonici da imitare.
Non sostituisce B2-B9, ma mostra come trasformare una idea discrezionale
in un draft tecnico ALGO-compatible.

============================================================
2) ESEMPIO A - STRATEGIA DAILY SEMPLICE
============================================================
Idea umana:
- Compra il close del lunedi' se:
  - oggi e' lunedi'
  - close di oggi < close di ieri
  - close di ieri < close di due barre fa
- Esci quando close > high[1]

Traduzione corretta:
- Pattern:
  - P1 = Monday two-step selloff reversal
- Conditions:
  - P1C1 = oggi e' lunedi' (tf D)
  - P1C2 = close < close[1] (tf D)
  - P1C3 = close[1] < close[2] (tf D)
- Entry:
  - P1E1 = enter long on close (tf EXEC)
- Exit:
  - P1X1 = close > high[1] (tf EXEC come fill, logica daily)
- Pattern TF:
  - D
- MRE:
  - D

Note corrette:
- il pattern vive su D
- il fill puo' vivere su EXEC
- MRE = D non obbliga EXEC = D; H1/M15 possono essere leciti se il fill e' ancorato correttamente alla giornata daily
- non bisogna inventare D1
- non bisogna trasformare la regola in un indicatore non auditabile

============================================================
3) ESEMPIO B - COSA NON FARE
============================================================
Errore:
- "Se EXEC_TF e' H1 allora ricostruisco io il daily dal dataset H1"

Perche' e' sbagliato:
- viola DB wins
- viola dataset != timeframe
- introduce inference implicita
- rompe il contratto pattern_contexts

Corretto:
- il runtime deve passare all'EA il contesto Pattern con il dataset D gia' risolto

============================================================
3-bis) ESEMPIO B2 - COSA NON FARE SU MRE
============================================================
Errore:
- "La strategia ha pattern_tf = D e MRE = D, quindi faccio `if timeframe != 'D': raise ValueError(...)`"

Perche' e' sbagliato:
- MRE significa minimo EXEC richiesto
- non significa che EXEC debba essere identico al tf logico
- impedisce inutilmente l'uso di EXEC piu' fini compatibili col runtime

Corretto:
- accettare EXEC uguale o piu' fine
- usare il dataset D del Pattern per la logica
- ancorare il fill all'ultimo EXEC bar compatibile con la barra logica del segnale

============================================================
3-ter) ESEMPIO B3 - COSA NON FARE SUL MAPPING LOGICAL -> EXEC
============================================================
Errore:
- `exec_row = exec_by_anchor.get(logical_close_anchor)`
- oppure descrivere il fill come "barra EXEC il cui close anchor corrisponde esattamente al logical close anchor"

Perche' e' sbagliato:
- rompe su EXEC intraday dove il logical close anchor non coincide con nessun close intraday esatto
- rompe su sessioni holiday / overnight / close-anchored
- e' una semantica fragile che il CORE puo' rifiutare in create/update/run

Corretto:
- usare il latest EXEC close anchor `<= logical close anchor`
- se il logical close anchor cade prima del primo EXEC close disponibile, saltare il segnale come bootstrap/range truncation

============================================================
4) ESEMPIO C - SEMANTICA VISIVA CHARTTRADE
============================================================
Caso corretto:
- Condition strutturale visibile solo se aggiunge valore auditabile
- Pattern visibile se aiuta a capire perche' si e' entrati
- Entry ed Exit visibili
- start_dt/cert_dt distinti solo se il fenomeno lo richiede davvero

Caso scorretto:
- emettere condizioni di puro calcolo solo per riempire il pane logic
- plottare marker su punti non corrispondenti al fenomeno reale

============================================================
5) ESEMPIO D - AI_DESCRIPTION PROPOSTA
============================================================
Forma buona:
- spiega idea strategica
- spiega Pattern
- spiega Conditions principali
- include Detailed Condition Semantics con una scheda per ogni condition_id canonico
- spiega entry/exit
- spiega eventuale contesto multi-market
- include tabella Variables
- include tabella Constants
- include ChartTrade Semantics con motivazioni
- non contiene marketing o testo vago

Forma cattiva:
- testo generico
- assenza di tf logici
- assenza di logica entry/exit
- assenza di tabella parametri
- assenza di semantica ChartTrade
- linguaggio non auditabile

============================================================
5-bis) ESEMPIO E - SHAPE CORRETTA DI AI_DESCRIPTION
============================================================
Forma corretta minima:
- `Strategy Overview`
- `Pattern Logic`
- `Detailed Condition Semantics`
- `Variables`
- `Constants`
- `ChartTrade Semantics`
- `Operational Notes`

Esempio corretto di tabella Variables:
| Variable | Meaning | Effect on strategy |
|---|---|---|
| ibs_threshold | Maximum allowed IBS for setup readiness. | Lower values make entries rarer and more selective. |

Esempio corretto di tabella Constants:
| Constant | Meaning | Effect on execution / PnL |
|---|---|---|
| point_value | Monetary value of one point. | Scales gross PnL and MAE/MFE currency values. |

Esempio corretto di ChartTrade Semantics:
- Visible events: `P1`, `P1E1`, `P1X1`
- Non-visible events: `P1C1`, `P1C2`
- Why visible: pattern explains setup; entry/exit explain execution
- Anchor: pattern on logical/EXEC mapping agreed in design; fills on EXEC
- start/cert: start is the first valid onset, cert is the canonical confirmation

============================================================
5-ter) ESEMPIO E2 - TABELLA RIASSUNTIVA PRE-REGISTRAZIONE
============================================================

Forma corretta minima della tabella:
- deve stare nella scheda principale in Markdown
- deve essere leggibile e confrontabile a colpo d'occhio
- se il Markdown e' troppo largo o denso, deve esistere anche una copia `.txt` monospaziata per lettura umana

Colonne minime consigliate:
- `Entita'`
- `Categoria`
- `Significato operativo`
- `TF logico reale`
- `TF payload runtime attuale`
- `Motivo della differenza`
- `Preset`

Esempio corretto:

| Entita' | Categoria | Significato operativo | TF logico reale | TF payload runtime attuale | Motivo della differenza | Preset |
| --- | --- | --- | --- | --- | --- | --- |
| `P1C1` | CONDITION | setup filter true | `D` | `EXEC` | event anchored to mapped EXEC close for current runtime compatibility | `until_entry` |
| `P1` | PATTERN | setup completo | `D` | `EXEC` | pattern event aligned with executable fill chronology | `until_entry` |
| `P1E1` | ENTRY | fill LONG effettivo | `EXEC` | `EXEC` | no difference: real execution event | `event_only` |

Forma sbagliata:
- scheda lunga e verbosa senza una tabella riassuntiva finale
- tabella che non distingue `TF logico reale` da `TF payload runtime`
- tabella presente solo in Markdown ma illeggibile e senza versione `.txt` alternativa quando serve

============================================================
5-quater) ESEMPIO E3 - EVENTO LOGICO CON RUNTIME ANCHOR DISTINTO
============================================================

Caso corretto:
- una `CONDITION` daily mantiene:
  - `tf = D`
  - `cert_tf = D`
  - `cert_dt = logical daily close anchor`
  - `cert_price = close della barra daily logica`
- ma l'evento espone anche in `meta`:
  - `runtime_anchor_dt`
  - `runtime_anchor_price`
  - `logical_bar_start_dt` se il dataset logico e' `BAR_START`

Perche' e' corretto:
- mantiene pulita la semantica logica dell'evento
- evita di falsare il tf dell'evento solo per esigenze runtime
- permette a validator/runtime/UI di sapere anche dove l'evento si ancora operativamente

Forma sbagliata:
- cambiare `tf` da `D` a `EXEC` solo per far passare la cronologia, perdendo la semantica logica, quando la piattaforma supporta gia' anchor runtime distinti

============================================================
5-quinquies) ESEMPIO E4 - OCCURRENCE-BASED AUDIT SU DOPPIO SWING
============================================================

Caso corretto:
- una strategia tipo bullish divergence su due swing low puo' esporre:
  - `logic_state` per mostrare stati booleani densi come `POSITION` o `SETUP ACTIVE`
  - `logic_occurrences` per mostrare le occorrenze discrete realmente usate
- esempio concettuale:
  - `occ_sl1_00017`
    - `entity_id = P1C1`
    - `entity_type = CONDITION`
    - `logic_label_short = SL1`
    - `semantic_role = anchor`
    - `status = selected`
  - `occ_sl2_00044`
    - `entity_id = P1C2`
    - `entity_type = CONDITION`
    - `logic_label_short = SL2`
    - `semantic_role = anchor`
    - `status = certified`
    - `depends_on_occurrence_ids = ["occ_sl1_00017"]`
  - `occ_div_00045`
    - `entity_id = P1C3`
    - `entity_type = CONDITION`
    - `logic_label_short = RSI DIV`
    - `semantic_role = confirmation`
    - `depends_on_occurrence_ids = ["occ_sl1_00017", "occ_sl2_00044"]`
  - `occ_p1_00009`
    - `entity_id = P1`
    - `entity_type = PATTERN`
    - `logic_label_short = PATTERN`
    - `semantic_role = pattern_complete`
    - `used_by_trade_ids = [138]`

Perche' e' corretto:
- distingue l'entita' astratta dalla sua occorrenza concreta
- rende auditabile quale swing low sia stato davvero usato dal trade
- evita che la UI debba indovinare da soli puntini fractal o da segmenti legacy del Logic Indicator

Forma sbagliata:
- esporre solo marker prezzo uguali fra loro e pretendere che il consumer capisca quale sia l'anchor selezionato
- usare solo `logic_state` per un problema che richiede di distinguere piu' occorrenze della stessa `CONDITION`
- lasciare la numerazione del trade come ordinamento locale UI invece di allinearla a un `trade_seq` persistito quando disponibile

============================================================
6) ESEMPIO E - STATUS DEL TASK
============================================================
Nuovo EA:
- usare nome descrittivo provvisorio
- non assegnare strategy_code ufficiale
- non assumere strategy_version ufficiale

Nuova versione di EA esistente:
- usare strategy_code gia' assegnato da ALGO
- proporre la strategy_version solo dentro quel contesto

============================================================
7) ESEMPIO F - RISPOSTA CORRETTA DI UNA AI DEV
============================================================
Dato input:
- strategia dichiarata daily
- entry at close
- exit at close
- nessuna richiesta di percent-of-equity
- nessuna assegnazione ALGO di strategy_code

Risposta corretta:
- usa D diretto
- usa close della barra segnale
- mantiene `position_size = 1` come default ALGO
- usa nome descrittivo provvisorio
- non inventa `EA-XXX-VYY`
- dichiara le assunzioni e produce il draft

============================================================
8) ESEMPIO G - RISPOSTA SBAGLIATA DI UNA AI DEV
============================================================
Risposta sbagliata:
- apre una lista di opzioni come "D diretto o M1 aggregato?"
- chiede "close corrente o open barra successiva?" quando il brief dice gia' at close
- propone un strategy_code ufficiale sequenziale inventato
- apre discussioni su roadmap future prima del draft base
- cita template/versioni non coerenti con il bundle ricevuto
- hardcoda `pattern_symbol` come alias di mercato invece di renderlo runtime-aware
- dimentica la force-exit quando il trade puo' restare aperto a fine dati
- accetta solo params nested e poi si rompe nel runtime reale di ALGO
- AI_DESCRIPTION di due righe senza tabella Variables/Constants
- ChartTrade Semantics lasciata implicita o fuori dal campo AI_DESCRIPTION

Perche' e' sbagliata:
- non applica i default canonici
- scarica sull'utente decisioni che il pacchetto canonico dovrebbe gia' risolvere
- viola la governance dell'identita' ufficiale ALGO

============================================================
9) CHECK RAPIDO PER AI ESTERNA
============================================================
Se non riesci a rispondere chiaramente a tutte queste domande, il draft non e' ancora pronto:
- qual e' il Pattern?
- quali sono le Conditions?
- qual e' il tf di ogni Condition?
- qual e' il Pattern TF?
- qual e' l'MRE?
- cosa compare in charttrade?
- cosa resta invisibile?
- entry ed exit sono completamente definite?
- il task e' draft o ufficiale?
- il strategy_code esiste gia' oppure no?
- il draft accetta il formato params runtime attuale?
- `pattern_symbol` e' runtime-aware?
- se MRE e' piu' grosso di EXEC, il mapping fill EXEC -> barra logica e' dichiarato?
- se il trade puo' restare aperto, esiste una force-exit?

============================================================
10) PROMPT UNIVERSALE: PRINCIPIO CANONICO
============================================================
Il prompt universale definito in B9 e' parte del corpus canonico.

Questo significa:
- non deve vivere solo in chat o in istruzioni temporanee
- deve poter essere consegnato insieme alle specs come handoff autosufficiente
- e' corretto specializzarlo sul singolo brief, ma non svuotarlo dei suoi vincoli
- se il prompt specializzato confligge con B2-B10, prevalgono sempre B2-B10

============================================================
FINE B10
============================================================