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

@@ -20,6 +20,7 @@ from research.lab.engine import Costs, simulate
from research.lab.factor_scan import RESULTS, load
from research.lab.report import deflated_sharpe, stats, yearly
from research.lab.stepwise import SUBPERIODS
from research.lab.trials import count_trials
warnings.filterwarnings("ignore")
@@ -81,7 +82,15 @@ def main() -> None:
ap.add_argument("--market", default="us")
ap.add_argument("--add", nargs="*", default=[], help="extra 'name=combo|top|every|buffer'")
ap.add_argument("--n-trials", type=int, default=0, help="override trial count for DSR")
ap.add_argument("--registered", nargs="+", help="Validate registered strategies using the production ledger")
ap.add_argument("--start")
ap.add_argument("--end")
ap.add_argument("--output")
args = ap.parse_args()
if args.registered:
from research.lab.registered import evaluate_registered
evaluate_registered(args)
return
mk = "us" if args.market == "us" else "cn"
p, _ = load(args.market)
cands = dict(CANDIDATES[mk])
@@ -89,8 +98,9 @@ def main() -> None:
name, rest = spec.split("=", 1)
combo, top, every, buf = rest.split("|")
cands[name] = (combo, dict(top_n=int(top), every=int(every), buffer=float(buf)))
trials_path = os.path.join(RESULTS, f"trials_{args.market}.csv")
n_trials = args.n_trials or (len(pd.read_csv(trials_path)) if os.path.exists(trials_path) else 100)
n_trials = args.n_trials or count_trials(RESULTS, args.market)
if n_trials < 1:
raise ValueError("DSR needs a trial ledger or an explicit --n-trials count")
full = (SUBPERIODS[mk][0][0], WINDOW[1])
out = {}
fee, cap = LIVE_FEES[mk]
@@ -115,7 +125,8 @@ def main() -> None:
expo = (0.5 + 0.25 * slow + 0.25 * fast).where(slow + fast < 2, 1.0)
res["window_trend_overlay"] = summarize(phase_runs(p, s, *WINDOW, exposure=expo, **kw), *WINDOW)
dsr = deflated_sharpe(res["full"]["_rets"], n_trials)
res["dsr_full"] = dsr
res["dsr_full"] = {**dsr, "observed_trials_lower_bound": n_trials,
"evidence_role": "development, not independent OOS"}
out[name] = res
w, f = res["window"], res["full"]
print(f"{name:42s} 2016-26: CAGR {w['cagr']*100:5.1f}% [{w['cagr_phase_min']*100:4.1f},{w['cagr_phase_max']*100:4.1f}] "