feat(factor): challenger CLI+月度链 stage6 接线(影子失败不阻链;宽表源=stage4 截面盘,月度批 values 不含 12 源) [nas]
This commit is contained in:
@@ -203,3 +203,24 @@ def run_challenge(values_dir: str, vnpy_db: str, as_of: str, out_dir: str,
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
json.dump(doc, f, ensure_ascii=False, indent=2)
|
||||
return path
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
import argparse
|
||||
ap = argparse.ArgumentParser(description="LGBM challenger 三路对拍(月度链 stage6)")
|
||||
ap.add_argument("--values-dir", required=True,
|
||||
help="因子截面宽表目录(每因子一 parquet;NAS=stage4 截面盘"
|
||||
" /volume1/stock/factor_cross_section,月度批 values/ 只含"
|
||||
"注册表因子不含 quant12 12 源)")
|
||||
ap.add_argument("--as-of", required=True)
|
||||
ap.add_argument("--out-dir", required=True, help="报告根(factor_monthly)")
|
||||
ap.add_argument("--vnpy-db", default=None)
|
||||
ap.add_argument("--host", default="nas")
|
||||
a = ap.parse_args(argv)
|
||||
try:
|
||||
path = run_challenge(a.values_dir, a.vnpy_db, a.as_of, a.out_dir, host=a.host)
|
||||
except FileNotFoundError as e:
|
||||
print(f"[challenger] skip: {e}")
|
||||
return 0 # 无宽表=非错误(截面盘未建),不阻链
|
||||
print(f"[challenger] 对拍件: {path}")
|
||||
return 0
|
||||
|
||||
@@ -156,6 +156,25 @@ PY
|
||||
rc_replay=$?
|
||||
echo "=== $(date '+%F %T') stage5 verdict_replay exit=$rc_replay (派生件,不进 final) ==="
|
||||
|
||||
# stage6 challenger 三路对拍(影子实验,失败不阻月度链;产物落子目录
|
||||
# 判定层端点只读根层零影响)。宽表源=stage4 截面盘(12 源主 parquet,
|
||||
# daily_section 幂等 merge 累积)——月度批 values/ 只含注册表因子,
|
||||
# 不含 quant12 12 源(2026-10-10 现场核实 NAS values/monthly_2026-09)。
|
||||
"$DOCKER" run --rm --name sanguo-factor-chal --user "${UID_ADMIN}:${GID_ADMIN}" \
|
||||
--group-add "${GID_ADMINS}" --no-healthcheck --entrypoint python \
|
||||
-e HOME=/tmp -e MPLCONFIGDIR=/tmp/mpl \
|
||||
-v /volume1/stock:/volume1/stock \
|
||||
-v "$APP":/app:ro \
|
||||
-w /app \
|
||||
sanguo_vnpy_v2:lock-aligned \
|
||||
-m sanguo_factor.challenger_lgbm \
|
||||
--values-dir /volume1/stock/factor_cross_section \
|
||||
--as-of "$AS_OF" --out-dir "$OUT_DIR" \
|
||||
--vnpy-db /volume1/stock/sanguo_vnpy_v2/data_backup/quant_trading.db \
|
||||
--host nas
|
||||
rc_chal=$?
|
||||
echo "=== $(date '+%F %T') stage6 challenger exit=$rc_chal (影子,不进 final) ==="
|
||||
|
||||
# 真失败=②或③ rc=1(读件错);exit 2 是信号按 0 处理;④ 非零=真失败
|
||||
final=0
|
||||
[ "$rc_review" -eq 1 ] && final=1
|
||||
|
||||
@@ -145,3 +145,22 @@ def test_run_challenge_end_to_end(tmp_path, monkeypatch):
|
||||
assert doc["pool"]["names"] == ["s0", "s1"]
|
||||
assert len(doc["feature_importance"]) == 2
|
||||
assert doc["split"]["oos_month"] == "2025-03"
|
||||
|
||||
|
||||
def test_cli_smoke(tmp_path, monkeypatch, capsys):
|
||||
from sanguo_factor import challenger_lgbm as cl
|
||||
idx = pd.date_range("2025-01-01", "2025-03-31", freq="B")
|
||||
close = pd.DataFrame(100 + np.cumsum(np.random.default_rng(1).normal(0.4, size=(len(idx), 2)), axis=0),
|
||||
index=idx, columns=["a", "b"])
|
||||
monkeypatch.setattr(cl, "_load_close_wide", lambda db, c, s, e: close)
|
||||
monkeypatch.setattr(cl, "load_pool", lambda: {
|
||||
"s0": {"direction": "+", "weight": 0.6},
|
||||
"s1": {"direction": "-", "weight": 0.4}})
|
||||
vdir = tmp_path / "vals"; vdir.mkdir()
|
||||
for n in ("s0", "s1"):
|
||||
pd.DataFrame(np.random.default_rng(2).normal(size=(len(idx), 2)),
|
||||
index=idx, columns=["a", "b"]).to_parquet(vdir / f"{n}.parquet")
|
||||
rc = cl.main(["--values-dir", str(vdir), "--as-of", "2025-03-31",
|
||||
"--out-dir", str(tmp_path), "--vnpy-db", "fake.db"])
|
||||
assert rc == 0
|
||||
assert (tmp_path / "challenger_lgbm" / "nas_2025-03-31.json").exists()
|
||||
|
||||
Reference in New Issue
Block a user