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>
This commit is contained in:
47
research/lab/baselines.py
Normal file
47
research/lab/baselines.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user