"""A/B the Tiingo backfill's effect on registered replays. Runs the requested strategies through backtest_runner on the current production store, then again with the pre-backfill snapshot (data/us*.csv .bak-tiingo) swapped in, and prints CAGR / Sharpe / MaxDD deltas. Files are swapped under the data_manager panel lock and restored afterwards. PYTHONPATH=. uv run python -m research.prod_impact [STRATEGY ...] """ from __future__ import annotations import os import shutil import sys import metrics import backtest_runner as br import data_manager FILES = ["us.csv", "us_open.csv", "us_high.csv", "us_low.csv", "us_volume.csv"] START = "2018-01-01" def run(names: list[str]) -> dict: r = br.run_market("us", names, None, 10_000.0, 0.001, 2.0, refresh=False, start=START) out = {} for s in r["strategy_runs"]: eq = s["equity"] out[s["name"]] = { "cagr": metrics.annualized_return(eq), "sharpe": metrics.sharpe_ratio(eq.pct_change().dropna()), "mdd": metrics.max_drawdown(eq), } return out def swap(pre_backfill: bool) -> None: """Swap the .bak-tiingo snapshot in or out (live files preserved as .merged-tmp).""" for f in FILES: live = os.path.join("data", f) bak = live + ".bak-tiingo" tmp = live + ".merged-tmp" if not os.path.exists(bak) and not os.path.exists(tmp): continue if pre_backfill: if os.path.exists(bak): shutil.copy2(live, tmp) shutil.copy2(bak, live) else: if os.path.exists(tmp): shutil.copy2(tmp, live) os.remove(tmp) def main() -> None: names = sys.argv[1:] or ["overnight_recovery_top10", "lowrisk_top20"] with data_manager._panel_lock("us"): print("=== merged (Tiingo backfill) ===") a = run(names) swap(pre_backfill=True) try: print("=== pre-backfill snapshot ===") b = run(names) finally: with data_manager._panel_lock("us"): swap(pre_backfill=False) print("\nstrategy cagr% sharpe mdd%") for n in a: print(f"{n:34s} {a[n]['cagr']*100:6.1f}->{b[n]['cagr']*100:6.1f} " f"{a[n]['sharpe']:5.2f}->{b[n]['sharpe']:5.2f} " f"{a[n]['mdd']*100:6.1f}->{b[n]['mdd']*100:6.1f}") if __name__ == "__main__": main()