#!/usr/bin/env python """r0726_skh_sigcache.py — costruisce e mette in CACHE le tabelle di segnale intra-bin di SKH01. E' il pezzo caro del follow-up "book sul path live" (`r0726_skh_live_book.py`): per ogni (asset, offset) ricostruisce il segnale che il cron ORARIO vedrebbe a OGNI osservazione dentro ogni bin 230m — ~82.000 righe, ~9 minuti. Isolato qui perche' e' (a) puro, (b) riusabile, e (c) ripartibile: la cache e' su disco, il job si puo' interrompere e riprendere. Sottocampione di offset dichiarato A PRIORI: 8 degli 23 della griglia SKH01, uno ogni 90 minuti su [0, 690). NON e' una selezione — e' un sottocampione uniforme scelto per costo (2 core, ~9 min per run, cron live da non affamare). Va dichiarato perche' la banda d'ancora che ne esce ha 8 punti invece di 23. uv run python scripts/research/r0726_skh_sigcache.py [--assets BTC ETH] [--offsets ...] """ from __future__ import annotations import argparse import sys import time from pathlib import Path import pandas as pd ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT / "scripts" / "research")) sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt")) import r0726_skh_partial_entry as SP # noqa: E402 # 8 offset uniformi su [0,690), uno ogni 90m. Dichiarati a priori, offset 0 = canonico. OFFSETS_SUB = (0, 90, 180, 270, 360, 450, 540, 630) ASSETS = ("BTC", "ETH") CACHE_DIR = ROOT / "data" / "_cache" / "skh_sigtab" def cache_path(asset: str, off: int) -> Path: return CACHE_DIR / f"sigtab_{asset.lower()}_off{off:03d}.parquet" def get_signal_table(asset: str, off: int, verbose: bool = True) -> pd.DataFrame: """Tabella di segnale intra-bin, da cache se presente.""" p = cache_path(asset, off) if p.exists(): return pd.read_parquet(p) t0 = time.time() tab = SP.signal_table(asset, off) p.parent.mkdir(parents=True, exist_ok=True) tab.to_parquet(p, index=False) if verbose: print(f" {asset} off{off:>3} {len(tab):>7} righe " f"segnali {int((tab['dir'] != 0).sum()):>5} in {time.time()-t0:5.0f}s", flush=True) return tab def main() -> None: ap = argparse.ArgumentParser() ap.add_argument("--assets", nargs="+", default=list(ASSETS)) ap.add_argument("--offsets", nargs="+", type=int, default=list(OFFSETS_SUB)) args = ap.parse_args() print(f"[SIGCACHE] {len(args.assets)} asset x {len(args.offsets)} offset " f"-> {CACHE_DIR}", flush=True) t0 = time.time() done = 0 for off in args.offsets: # offset esterno: cosi' ogni offset e' for a in args.assets: # completo su entrambi gli asset appena finisce p = cache_path(a, off) if p.exists(): print(f" {a} off{off:>3} gia' in cache", flush=True) continue get_signal_table(a, off) done += 1 print(f"[SIGCACHE] fatto: {done} nuove tabelle in {time.time()-t0:.0f}s", flush=True) if __name__ == "__main__": main()