diff --git a/sanguo_portfolio/runner_backtest.py b/sanguo_portfolio/runner_backtest.py index c99b847..18839d4 100644 --- a/sanguo_portfolio/runner_backtest.py +++ b/sanguo_portfolio/runner_backtest.py @@ -494,6 +494,13 @@ def run_backtest_json(params: Dict[str, Any]) -> Dict[str, Any]: stamp_duty_rate=float(params.get("stamp_duty_rate", 0.001)), min_commission=float(params.get("min_commission", 5.0)), slippage=float(params.get("slippage", 0.0)), + # factor_topn 四参数(2026-09-12:漏透传=矩阵三跑全空仓的根因—— + # CLI --json 走本函数手工拼 Namespace,factor_dir 缺省空串使策略 + # 「factor_dir 未配置」直接空仓跑完) + factor_name=params.get("factor_name", "composite_quant12_v2a"), + factor_dir=params.get("factor_dir", ""), + top_n=int(params.get("top_n", 100)), + rebalance_days=int(params.get("rebalance_days", 21)), initial_positions=json.dumps(params["initial_positions"]) if params.get("initial_positions") else "", ) @@ -829,6 +836,10 @@ def main() -> None: "stamp_duty_rate": args.stamp_duty_rate, "min_commission": args.min_commission, "slippage": args.slippage, + "factor_name": getattr(args, "factor_name", ""), + "factor_dir": getattr(args, "factor_dir", ""), + "top_n": getattr(args, "top_n", 100), + "rebalance_days": getattr(args, "rebalance_days", 21), }) print(json.dumps(result, ensure_ascii=False, default=str)) else: diff --git a/tests/portfolio/test_factor_topn.py b/tests/portfolio/test_factor_topn.py index aa34aae..10f676c 100644 --- a/tests/portfolio/test_factor_topn.py +++ b/tests/portfolio/test_factor_topn.py @@ -170,3 +170,27 @@ def test_initialize_registers_daily_schedule(factor_dir): st.broker.run_daily = lambda fn, t: calls.append((fn, t)) st.initialize(_ctx("2024-10-08")) assert calls == [(st.rebalance, "9:30")] + + +def test_run_backtest_json_passes_factor_topn_params(monkeypatch): + """CLI --json 路径(run_backtest_json 手工拼 Namespace)必须透传 factor 四参—— + 2026-09-12 矩阵三跑全空仓根因: 漏透传→factor_dir=""→策略「未配置」直接空仓, + 而进程内 run_backtest(parse_args()) 路径完整(诊断一直成交掩盖此 bug).""" + from sanguo_portfolio import runner_backtest + captured: dict = {} + + def _fake_run_backtest(args): + captured.update({ + k: getattr(args, k, "") + for k in ("factor_name", "factor_dir", "top_n", "rebalance_days") + }) + return {} + + monkeypatch.setattr(runner_backtest, "run_backtest", _fake_run_backtest) + runner_backtest.run_backtest_json({ + "strategy": "factor_topn", + "factor_name": "composite_test", "factor_dir": "/x/y", + "top_n": 50, "rebalance_days": 10, + }) + assert captured == {"factor_name": "composite_test", "factor_dir": "/x/y", + "top_n": 50, "rebalance_days": 10}