feat: VBT visualization + validation pipeline, HFT tick viz, DuckDB loader

Track 1 — VBT Candle-Frequency Pipeline:
- backtests/vbt_validator.py: VBTValidator with 11 checks — timestamp monotonicity,
  duplicates, NaN, data gaps, lookahead bias, signal alignment, density,
  coincident entry/exit, min trade count, fee application, benchmark comparison.
  ValidationReport dataclass with errors/warnings/stats. Validates VBT results
  or raw signal arrays.
- backtests/vbt_viz.py: VBTVisualizer with 10+ Plotly chart methods — equity
  curve with benchmark, drawdown, rolling Sharpe/Sortino/vol, trade markers,
  returns distribution with normal fit, monthly PnL heatmap, gross vs net,
  holding periods, parameter sensitivity heatmaps, dashboard compositor,
  HTML save (self-contained, CDN Plotly). All methods handle empty/null inputs.
- backtests/vbt_report.py: Markdown + HTML report generator — structured
  sections for implementation summary, performance metrics, cost analysis,
  validation results, signal analysis, known limitations, next steps.
  batch_report() for mass report generation from results directory.
- backtests/vbt_runner.py: Added run_benchmark() (buy-and-hold VBT portfolio),
  validate() (integrated VBTValidator), run_with_report() (fetch→validate→
  backtest→visualize→save in one call).

Track 2 — HFT Tick Pipeline:
- backtests/tick_viz.py: 9-panel HFT dashboard — price+trade markers,
  spread dynamics, top-of-book depth, microprice vs mid, OBI/OFI panel,
  VPIN toxicity with thresholds, event timeline (PnL from tick_runner),
  markout curves at 6 horizons. Parquet→pandas→Plotly pipeline.
  Dark-themed HTML output for microstructure review.
- data/duckdb_load.py: Parquet→DuckDB loader — creates l2_snapshots,
  trades, funding tables with schema. Pre-computed 1s rollup views for
  microprice, OFI, trade imbalance. Markout queries directly in SQL.
  Incremental loading with load_state tracking.

CLI Integration:
- cli.py: Added 'report' (full VBT report), 'validate' (check existing
  results), 'hft' (tick dashboard generation) commands. Fixed argparse
  help string escaping.

355 tests passing (34 new).
This commit is contained in:
ramseshk
2026-08-11 12:22:11 +08:00
parent 09cb0d42b5
commit 20ee340cef
8 changed files with 3332 additions and 2 deletions
+122 -2
View File
@@ -545,6 +545,98 @@ def cmd_funding(args):
print(f" Then 'python -m cli funding --coin BTC' to re-run.")
def cmd_report(args):
"""Generate a combined VBT report: backtest + validation + visualization."""
import json as _json
from backtests.vbt_runner import VBTBacktestRunner
runner = VBTBacktestRunner()
result = runner.run_with_report(
strategy=args.strategy,
interval=args.interval,
testnet=False,
limit=args.limit,
output_dir=args.output_dir,
)
if result:
print(f"Report generated for {args.strategy} ({args.interval})")
print(f" Strategy: {result['strategy']}")
print(f" Sharpe: {result.get('sharpe', 0):.3f}")
print(f" Net PnL: ${result.get('pnl', 0):.2f}")
print(f" Trades: {result.get('total_trades', 0)}")
print(f" Validation: {len(result.get('validation_errors', []))} errors, "
f"{len(result.get('validation_warnings', []))} warnings")
print(f" Output: {args.output_dir}/")
else:
print(f"No data available for {args.strategy}. "
f"Try: python -m cli backtest --strategy {args.strategy} --interval {args.interval}")
def cmd_validate(args):
"""Validate existing backtest results without re-running."""
import json as _json
from pathlib import Path
from backtests.vbt_validator import VBTValidator, ValidationReport
rd = Path(args.results_dir)
files = sorted(rd.glob("*.json"))
if not files:
print(f"No backtest results found in {args.results_dir}")
return
validator = VBTValidator()
total = 0
passed = 0
for fp in files:
try:
data = _json.loads(fp.read_text())
except Exception:
continue
strat = data.get("strategy", "?")
if args.strategy != "all" and args.strategy.lower() != strat.lower():
continue
total += 1
report = ValidationReport(
strategy=strat,
interval=data.get("interval", "?"),
)
trades = data.get("trades", [])
n_trades = data.get("total_trades", len(trades))
if n_trades < 10:
report.warnings.append(
f"{fp.name}: only {n_trades} trades — insufficient for stats"
)
sharpe = data.get("sharpe", 0)
if n_trades > 0 and abs(sharpe) > 5:
report.warnings.append(
f"{fp.name}: extreme Sharpe {sharpe:.2f} with {n_trades} trades"
)
if report.errors or report.warnings:
print(report.summary())
else:
passed += 1
print(f"\n{passed}/{total} backtests clear validation")
if total > 0 and passed == 0:
print("⚠ All backtests have warnings/errors. Review needed.")
print(f"\nFull validation requires re-running with VBTValidator.validate().")
print(f"Use: python -m cli report --strategy <name> for full validation.")
def cmd_hft_viz(args):
"""Generate HFT tick visualization dashboard."""
from backtests.tick_viz import cmd_tick_viz
cmd_tick_viz(args)
def main():
import argparse
p = argparse.ArgumentParser(description="FTDT Quant Lab CLI")
@@ -609,8 +701,8 @@ def main():
pt.add_argument("--skew-factor", type=float, default=0.5)
pt.add_argument("--vpin-threshold", type=float, default=0.30)
pt.add_argument("--vpin-alarm", type=float, default=0.50)
pt.add_argument("--maker-fee", type=float, default=0.02, help="Maker fee in %")
pt.add_argument("--taker-fee", type=float, default=0.05, help="Taker fee in %")
pt.add_argument("--maker-fee", type=float, default=0.02, help="Maker fee (e.g. 0.02 = 2bps)")
pt.add_argument("--taker-fee", type=float, default=0.05, help="Taker fee (e.g. 0.05 = 5bps)")
pt.add_argument("--adverse-prob", type=float, default=0.15)
pt.add_argument("--cancel-after-ms", type=float, default=5000.0)
pt.add_argument("--quote-refresh-ms", type=float, default=2000.0)
@@ -631,6 +723,28 @@ def main():
pf.add_argument("--start-date", default="2026-01-01")
pf.add_argument("--end-date", default="2030-01-01")
# report
prp = sp.add_parser("report", help="Generate VBT backtest report (Markdown + HTML + dashboard)")
prp.add_argument("--strategy", default="pairs")
prp.add_argument("--interval", default="1h")
prp.add_argument("--limit", type=int, default=5000)
prp.add_argument("--output-dir", default="backtests/reports")
prp.add_argument("--format", default="html", choices=["md", "html"])
# validate
pv = sp.add_parser("validate", help="Validate existing backtest results without re-running")
pv.add_argument("--strategy", default="all", help="Strategy name or 'all'")
pv.add_argument("--results-dir", default="backtests/results")
# hft
ph = sp.add_parser("hft", help="Generate HFT tick visualization dashboard")
ph.add_argument("--data-dir", default="data/raw")
ph.add_argument("--coin", default="BTC")
ph.add_argument("--start-date", default="2026-08-01")
ph.add_argument("--end-date", default="2026-08-02")
ph.add_argument("--tick-result", default=None, help="Path to tick_runner JSON result")
ph.add_argument("--output-dir", default="backtests/reports")
args = p.parse_args()
import json as _json
@@ -653,6 +767,12 @@ def main():
cmd_discover(args)
elif args.command == "funding":
cmd_funding(args)
elif args.command == "report":
cmd_report(args)
elif args.command == "validate":
cmd_validate(args)
elif args.command == "hft":
cmd_hft_viz(args)
if __name__ == "__main__":