chore(reset): v2.0.0 — storico certificato Deribit mainnet, ripartenza pulita
Reset del progetto su fondamenta verificate dopo la scoperta che l'intera libreria "validata OOS" era artefatto di feed contaminato (print fantasma del feed Cerbero TESTNET + storico Binance/USDT). - Storico ricostruito da Deribit MAINNET (ccxt pubblico, tokenless) e CERTIFICATO (certify_feed.py): BTC/ETH puliti su TUTTA la storia (mediana 2-6 bps vs Coinbase USD), integrita' OHLC + coerenza resample (maxΔ 0.00) + cross-venue OK. Alt esclusi (illiquidi/divergenti: LTC/DOGE 50-82% barre flat; XRP/BNB non certificabili). - Verdetto sul feed pulito: FADE / PAIRS / XS01 / TSM01 morti (ogni portafoglio Sharpe -2.3..-3.0, DD ~40%); solo SH01 e frammenti HONEST con segnale residuo, da ri-validare in isolamento. - Cleanup "restart pulito": strategie, stack live (src/live, src/portfolio, runner/executor, yml, docker), ~100 script ricerca/gate, waste/games/ portfolios, dati non certificati + cache e 60+ diari -> archiviati in Old/ (preservati, non cancellati). Diario consolidato in un unico documento. - Skeleton ricerca tenuto: Strategy ABC + indicatori + src/fractal + src/backtest/engine + load_data; tool dati certificati (rebuild_history, certify_feed, audit_feed, multi_source_check). - Universo dati ATTIVO: solo BTC/ETH (5m/15m/1h); guardrail fisico (load_data su alt -> FileNotFoundError). Esecuzione DISABILITATA, conto flat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
grid_brief — digest ANONIMO per gli agenti del gioco GRID TRADERS (sessione 3).
|
||||
|
||||
Come agent_brief, ma con statistiche pensate per DIMENSIONARE una griglia:
|
||||
oltre a vol/autocorrelazioni, l'escursione tipica (max/min - 1) su finestre
|
||||
rolling e quanto spesso il prezzo "esce" da un range simmetrico attorno a un
|
||||
punto di partenza entro N barre. L'agente non sa cosa siano X e Y.
|
||||
|
||||
uv run python -m scripts.games.grid_brief 1h # stampa il digest
|
||||
uv run python -m scripts.games.grid_brief --all # scrive data/games/grid_digests.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from scripts.games.engine import load_anon
|
||||
from scripts.games.agent_brief import _stats
|
||||
|
||||
TF_ID = {"15m": "T2", "30m": "T3", "1h": "T4", "2h": "T5", "4h": "T6", "1d": "T7"}
|
||||
|
||||
|
||||
def _range_stats(close, windows=(100, 500, 2000)):
|
||||
"""Escursione (max/min - 1) su finestre rolling: mediana e p90, in %."""
|
||||
s = pd.Series(close)
|
||||
out = {}
|
||||
for w in windows:
|
||||
if len(close) < w * 2:
|
||||
continue
|
||||
exc = (s.rolling(w).max() / s.rolling(w).min() - 1).dropna()
|
||||
out[f"w{w}"] = {"median_pct": round(float(exc.median() * 100), 2),
|
||||
"p90_pct": round(float(exc.quantile(0.9) * 100), 2)}
|
||||
return out
|
||||
|
||||
|
||||
def _escape_stats(close, half_widths=(0.05, 0.10, 0.20), horizon=500):
|
||||
"""Da un punto di partenza, % di volte in cui il prezzo esce da
|
||||
+-half_width entro `horizon` barre (campionato ogni horizon/2)."""
|
||||
n = len(close)
|
||||
stepi = max(1, horizon // 2)
|
||||
starts = np.arange(0, n - horizon, stepi)
|
||||
out = {}
|
||||
for hw in half_widths:
|
||||
esc = 0
|
||||
for st in starts:
|
||||
w = close[st:st + horizon]
|
||||
p0 = w[0]
|
||||
if np.any(w > p0 * (1 + hw)) or np.any(w < p0 * (1 - hw)):
|
||||
esc += 1
|
||||
out[f"+-{hw*100:.0f}%"] = round(100.0 * esc / max(1, len(starts)), 1)
|
||||
return out
|
||||
|
||||
|
||||
def make_grid_digest(tf: str, window: int = 60):
|
||||
data = load_anon(tf)
|
||||
n = data["n"]
|
||||
s = max(0, n - window)
|
||||
dig = {"timeframe_id": TF_ID.get(tf, "T?"), "n_bars_total": n, "series": {}}
|
||||
for name in ("A", "B"):
|
||||
o = data[name]
|
||||
c = o["close"]
|
||||
norm = c[s:] / c[s] * 100.0
|
||||
dig["series"][{"A": "X", "B": "Y"}[name]] = {
|
||||
"stats": _stats(c, o["high"], o["low"]),
|
||||
"range_excursion_rolling": _range_stats(c),
|
||||
"escape_from_range_within_500_bars_pct": _escape_stats(c),
|
||||
"recent_window_norm": [round(float(v), 2) for v in norm],
|
||||
}
|
||||
return dig
|
||||
|
||||
|
||||
GRID_MENU = {
|
||||
"gioco": ("Configura una GRIGLIA di trading secondo la spec (griglia geometrica "
|
||||
"FISSA dentro un range attorno al prezzo di deploy; compra quando il "
|
||||
"prezzo scende attraverso un livello, rivendi quel livello quando "
|
||||
"risale al livello successivo; stop-loss sotto il range e take-profit "
|
||||
"sopra chiudono tutto; poi la griglia si ri-deploya sul prezzo corrente)."),
|
||||
"obiettivo": ("PnL netto positivo dopo i costi (0.10% andata+ritorno per ogni "
|
||||
"round-trip di livello). Servono >=10 operazioni al mese. La "
|
||||
"griglia monetizza le oscillazioni e PERDE nei trend: lo stop-loss "
|
||||
"limita il danno. Non sai cosa siano X e Y."),
|
||||
"vincolo_break_even": ("passo_griglia = ((1+range_up)/(1-range_down))^(1/grid_levels) - 1 "
|
||||
"DEVE superare 1.5 x 0.10% = 0.15%, o il bot si rifiuta "
|
||||
"di partire. Griglie troppo fitte muoiono di fee."),
|
||||
"parametri": {
|
||||
"series": "X oppure Y",
|
||||
"range_down_pct": "estremo inferiore del range, % sotto il prezzo di deploy (2-30)",
|
||||
"range_up_pct": "estremo superiore del range, % sopra il prezzo di deploy (2-30)",
|
||||
"grid_levels": "numero di livelli della griglia (4-30)",
|
||||
"sl_buf_pct": "stop-loss: % sotto RANGE_LOW (1-15)",
|
||||
"tp_buf_pct": "take-profit: % sopra RANGE_HIGH (1-15)",
|
||||
"max_bars": "durata massima di una griglia in barre, poi liquida e ri-deploya (48-3000)",
|
||||
},
|
||||
"trade_off": ("range stretto + tanti livelli = tanti round-trip piccoli ma SL "
|
||||
"frequenti nei trend; range largo = SL rari ma capitale spesso "
|
||||
"fermo. Lo stop-loss largo aumenta la perdita quando scatta; "
|
||||
"stretto scatta piu' spesso. Usa le statistiche di escursione "
|
||||
"del digest per dimensionare range e stop."),
|
||||
"output_schema": {
|
||||
"series": "X|Y", "range_down_pct": "num", "range_up_pct": "num",
|
||||
"grid_levels": "int", "sl_buf_pct": "num", "tp_buf_pct": "num",
|
||||
"max_bars": "int", "hypothesis": "1-2 frasi: il tuo ragionamento",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
from pathlib import Path
|
||||
if "--all" in sys.argv:
|
||||
out = {tf: make_grid_digest(tf) for tf in TF_ID}
|
||||
p = Path("data/games/grid_digests.json")
|
||||
p.parent.mkdir(parents=True, exist_ok=True)
|
||||
p.write_text(json.dumps(out))
|
||||
print(f"scritti digest per {list(out)} -> {p}")
|
||||
else:
|
||||
tf = sys.argv[1] if len(sys.argv) > 1 else "1h"
|
||||
print(json.dumps(make_grid_digest(tf), indent=2)[:3000])
|
||||
Reference in New Issue
Block a user