Files
quant-trading/research/lab/run_ml.py
Gahow Wang 0e378efa67 research: point-in-time factor research harness (research/lab)
Event-driven long-only simulator with next-open fills, CN tradability
(halts, limit-up/down, stamp duty), PIT S&P 500 / CSI 300 membership,
rebalance-phase averaging, factor IC scans, maximin stepwise composite
search across 5-year blocks, walk-forward LightGBM/ridge combiners,
overlays, and deflated Sharpe. Fetchers pull 20y US OHLCV from Yahoo and
CSI 300/500 ever-members (membership snapshot from baostock, bars from
Yahoo). Research data lives in data/research/ (ignored).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 19:11:59 +08:00

53 lines
1.9 KiB
Python

"""
Walk-forward ML combination experiment.
PYTHONPATH=. uv run --with lightgbm python -m research.lab.run_ml --market us
"""
import argparse
import os
import pickle
import time
import warnings
from research.lab import ml
from research.lab.factor_scan import load, phase_avg
from research.lab.factors import FACTORS
from research.lab.report import fmt
warnings.filterwarnings("ignore")
OUT = os.path.join("data", "research", "results")
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--market", default="us")
ap.add_argument("--models", nargs="*", default=["lgbm", "ridge", "ic_weight"])
ap.add_argument("--horizons", nargs="*", type=int, default=[5, 20])
ap.add_argument("--first-test-year", type=int, default=2010)
args = ap.parse_args()
mk = "us" if args.market == "us" else "cn"
p, periods = load(args.market)
names = [n for n, f in FACTORS.items() if mk in f.markets]
t = time.time()
feats = ml.feature_ranks(p, names)
print(f"{len(names)} features in {time.time() - t:.0f}s", flush=True)
evalp = {"design": (f"{args.first_test_year}-01-01", periods["design"][1]), "holdout": periods["holdout"]}
os.makedirs(OUT, exist_ok=True)
for model in args.models:
for h in args.horizons:
t = time.time()
score = ml.walk_forward(p, feats, model=model, horizon=h, first_test_year=args.first_test_year)
with open(os.path.join(OUT, f"ml_{args.market}_{model}_h{h}.pkl"), "wb") as fh:
pickle.dump(score, fh)
for top_n in (10, 20):
parts = []
for per, (a, b) in evalp.items():
parts.append(f"{per}: {fmt(phase_avg(p, score, a, b, top_n, 5))}")
print(f"{model:9s} h{h:<3d} top{top_n:<3d}", " | ".join(parts), f"[{time.time() - t:.0f}s]", flush=True)
if __name__ == "__main__":
main()