feat(games): sessioni 2-3 Blind Traders (opzioni/session/grid) + gate PORT06 e tooling reset

- Gioco GRID TRADERS (sessione 3, regola STRATEGIA_GRIGLIA.md): grid_engine
  (backtest causale fee-aware della griglia geometrica), grid_brief (digest
  anonimo per dimensionare la griglia), grid_arena (torneo 100 agenti);
  diario docs/diary/2026-06-10-grid-traders-game3.md
- Gioco OPZIONI: options_engine (BS + skew fittato + DVOL storica),
  options_arena, opt_calibrate (superficie premi REALE da cerbero-bite)
- Gioco SESSION: session_engine/session_arena (pattern orari intraday)
- arena: vincolo GAME_NO_LIVE=1 (vieta pairs e fade zscore/breakout/momentum
  gia' live, coercizione a trend/ma_cross) + normalize del candidato PRIMA
  della valutazione nel hill-climb
- Gate: grid_game_gate (griglia ETH vincitrice vs PORT06, mark-to-market),
  pairs30m_gate (ETH/BTC 30m ridondante col 15m gia' deployato?)
- reset_flatten: flatten one-shot del conto testnet per il reset portafoglio
- .gitignore: data/portfolio_paper_stats/ (stato runtime sleeve paper-only)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-11 09:49:17 +00:00
parent 8adf388e86
commit 8d69a0cef5
16 changed files with 2193 additions and 5 deletions
+26 -5
View File
@@ -46,6 +46,19 @@ SINGLE_FAMILIES = ["zscore", "breakout", "ma_cross", "rsi", "momentum"]
DIRECTIONS = ["reversion", "trend"]
TIMEFRAMES = ["5m", "15m", "30m", "1h", "2h", "4h", "1d"] # tutti i timing validi
# Vincolo opzionale: accetta SOLO strategie NON gia' usate in live. Firme live (da
# vietare): 'pairs' (PR01) + REVERSION di zscore(MR01)/breakout(MR02)/momentum(MR07,
# return-reversal). NB: momentum+reversion == MR07 -> e' LIVE, va vietato (loophole).
# Coercizione: pairs -> ma_cross(trend); (zscore|breakout|momentum)+reversion -> +trend.
# Resta spazio NUOVO: trend di zscore/breakout/momentum, ma_cross, rsi (ogni direzione).
NO_LIVE = False
_LIVE_REV_FAMS = {"zscore", "breakout", "momentum"} # in reversion = MR01/MR02/MR07 live
def set_no_live(v: bool):
global NO_LIVE
NO_LIVE = bool(v)
def _rand_param(rng, lo, hi, typ):
if typ == "i":
@@ -54,7 +67,7 @@ def _rand_param(rng, lo, hi, typ):
def random_spec(rng):
if rng.random() < 0.25:
if not NO_LIVE and rng.random() < 0.25:
fam = "pairs"
else:
fam = rng.choice(SINGLE_FAMILIES)
@@ -66,7 +79,10 @@ def random_spec(rng):
spec["series"] = "AB"
else:
spec["series"] = rng.choice(["A", "B"])
spec["params"]["direction"] = rng.choice(DIRECTIONS)
d = rng.choice(DIRECTIONS)
if NO_LIVE and fam in _LIVE_REV_FAMS:
d = "trend" # zscore/breakout in reversion sono live -> trend
spec["params"]["direction"] = d
return spec
@@ -98,6 +114,8 @@ def _normalize(spec):
fam = spec.get("family")
if fam not in SPACE:
fam = "zscore"
if NO_LIVE and fam == "pairs":
fam = "ma_cross" # pairs (PR01) e' live -> rimpiazza con ma_cross (nuovo, trend)
out = {"family": fam, "params": {}}
for k, (lo, hi, typ) in SPACE[fam].items():
v = spec.get("params", {}).get(k, (lo + hi) / 2)
@@ -113,7 +131,10 @@ def _normalize(spec):
else:
out["series"] = spec.get("series", "A") if spec.get("series") in ("A", "B") else "A"
d = spec.get("params", {}).get("direction") or spec.get("direction")
out["params"]["direction"] = d if d in DIRECTIONS else "reversion"
d = d if d in DIRECTIONS else "reversion"
if NO_LIVE and fam in _LIVE_REV_FAMS and d == "reversion":
d = "trend" # zscore/breakout in reversion = fade live -> trend
out["params"]["direction"] = d
return out
@@ -165,12 +186,12 @@ def run_tournament(specs, briefs=None, seed=7,
for ep in range(1, epochs + 1):
# elaborazione: l'agente affina sul TRAIN (cio' che vede); ricalcola VALID
for a in alive():
cand = mutate(a.spec, rng)
cand = _normalize(mutate(a.spec, rng)) # normalizza PRIMA di valutare
data = datasets[a.tf]
tr, va, _ = splits_map[a.tf]
m = evaluate(data, cand, tr)
if m["fitness"] > a.train_fit:
a.spec = _normalize(cand)
a.spec = cand
a.metrics, a.train_fit = m, m["fitness"]
a.vmetrics = evaluate(data, a.spec, va)
a.valid_fit = a.vmetrics["fitness"]