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:
@@ -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}] "
|
||||
|
||||
Reference in New Issue
Block a user