Files
quant-trading/research/lab/baselines.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

48 lines
1.5 KiB
Python

"""
Re-run the existing factor_combo champions under the research engine, with
the same rules every new candidate gets: next-open execution, PIT universe,
costs, and averaging over rebalance phases.
PYTHONPATH=. uv run python -m research.lab.baselines --market us
"""
import argparse
import warnings
import pandas as pd
from research.lab.factor_scan import load, phase_avg
from research.lab.report import fmt
from strategies.factor_combo import SIGNAL_REGISTRY
warnings.filterwarnings("ignore")
CHAMPIONS = {
"us": ["rec_mfilt+deep_upvol", "ma200+mom7m+rec126", "rec_mfilt+ma200", "mom7m+rec126"],
"cn": ["up_cap+quality_mom", "down_resil+qual_mom", "rec63+mom_gap", "up_cap+mom_gap"],
}
def baseline_scores(p, market: str) -> dict[str, pd.DataFrame]:
mk = "us" if market == "us" else "cn"
# factor_combo signals were built on prices masked to index membership
px = p.close.where(p.member).ffill(limit=5)
return {name: SIGNAL_REGISTRY[name](px) for name in CHAMPIONS[mk]}
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--market", default="us")
args = ap.parse_args()
p, periods = load(args.market)
scores = baseline_scores(p, args.market)
for name, s in scores.items():
for every, label in [(5, "weekly"), (21, "monthly")]:
for per, (a, b) in periods.items():
st = phase_avg(p, s, a, b, 10, every)
print(f"{name:22s} top10 {label:8s} {per:8s} {fmt(st)}", flush=True)
if __name__ == "__main__":
main()