chore: untrack paper_trades runtime data + report per anno/mercato
- data/paper_trades/ rimosso dal tracking (dati runtime, gitignored) - scripts/analysis/yearly_market_report.py: accuracy/trades/PnL per anno×mercato Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,3 +16,4 @@ data/processed/
|
|||||||
*.pt
|
*.pt
|
||||||
*.pth
|
*.pth
|
||||||
notebooks/.ipynb_checkpoints/
|
notebooks/.ipynb_checkpoints/
|
||||||
|
data/paper_trades/
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"capital": 1000,
|
|
||||||
"in_position": false,
|
|
||||||
"direction": 0,
|
|
||||||
"entry_price": 0,
|
|
||||||
"entry_time": "",
|
|
||||||
"bars_held": 0,
|
|
||||||
"total_trades": 0,
|
|
||||||
"total_wins": 0,
|
|
||||||
"started_at": "2026-05-27T21:16:02.087963+00:00",
|
|
||||||
"last_bar_ts": 0,
|
|
||||||
"last_update": "2026-05-27T21:16:04.705726+00:00"
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"ts": "2026-05-27T21:16:02.087975+00:00", "worker": "ML01_squeeze_gbm__ETH__15m", "event": "INIT", "capital": 1000, "strategy": "ML01_squeeze_gbm", "asset": "ETH", "tf": "15m"}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"capital": 1000,
|
|
||||||
"in_position": false,
|
|
||||||
"direction": 0,
|
|
||||||
"entry_price": 0,
|
|
||||||
"entry_time": "",
|
|
||||||
"bars_held": 0,
|
|
||||||
"total_trades": 0,
|
|
||||||
"total_wins": 0,
|
|
||||||
"started_at": "2026-05-27T21:16:02.087646+00:00",
|
|
||||||
"last_bar_ts": 0,
|
|
||||||
"last_update": "2026-05-27T21:16:04.584685+00:00"
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"ts": "2026-05-27T21:16:02.087660+00:00", "worker": "SQ01_squeeze_base__BTC__15m", "event": "INIT", "capital": 1000, "strategy": "SQ01_squeeze_base", "asset": "BTC", "tf": "15m"}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"capital": 1000,
|
|
||||||
"in_position": false,
|
|
||||||
"direction": 0,
|
|
||||||
"entry_price": 0,
|
|
||||||
"entry_time": "",
|
|
||||||
"bars_held": 0,
|
|
||||||
"total_trades": 0,
|
|
||||||
"total_wins": 0,
|
|
||||||
"started_at": "2026-05-27T21:16:02.087214+00:00",
|
|
||||||
"last_bar_ts": 0,
|
|
||||||
"last_update": "2026-05-27T21:16:04.339917+00:00"
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"ts": "2026-05-27T21:16:02.087241+00:00", "worker": "SQ02_antifake_vol__BTC__15m", "event": "INIT", "capital": 1000, "strategy": "SQ02_antifake_vol", "asset": "BTC", "tf": "15m"}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"capital": 1000,
|
|
||||||
"in_position": false,
|
|
||||||
"direction": 0,
|
|
||||||
"entry_price": 0,
|
|
||||||
"entry_time": "",
|
|
||||||
"bars_held": 0,
|
|
||||||
"total_trades": 0,
|
|
||||||
"total_wins": 0,
|
|
||||||
"started_at": "2026-05-27T21:16:02.087438+00:00",
|
|
||||||
"last_bar_ts": 0,
|
|
||||||
"last_update": "2026-05-27T21:16:04.463602+00:00"
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"ts": "2026-05-27T21:16:02.087448+00:00", "worker": "SQ02_antifake_vol__ETH__15m", "event": "INIT", "capital": 1000, "strategy": "SQ02_antifake_vol", "asset": "ETH", "tf": "15m"}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"in_position": false,
|
|
||||||
"direction": null,
|
|
||||||
"entry_price": 0,
|
|
||||||
"entry_time": null,
|
|
||||||
"bars_held": 0,
|
|
||||||
"last_update": "2026-05-27T07:40:09.196718+00:00"
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
{"timestamp": "2026-05-27T07:35:10.715321+00:00", "event": "TRAINING", "lookback_days": 365}
|
|
||||||
{"timestamp": "2026-05-27T07:35:11.967644+00:00", "event": "TRAINING_DONE", "samples": 90, "up_ratio": 48.888888888888886, "train_accuracy": 100.0}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{"timestamp": "2026-05-27T07:36:03.120802+00:00", "event": "STARTUP", "equity": 101459.276155, "testnet": true}
|
|
||||||
{"timestamp": "2026-05-27T07:36:03.121518+00:00", "event": "TRAINING", "lookback_days": 365}
|
|
||||||
{"timestamp": "2026-05-27T07:36:04.249123+00:00", "event": "TRAINING_DONE", "samples": 90, "up_ratio": 48.888888888888886, "train_accuracy": 100.0}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{"timestamp": "2026-05-27T08:04:41.544464+00:00", "event": "TRAINING", "lookback_days": 365, "instrument": "ETH-PERPETUAL"}
|
|
||||||
{"timestamp": "2026-05-27T08:04:42.704464+00:00", "event": "TRAINING_DONE", "samples": 90, "up_ratio": 48.888888888888886, "train_accuracy": 100.0}
|
|
||||||
{"timestamp": "2026-05-27T08:04:42.918237+00:00", "event": "OPENING", "side": "buy", "amount": 0.216, "price": 2083.75, "virtual_capital": 1000.0, "notional": 450.0, "signal": {"direction": "buy", "probability": 0.75, "squeeze_duration": 10}}
|
|
||||||
{"timestamp": "2026-05-27T08:04:43.143718+00:00", "event": "OPENED", "order_result": {"order": {"label": "pythagoras-squeeze", "price": 2292.25, "order_id": "USDC-209283595178", "user_id": 81070, "amount": 0.216, "instrument_name": "ETH_USDC-PERPETUAL", "direction": "buy", "time_in_force": "good_til_cancelled", "web": false, "api": true, "creation_timestamp": 1779869083116, "mmp": false, "replaced": false, "post_only": false, "reduce_only": false, "filled_amount": 0.216, "last_update_timestamp": 1779869083116, "average_price": 2083.9, "contracts": 216.0, "order_state": "filled", "order_type": "market", "is_liquidation": false, "risk_reducing": false}, "trades": [{"label": "pythagoras-squeeze", "timestamp": 1779869083116, "state": "filled", "price": 2083.9, "order_id": "USDC-209283595178", "user_id": 81070, "amount": 0.216, "instrument_name": "ETH_USDC-PERPETUAL", "direction": "buy", "index_price": 2083.37, "trade_seq": 6674514, "api": true, "mark_price": 2083.86, "matching_id": null, "tick_direction": 0, "profit_loss": 0.0, "mmp": false, "post_only": false, "reduce_only": false, "self_trade": false, "contracts": 216.0, "trade_id": "USDC-32731729", "fee_currency": "USDC", "order_type": "market", "fee": 0.2250612, "liquidity": "T", "risk_reducing": false}], "data_timestamp": "2026-05-27T08:04:43.126155+00:00"}}
|
|
||||||
{"timestamp": "2026-05-27T08:04:46.361078+00:00", "event": "CLOSING", "reason": "test", "entry_price": 2083.75, "exit_price": 2083.95, "size": 0.216, "trade_pnl": 0.04, "fee": 0.9, "net_pnl": -0.86, "pnl_pct": -0.086, "bars_held": 0, "capital_before": 1000.0}
|
|
||||||
{"timestamp": "2026-05-27T08:04:46.574322+00:00", "event": "CLOSED", "result": {"order_id": "USDC-209283608601", "state": "filled", "data_timestamp": "2026-05-27T08:04:46.555823+00:00"}, "net_pnl": -0.86, "pnl_pct": -0.086, "virtual_capital": 999.14}
|
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
"""Report accuracy per ANNO × MERCATO delle strategie migliori.
|
||||||
|
|
||||||
|
Esegue ogni strategia vincente su BTC e ETH e produce tabella
|
||||||
|
accuracy/trades per ogni anno. Permette di vedere robustezza temporale
|
||||||
|
e differenze tra mercati.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, ".")
|
||||||
|
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
STRATEGIES_DIR = Path("scripts/strategies")
|
||||||
|
|
||||||
|
|
||||||
|
def load_class(module_file, class_name):
|
||||||
|
path = STRATEGIES_DIR / f"{module_file}.py"
|
||||||
|
spec = importlib.util.spec_from_file_location(module_file, path)
|
||||||
|
mod = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(mod)
|
||||||
|
return getattr(mod, class_name)
|
||||||
|
|
||||||
|
|
||||||
|
# (label, module, class, params, hold)
|
||||||
|
STRATEGIES = [
|
||||||
|
("SQ02 antifake+vol", "SQ02_squeeze_antifake_vol", "SqueezeAntifakeVol", {}, 3),
|
||||||
|
("MT01 ema20+vol", "MT01_squeeze_mtf_momentum", "SqueezeMTFMomentum",
|
||||||
|
{"ema_period": 20, "min_slope": 0.001, "vol_filter": True}, 3),
|
||||||
|
("PD01 vtb3 vm1.3", "PD01_price_volume_divergence", "PriceVolumeDivergence",
|
||||||
|
{}, 3),
|
||||||
|
("CM01 cb6+vol", "CM01_cross_market_momentum", "CrossMarketMomentum",
|
||||||
|
{"cross_bars": 6, "mom_min": 0.001, "use_vol": True}, 3),
|
||||||
|
("AD01 lt.65 ht.95", "AD01_adaptive_squeeze", "AdaptiveSqueeze",
|
||||||
|
{"low_thr": 0.65, "high_thr": 0.95, "use_vol": True}, 3),
|
||||||
|
]
|
||||||
|
|
||||||
|
ASSETS = ["BTC", "ETH"]
|
||||||
|
TF = "15m"
|
||||||
|
ALL_YEARS = list(range(2018, 2027))
|
||||||
|
|
||||||
|
|
||||||
|
def run():
|
||||||
|
results = {} # (label, asset) -> BacktestResult
|
||||||
|
|
||||||
|
for label, module, cls_name, params, hold in STRATEGIES:
|
||||||
|
try:
|
||||||
|
cls = load_class(module, cls_name)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"SKIP {label}: {e}")
|
||||||
|
continue
|
||||||
|
strat = cls()
|
||||||
|
for asset in ASSETS:
|
||||||
|
try:
|
||||||
|
r = strat.backtest(asset, TF, hold=hold, **params)
|
||||||
|
if r:
|
||||||
|
results[(label, asset)] = r
|
||||||
|
except Exception as e:
|
||||||
|
print(f" errore {label} {asset}: {e}")
|
||||||
|
|
||||||
|
# ── Tabella ACCURACY per anno × mercato ──────────────────────────
|
||||||
|
print(f"\n{'=' * 140}")
|
||||||
|
print(f" ACCURACY PER ANNO × MERCATO — {TF} (fee 0.2% RT, leva 3x, pos 15%)")
|
||||||
|
print(f"{'=' * 140}")
|
||||||
|
|
||||||
|
header = f" {'Strategia':<22s} {'Mkt':>3s}"
|
||||||
|
for y in ALL_YEARS:
|
||||||
|
header += f" {y:>7d}"
|
||||||
|
header += f" │ {'TOT':>6s} {'DD%':>5s} {'Worst':>10s}"
|
||||||
|
print(header)
|
||||||
|
print(f" {'─' * 136}")
|
||||||
|
|
||||||
|
for label, module, cls_name, params, hold in STRATEGIES:
|
||||||
|
for asset in ASSETS:
|
||||||
|
r = results.get((label, asset))
|
||||||
|
if not r:
|
||||||
|
continue
|
||||||
|
yd = {ys.year: ys for ys in r.yearly}
|
||||||
|
line = f" {label:<22s} {asset:>3s}"
|
||||||
|
for y in ALL_YEARS:
|
||||||
|
if y in yd:
|
||||||
|
line += f" {yd[y].accuracy:>5.0f}%↑" if yd[y].accuracy >= 80 else f" {yd[y].accuracy:>5.0f}% "
|
||||||
|
else:
|
||||||
|
line += f" {'—':>7s}"
|
||||||
|
worst = r.worst_year
|
||||||
|
worst_str = f"{worst.year}({worst.accuracy:.0f}%)" if worst else "N/A"
|
||||||
|
line += f" │ {r.accuracy:>5.1f}% {r.max_dd:>4.1f}% {worst_str:>10s}"
|
||||||
|
print(line)
|
||||||
|
print(f" {'·' * 136}")
|
||||||
|
|
||||||
|
# ── Tabella TRADES per anno × mercato ────────────────────────────
|
||||||
|
print(f"\n{'=' * 140}")
|
||||||
|
print(f" NUMERO TRADES PER ANNO × MERCATO")
|
||||||
|
print(f"{'=' * 140}")
|
||||||
|
|
||||||
|
header = f" {'Strategia':<22s} {'Mkt':>3s}"
|
||||||
|
for y in ALL_YEARS:
|
||||||
|
header += f" {y:>7d}"
|
||||||
|
header += f" │ {'TOT':>6s} {'€/day':>6s}"
|
||||||
|
print(header)
|
||||||
|
print(f" {'─' * 130}")
|
||||||
|
|
||||||
|
for label, module, cls_name, params, hold in STRATEGIES:
|
||||||
|
for asset in ASSETS:
|
||||||
|
r = results.get((label, asset))
|
||||||
|
if not r:
|
||||||
|
continue
|
||||||
|
yd = {ys.year: ys for ys in r.yearly}
|
||||||
|
line = f" {label:<22s} {asset:>3s}"
|
||||||
|
for y in ALL_YEARS:
|
||||||
|
if y in yd:
|
||||||
|
line += f" {yd[y].trades:>7d}"
|
||||||
|
else:
|
||||||
|
line += f" {'—':>7s}"
|
||||||
|
line += f" │ {r.trades:>6d} {r.daily_pnl:>+6.2f}"
|
||||||
|
print(line)
|
||||||
|
print(f" {'·' * 130}")
|
||||||
|
|
||||||
|
# ── Tabella PnL per anno × mercato ──────────────────────────────
|
||||||
|
print(f"\n{'=' * 140}")
|
||||||
|
print(f" PnL € PER ANNO × MERCATO (su €1000, no compounding tra anni)")
|
||||||
|
print(f"{'=' * 140}")
|
||||||
|
|
||||||
|
header = f" {'Strategia':<22s} {'Mkt':>3s}"
|
||||||
|
for y in ALL_YEARS:
|
||||||
|
header += f" {y:>7d}"
|
||||||
|
header += f" │ {'TOT€':>8s}"
|
||||||
|
print(header)
|
||||||
|
print(f" {'─' * 132}")
|
||||||
|
|
||||||
|
for label, module, cls_name, params, hold in STRATEGIES:
|
||||||
|
for asset in ASSETS:
|
||||||
|
r = results.get((label, asset))
|
||||||
|
if not r:
|
||||||
|
continue
|
||||||
|
yd = {ys.year: ys for ys in r.yearly}
|
||||||
|
line = f" {label:<22s} {asset:>3s}"
|
||||||
|
for y in ALL_YEARS:
|
||||||
|
if y in yd:
|
||||||
|
line += f" {yd[y].pnl:>+7.0f}"
|
||||||
|
else:
|
||||||
|
line += f" {'—':>7s}"
|
||||||
|
line += f" │ {r.pnl:>+8.0f}"
|
||||||
|
print(line)
|
||||||
|
print(f" {'·' * 132}")
|
||||||
|
|
||||||
|
# ── Sintesi: media per anno (tutte le strategie) ────────────────
|
||||||
|
print(f"\n{'=' * 140}")
|
||||||
|
print(f" SINTESI — Accuracy media per anno (tutte le strategie, BTC+ETH)")
|
||||||
|
print(f"{'=' * 140}")
|
||||||
|
year_acc = {y: [] for y in ALL_YEARS}
|
||||||
|
for (label, asset), r in results.items():
|
||||||
|
for ys in r.yearly:
|
||||||
|
if ys.trades >= 10:
|
||||||
|
year_acc[ys.year].append(ys.accuracy)
|
||||||
|
|
||||||
|
line_y = f" {'Anno':<22s} "
|
||||||
|
line_a = f" {'Acc media':<22s} "
|
||||||
|
for y in ALL_YEARS:
|
||||||
|
accs = year_acc[y]
|
||||||
|
avg = sum(accs) / len(accs) if accs else 0
|
||||||
|
line_y += f" {y:>7d}"
|
||||||
|
line_a += f" {avg:>6.1f}%"
|
||||||
|
print(line_y)
|
||||||
|
print(line_a)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run()
|
||||||
Reference in New Issue
Block a user