Compare commits
2 Commits
36ac2426a1
...
003875f2c3
| Author | SHA1 | Date | |
|---|---|---|---|
| 003875f2c3 | |||
| e18adba4a6 |
File diff suppressed because one or more lines are too long
@@ -440,7 +440,10 @@ def yearly_stats(trades, ts):
|
|||||||
cap, peak, tot_peak, tot_dd = 1000.0, 1000.0, 1000.0, 0.0
|
cap, peak, tot_peak, tot_dd = 1000.0, 1000.0, 1000.0, 0.0
|
||||||
cur = None
|
cur = None
|
||||||
for i, j, ret in sorted(trades, key=lambda t: t[1]):
|
for i, j, ret in sorted(trades, key=lambda t: t[1]):
|
||||||
ey, xy = ts.iloc[i].year, ts.iloc[j].year
|
# clamp: l'engine live-path lascia j >= n per il trade aperto al bordo
|
||||||
|
# della serie (con dati freschi a fine-serie c'e' quasi sempre)
|
||||||
|
ey = ts.iloc[i].year
|
||||||
|
xy = ts.iloc[min(j, len(ts) - 1)].year
|
||||||
d = years.setdefault(ey, {"n": 0, "pnl": 0.0, "dd": 0.0})
|
d = years.setdefault(ey, {"n": 0, "pnl": 0.0, "dd": 0.0})
|
||||||
d["n"] += 1
|
d["n"] += 1
|
||||||
d["pnl"] += ret * 100
|
d["pnl"] += ret * 100
|
||||||
|
|||||||
+38
-5
@@ -32,7 +32,10 @@ ASSETS = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
TIMEFRAMES = ["1m", "5m", "15m", "1h"]
|
# NB: "1m" RIMOSSO da download_all (2026-06-12): con MAX_DAYS_PER_REQUEST=1 sono
|
||||||
|
# ~3000 richieste/asset — il refresh notturno e' rimasto 10h su BTC. Il 1m si
|
||||||
|
# scarica ad-hoc con download_asset("BTC", "1m") quando serve davvero.
|
||||||
|
TIMEFRAMES = ["5m", "15m", "1h"]
|
||||||
|
|
||||||
DERIBIT_RESOLUTION = {"1m": "1", "5m": "5", "15m": "15", "1h": "60"}
|
DERIBIT_RESOLUTION = {"1m": "1", "5m": "5", "15m": "15", "1h": "60"}
|
||||||
|
|
||||||
@@ -45,15 +48,20 @@ def _parquet_path(asset: str, tf: str) -> Path:
|
|||||||
return DATA_DIR / f"{asset.lower()}_{tf}.parquet"
|
return DATA_DIR / f"{asset.lower()}_{tf}.parquet"
|
||||||
|
|
||||||
|
|
||||||
def _fetch_deribit(instrument: str, resolution: str, start: str, end: str) -> list[dict]:
|
def _fetch_deribit(instrument: str, tf: str, start: str, end: str) -> list[dict]:
|
||||||
|
"""Endpoint v2 unificato (2026-06-12): il legacy /mcp-deribit/tools/get_historical
|
||||||
|
e' stato RIMOSSO da cerbero-mcp (404) — il refresh notturno ha skippato in
|
||||||
|
silenzio ogni chunk e scritto parquet troncati al 2018. Il v2 e' lo stesso
|
||||||
|
endpoint usato dal runner live (interval '1h'/'15m'/...)."""
|
||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
f"{CERBERO_URL}/mcp-deribit/tools/get_historical",
|
f"{CERBERO_URL}/mcp/tools/get_historical",
|
||||||
headers=CERBERO_HEADERS,
|
headers=CERBERO_HEADERS,
|
||||||
json={
|
json={
|
||||||
|
"exchange": "deribit",
|
||||||
"instrument": instrument,
|
"instrument": instrument,
|
||||||
|
"interval": tf,
|
||||||
"start_date": start,
|
"start_date": start,
|
||||||
"end_date": end,
|
"end_date": end,
|
||||||
"resolution": resolution,
|
|
||||||
},
|
},
|
||||||
timeout=30,
|
timeout=30,
|
||||||
)
|
)
|
||||||
@@ -84,6 +92,7 @@ def _download_cerbero_range(
|
|||||||
)
|
)
|
||||||
all_candles: list[dict] = []
|
all_candles: list[dict] = []
|
||||||
max_days = MAX_DAYS_PER_REQUEST[tf]
|
max_days = MAX_DAYS_PER_REQUEST[tf]
|
||||||
|
chunks = skipped = 0
|
||||||
current = datetime.fromisoformat(start_date)
|
current = datetime.fromisoformat(start_date)
|
||||||
end = datetime.fromisoformat(end_date)
|
end = datetime.fromisoformat(end_date)
|
||||||
|
|
||||||
@@ -100,19 +109,29 @@ def _download_cerbero_range(
|
|||||||
|
|
||||||
for attempt in range(3):
|
for attempt in range(3):
|
||||||
try:
|
try:
|
||||||
candles = _fetch_deribit(instrument, resolution, start_str, end_str)
|
candles = _fetch_deribit(instrument, tf, start_str, end_str)
|
||||||
all_candles.extend(candles)
|
all_candles.extend(candles)
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if attempt == 2:
|
if attempt == 2:
|
||||||
print(f" SKIP {start_str}→{end_str}: {e}")
|
print(f" SKIP {start_str}→{end_str}: {e}")
|
||||||
|
skipped += 1
|
||||||
time.sleep(2 ** attempt)
|
time.sleep(2 ** attempt)
|
||||||
|
|
||||||
|
chunks += 1
|
||||||
pbar.update(max_days)
|
pbar.update(max_days)
|
||||||
current = chunk_end
|
current = chunk_end
|
||||||
|
|
||||||
pbar.close()
|
pbar.close()
|
||||||
|
|
||||||
|
# GUARD (2026-06-12): se la maggioranza dei chunk e' stata skippata l'endpoint
|
||||||
|
# e' rotto, non il singolo range — ritornare il parziale farebbe scrivere un
|
||||||
|
# parquet troncato in silenzio (successo: 10h di SKIP e file fermi al 2018)
|
||||||
|
if chunks and skipped > chunks // 2:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"{instrument} {tf}: {skipped}/{chunks} chunk falliti — endpoint rotto? "
|
||||||
|
"NON scrivo dati parziali.")
|
||||||
|
|
||||||
if not all_candles:
|
if not all_candles:
|
||||||
return pd.DataFrame()
|
return pd.DataFrame()
|
||||||
|
|
||||||
@@ -210,6 +229,20 @@ def download_asset(asset: str, tf: str) -> pd.DataFrame:
|
|||||||
|
|
||||||
df = pd.concat(parts).drop_duplicates(subset="timestamp").sort_values("timestamp").reset_index(drop=True)
|
df = pd.concat(parts).drop_duplicates(subset="timestamp").sort_values("timestamp").reset_index(drop=True)
|
||||||
|
|
||||||
|
# GUARD anti-regressione (2026-06-12): MAI sovrascrivere un parquet buono con
|
||||||
|
# dati che finiscono PRIMA dell'esistente (fase Cerbero vuota per endpoint
|
||||||
|
# rotto = file troncato al 2018 scritto in silenzio; il bootstrap SH01 e i
|
||||||
|
# backtest leggono questi file)
|
||||||
|
if path.exists():
|
||||||
|
try:
|
||||||
|
old_last = int(pd.read_parquet(path, columns=["timestamp"])["timestamp"].max())
|
||||||
|
except Exception:
|
||||||
|
old_last = 0
|
||||||
|
if int(df["timestamp"].max()) < old_last:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"{path.name}: i nuovi dati finiscono PRIMA dell'esistente — "
|
||||||
|
"NON sovrascrivo (endpoint rotto?)")
|
||||||
|
|
||||||
path.parent.mkdir(parents=True, exist_ok=True)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
df.to_parquet(path, index=False)
|
df.to_parquet(path, index=False)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user