FORGE — Feature-Oriented Rule Generation Engine
FORGE è un sistema di ricerca quantitativa per la scoperta sistematica di regole di trading algoritmico da dati storici di mercato. A partire da una KPI Table (OHLCV + indicatori tecnici), FORGE identifica eventi booleani con struttura temporale stabile, ne misura il potere predittivo rispetto a un target economico derivato dai dati, e produce contratti formali pronti per la validazione operativa.
Cosa fa FORGE, in parole semplici
Se fai trading — o lavori con chi lo fa — probabilmente hai già sentito frasi tipo "ogni volta che succede X, il prezzo tende a salire." Individuare un pattern del genere è la parte facile. Quella difficile è capire se sia reale, o se sembri buono solo perché sono state provate abbastanza idee che una, per puro caso, doveva per forza sembrare buona. È esattamente la trappola che FORGE è costruito per evitare, in quattro passaggi:
- Cerca un pattern ripetibile nella storia di prezzi e indicatori — senza mai guardare cosa succede dopo, così il pattern non ha modo di "barare" conoscendo di nascosto il futuro.
- Verifica se quel pattern predice davvero qualcosa — il prezzo si muove in modo affidabile in una direzione, abbastanza spesso e abbastanza da contare?
- Simula il trading per davvero — commissioni reali, esecuzione realistica degli ordini, e test solo su dati che il pattern non ha mai visto mentre veniva scoperto.
- Verifica se regge anche altrove — su altri asset, non solo su quello su cui è stato scoperto.
Ogni pattern esce da questo processo con un verdetto onesto — funziona, è al limite, oppure non regge — più le ragioni specifiche dietro a quel verdetto, mai un punteggio a scatola chiusa. Nessun pattern viene mai aggiustato a posteriori per farlo sembrare migliore; una regola che non supera la soglia viene riportata così com'è, non rimaneggiata di nascosto finché non la supera.
Una cosa che FORGE deliberatamente non fa: piazzare ordini, o parlare con un exchange. È uno strumento di ricerca che ti dice quali idee vale la pena approfondire e passa il testimone con i dettagli operativi (quando entrare, quando uscire, quanto è forte l'evidenza) — trasformare questo in trading dal vivo è un passo separato e deliberato, affidato a qualunque sistema tu usi davvero per eseguire gli ordini.
Il resto di questo documento entra nel funzionamento tecnico, per chi vuole eseguire FORGE in prima persona.
Perché FORGE
La ricerca sistematica di edge di trading soffre di tre problemi ricorrenti:
- Look-ahead bias — le soglie degli eventi calibrate osservando i rendimenti "conoscono" già il futuro prima della scoperta
- Ottimizzazione in-sample — soglie e orizzonti calibrati sulla stessa finestra usata per la valutazione producono backtest circolari
- Mancanza di separazione operativa — l'evidenza statistica del potere predittivo non equivale alla profittabilità sotto commissioni reali e meccaniche di esecuzione degli ordini
FORGE affronta tutti e tre con un'architettura a pipeline rigidamente separata: ogni modulo risponde a una sola domanda e passa al successivo solo un artefatto formale. Nessun modulo può accedere ai dati del modulo successivo; nessuna soglia può essere ricalibrata dopo la scoperta.
Pipeline
KPI Table (OHLCV + indicatori tecnici)
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Modulo 0 — Market Context │
│ Classifica ogni barra per regime di mercato (5 livelli). │
│ Output: KPI Table + colonne 'regime' e 'regime_stable' │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Modulo 1 — Event Discovery │
│ Scopre eventi booleani dalla struttura temporale degli │
│ indicatori. Non vede mai il forward return. │
│ Output: list[EventCandidate] │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Modulo 2 — Alpha Discovery │
│ Deriva il target per evento, misura il potere predittivo IS, │
│ conferma sull'OOS tail. Prima esposizione al forward return. │
│ Output: list[AlphaContract] │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Modulo 3 — Rule Discovery │
│ Backtest realistico con order mechanics, fee, walk-forward OOS. │
│ Output: EDGE / PARTIAL-EDGE / NON-EDGE + parametri operativi │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Modulo 4 — Rule Registry │
│ Deduplicazione, backtest cross-ticker, classificazione genericity│
│ Output: tabella piatta + report HTML autocontenuto │
└──────────────────────────────────────────────────────────────────┘
Invarianti fondamentali
| Invariante | Cosa previene |
|---|---|
| Modulo 1 non vede mai il forward return | Look-ahead bias nella selezione degli eventi |
| Le soglie sono immutabili dopo la scoperta | Ottimizzazione delle soglie sul campione di valutazione |
| Orizzonte, direzione e take-profit sono derivati dai dati per ogni evento | Assunzioni economiche che pre-cuociono il risultato |
| Il verdetto operativo è vincolato al walk-forward OOS (Modulo 3); la conferma OOS di Alpha è registrata su ogni contratto | Conferma a posteriori di una conclusione già decisa |
Installazione
FORGE dipende unicamente da numpy e pandas. Nessuna dipendenza da scipy, statsmodels o librerie ML: tutte le primitive statistiche (Spearman, t-test, regressione OU, FDR Benjamini-Hochberg, funzione beta incompleta) sono implementate in puro numpy.
pip install forgedge
Quick start
import pandas as pd
from forgedge import forge
# KPI Table con OHLCV + indicatori tecnici (colonna 'close' richiesta)
kpi = pd.read_parquet("kpi_table.parquet")
# Pipeline completa: da KPI Table a regole validate in una singola chiamata
result = forge(kpi, ticker="BTCUSDC", timeframe="1H")
print(result.summary()) # una riga per candidato + rule_verdict
for contract, response in result.edges(): # solo EDGE / PARTIAL-EDGE
print(contract.alpha_id, response.verdict)
print(result.registry.summary()) # Modulo 4 — regole catalogate
Prima che tutto questo giri, forge() risolve ogni campo di configurazione non impostato rispetto al timeframe/schema della sessione e verifica l'intero bundle per contraddizioni interne — di default (strict=True) solleva subito ValueError se ne trova una che farebbe fallire ogni candidato per ragioni che non c'entrano nulla col segnale, invece di lasciare che la run finisca e produca un muro di rigetti inspiegati. strict=False declassa questo a un warning e gira comunque; forgedge.config_report(...) esegue lo stesso controllo in standalone, prima di impegnarsi in una run completa.
Su un timeframe daily o più lento la griglia di holding period di default
viene sostituita automaticamente da una calibrata sul daily (la griglia
standard è calibrata su barre ~orarie); passando un proprio AlphaConfig si
mantiene il pieno controllo. Per una configurazione per-modulo coerente in
frequenza usare forgedge.presets.forge_preset("balanced", timeframe="1D",
asset=...).
La griglia di ogni evento viene inoltre arricchita con orizzonti a
0.5×/1×/2× la finestra dominante del suo indicatore
(AlphaConfig.horizon_enrichment — un'unione, mai una restrizione, con cap
statistico perché le finestre di condizionamento lente non possano chiedere
holding period non misurabili; le ipotesi aggiunte sono contate dal ledger e
prezzate dal rotation null).
Di default forge() esegue anche il fast rotation null a livello di
ricerca — la distribuzione nulla esatta del miglior eccesso standardizzato
su ogni offset circolare, calcolata via FFT in ~secondi — e un verdetto EDGE
pieno richiede in aggiunta di batterla (rotation_p <= 0.05): una regola che
ha solo vinto la lotteria del multiple testing della propria sessione di
discovery viene declassata a PARTIAL-EDGE. La superficie di ipotesi della
sessione è registrata su ForgeResult.ledger; disattivabile con
fast_null=False.
Gli split temporali sono purged: le barre in-sample la cui finestra
forward attraversa il confine IS/OOS sono escluse da ogni misura di Alpha
Discovery, e le finestre train del walk-forward terminano un worst-case trade
span prima della finestra test (vedi forgedge.timebudget.TimeBudget —
passandone uno a forge() tutti i moduli condividono un unico asse, con
embargo opzionale).
Rule Discovery seleziona il punto operativo solo dentro le finestre train
del walk-forward (RuleDiscoveryConfig.selection_mode="walk_forward", il
default): i parametri pubblicati vengono dall'ultima finestra train (o dal
consenso tra le finestre), e ogni metrica che alimenta il verdetto è
calcolata sullo span di selezione — l'ultima finestra test non è mai letta
da alcuna selezione. selection_mode="full_sample" ripristina il
comportamento legacy full-table.
I verdetti sono power-aware: un potenziale EDGE/PARTIAL-EDGE la cui
evidenza out-of-sample aggregata non può sostenerlo (troppi pochi trade test
aggregati, o una minimum detectable expectancy sopra l'effetto dichiarato)
viene emesso come INSUFFICIENT-DATA — non tradeable, ma conserva la sua
ValidatedRule per la ri-valutazione su più dati. La valutazione legge solo
il ledger concatenato delle finestre test, mai le singole finestre del
walk-forward (corte by design). Disattivabile con
SelectionCriteria(power_gate=False).
Sessioni multi-ticker con forge_multi:
from forgedge import forge_multi
frames = {"BTCUSDC": btc_kpi, "ETHUSDC": eth_kpi, "ADAUSDC": ada_kpi}
results, registry = forge_multi(frames, timeframe="1H")
# GENERIC: la regola si generalizza su ≥ 2/3 dei ticker testati
df = registry.flat_table()
print(df[["rule_id", "classification", "pf", "cross_ticker_score"]])
# Report HTML autocontenuto (SVG inline, nessuna CDN)
html = registry.html_report(timeframe="1H")
with open("report.html", "w") as f:
f.write(html)
Monitoraggio delle regole pubblicate su candele fresche:
from forgedge import RuleSpec, rule_performance_report
# Una spec per ogni regola tradeable di un run forge() — is_end / verdict /
# expectancy OOS vengono compilati automaticamente dagli artefatti di sessione.
specs = RuleSpec.from_forge_result(result)
# Oppure passa direttamente il ForgeResult — stesso effetto.
html = rule_performance_report(result, candele_fresche)
with open("rules_report.html", "w") as f:
f.write(html)
Ogni regola viene rieseguita in modo deterministico sulle candele fornite
(lo stesso percorso EventCandidate.apply() usato da Rule Discovery) — le
candele non devono essere la tabella di discovery, quindi è il modo naturale
per monitorare le regole pubblicate su dati raccolti dopo la discovery. Il
report mostra, per regola: equity vs buy & hold su doppio asse (scale
indipendenti — confronta le forme, non le grandezze), un trend di
attivazione mensile diviso in in-sample/out-of-sample, la distribuzione
gain/loss, le KDE delle distribuzioni dei rendimenti — low/close/high, ognuno
il rendimento forward sull'orizzonte di holding (target_h barre dopo la
barra del segnale, ancorato al close di quella barra, orientato per
direzione: la stessa grandezza da cui Alpha Discovery deriva il target e su
cui Rule Discovery fa trading), base vs evento — uno scatter MAE→net
(rischio intra-trade per queste regole senza stop), l'expectancy rolling
(rilevatore di decadimento dell'edge), la performance per regime, gli ultimi
trade e un badge "segnale
attivo ora".
I tre concetti
FORGE struttura il processo di scoperta attorno a tre concetti formali che rispondono ciascuno a una domanda distinta e producono un artefatto distinto.
Evento — osservare il mercato senza bias
Un evento è una condizione booleana sulle barre storiche, scoperta dalla struttura temporale degli indicatori senza mai calcolare un forward return. Le soglie sono distribuzionali (percentili specifici per asset) e immutabili una volta fissate.
c = candidates[0]
print(c.expression) # "rsi_14 < 31.2 AND spread_ema_9_25 < -0.0118"
signal = c.apply(new_kpi) # pd.Series bool — deterministico, senza look-ahead
Il ConsistencyGate filtra gli eventi con struttura temporale instabile (attivazioni insufficienti, clustering stagionale, bassa frequenza mensile) prima che venga calcolato qualsiasi rendimento.
Alpha — misurare il potere predittivo
Un alpha è la risposta empirica alla domanda: dato che l'evento si è attivato, cosa succede statisticamente nelle prossime h barre? Orizzonte, direzione e take-profit (sell_pct) sono tutti derivati dai dati — mai assunti — scansionando |mean_advantage|/√h su una grid di orizzonti e prendendo il quantile MFE delle barre attive.
c = promoted[0]
dt = c.derived_target
print(f"{dt.direction} a h={dt.holding_period_h}h sell_pct={dt.sell_pct:.4f}")
print(f"Grade {c.alpha_score.grade} | OOS lift: {c.oos_validation.lift:.4f}")
L'unico gate di rigetto rigido è la direzione indeterminata (nessun vantaggio finito su nessun orizzonte). Tutte le altre metriche statistiche (IC, Cohen's d, lift, FDR) contribuiscono al grade A–D senza bloccare la promozione.
Regola — tradare in modo realistico
Una regola è il verdetto operativo su un contratto alpha. Rule Discovery esegue un backtest realistico — uscita a take-profit, stop a orizzonte, commissioni per lato — valutato prima con un ingresso a mercato (quel verdetto è autoritativo), poi opzionalmente con un ingresso a limite adottato solo se supera condizioni out-of-sample rigorose — e valida la migliore configurazione di parametri su un walk-forward OOS rolling.
resp = RuleDiscovery(ed.df, contract, cand).run()
print(resp.verdict) # "EDGE", "PARTIAL-EDGE", "NON-EDGE"
if resp.is_edge:
p = resp.validated_rule.params
print(f"Entry: limit -{p.buy_drop_pct:.2%} TP: +{p.sell_pct:.2%} h={p.target_h}")
print(f"IS PF: {resp.in_sample_summary.profit_factor:.2f}"
f" WF consistency: {resp.walk_forward.consistency:.0%}")
Panoramica dei moduli
| Modulo | Domanda cui risponde | Output principale |
|---|---|---|
| 0 — Market Context | In che regime si trova questa barra? | Colonna regime (5 livelli), regime_stable |
| 1 — Event Discovery | Questa configurazione dell'indicatore è stabile e ripetibile? | EventCandidate — soglie immutabili, apply() |
| 2 — Alpha Discovery | L'evento predice un rendimento orientato? | AlphaContract — target derivato, grade A–D |
| 3 — Rule Discovery | Questo alpha è profittevole con order mechanics reali? | RuleDiscoveryResponse — verdetto EDGE, ValidatedRule |
| 4 — Rule Registry | Questa regola si generalizza su altri ticker? | Tabella piatta, report HTML — GENERIC / PARTIAL / SPECIFIC |
Stato implementazione
| Modulo | Stato |
|---|---|
| 0 — Market Context | ✅ Implementato |
| 1 — Event Discovery | ✅ Implementato |
| 2 — Alpha Discovery | ✅ Implementato |
| 3 — Rule Discovery | ✅ Implementato |
| 4 — Rule Registry | 🚧 WIP |
Documentazione
Inizia dal manuale — docs/manuale-it.md è un'unica guida completa, verificata con esempi, che copre l'installazione fino all'architettura di produzione, il troubleshooting, best practice/anti-pattern, una FAQ e un glossario. Ogni cosa al suo interno è stata verificata contro il sorgente attuale e, dove è citato un numero, contro una run reale. Tutto quello sotto è materiale di riferimento più stretto e complementare.
| File | Contenuto |
|---|---|
docs/manuale-it.md |
Il manuale completo, verificato con esempi — dall'installazione alla produzione, l'API di ogni modulo, troubleshooting, FAQ |
concepts_it.md |
Guida concettuale: evento, alpha e regola — dal mercato al segnale |
how_to_use_it.md |
Guida pratica alla pipeline end-to-end per produzione |
modulo_0_it.md |
Market Context: classificazione regime, EMAProxy, configurazione |
modulo_1_it.md |
Event Discovery: pipeline 5-step, ConsistencyGate, EventCandidate |
modulo_2_it.md |
Alpha Discovery: target derivato, IC, OOS, AlphaContract |
modulo_3_it.md |
Rule Discovery: backtest, verdetto EDGE, walk-forward, report |
modulo_4_it.md |
Rule Registry: deduplicazione, cross-ticker, genericity, export |
configuration_it.md |
Riferimento completo alla configurazione: ogni campo dataclass, tipo, default e descrizione |
playground_it.md |
forgedge.playground: helper di analisi di sola lettura sopra i ForgeResult messi in pool |
deployment_it.md |
forgedge.deployment: gate di promozione, export delle regole, manifest di monitoraggio per portare le regole scoperte in produzione |
experiment_it.md |
forgedge.experiment: StepWiseDiscovery — una ricerca partition & compose che fa crescere una regola confermata sull'hold-out una condizione AND alla volta sopra forge() |
Lavori su questo codebase con un agente AI? La skill forgedge in
.claude/skills/forgedge/ copre l'API della
libreria, i verdetti e gli invarianti della pipeline in una forma che un
agente può usare direttamente.
Licenza
MIT