fix: unify backtest execution and add forward validation candidates

Share point-in-time signals and a cash-funded order ledger across backtests and trading. Correct eligibility, missing-price fills, quality factors, regime inputs, warmup windows, and cash-injection accounting. Add reproducible registered-strategy comparisons and paired forward candidates with a 20% resize band.

Validation: 125 Python tests, 23 web tests, TypeScript/Vite build, 87 strategy causality checks, and real-data target/order/equity parity for 10 strategies.
This commit is contained in:
2026-10-01 02:24:01 -07:00
parent 761725d2c8
commit 722cade974
45 changed files with 1561 additions and 1021 deletions

View File

@@ -27,7 +27,7 @@ from datetime import datetime
import pandas as pd
from backtest_runner import run_market
from backtest_runner import run_market, ENGINE_VERSION
from bridge import (
STRATEGY_META,
_build_strategy_result,
@@ -102,6 +102,7 @@ def build_scenario_for_market(
try:
result = _build_strategy_result(
run["name"], equity, run["trades"], injection_schedule, capital,
execution_plan=run.get("_plan"), fee_params=run.get("_fees"),
)
strategy_results.append(result)
except Exception as e:
@@ -124,13 +125,18 @@ def build_scenario_for_market(
return {
"meta": {
"market": raw["market"],
"years": years,
"years": raw.get("actual_years", years),
"requestedYears": years,
"initialCapital": capital,
"startDate": raw["start_date"],
"endDate": raw["end_date"],
"txCost": raw["tx_cost"],
"fixedFee": raw["fixed_fee"],
"injectionSchedule": injection_schedule,
"engineVersion": raw.get("engine_version"),
"warnings": raw.get("warnings", []),
"coverage": raw.get("coverage"),
"errors": raw.get("errors", []),
},
"strategies": strategy_results,
"benchmark": {
@@ -150,6 +156,7 @@ def main():
"--years", type=int, default=10,
help="Backtest period in years (default: 10)",
)
parser.add_argument("--offline", action="store_true", help="Use the frozen local data snapshot")
args = parser.parse_args()
output_dir = args.output or os.path.join(
@@ -182,7 +189,7 @@ def main():
"us", us_strategies,
years=args.years, capital=us_capital,
tx_cost=0.001, fixed_fee=2.0,
fee_base=1.88, fee_per_share=0.009,
fee_base=1.88, fee_per_share=0.009, refresh=not args.offline,
)
log("=" * 60)
@@ -191,7 +198,7 @@ def main():
cn_raw = run_market(
"cn", cn_strategies,
years=args.years, capital=cn_capital,
tx_cost=0.001, fixed_fee=5.0,
tx_cost=0.001, fixed_fee=5.0, refresh=not args.offline,
)
# Materialize every scenario × market by applying its injection schedule.
@@ -218,13 +225,14 @@ def main():
output = {
"computedAt": datetime.now().isoformat(),
"config": {"years": args.years, "initialCapital": MARKET_CAPITAL},
"config": {"years": args.years, "initialCapital": MARKET_CAPITAL, "engineVersion": ENGINE_VERSION},
"scenarios": scenarios_out,
}
output_path = os.path.join(output_dir, "backtest_results.json")
with open(output_path, "w") as f:
with open(output_path + ".tmp", "w") as f:
json.dump(_sanitize_for_json(output), f, default=str)
os.replace(output_path + ".tmp", output_path)
file_size = os.path.getsize(output_path) / (1024 * 1024)
log("=" * 60)