Files
myquant/finance/cli/demo_factor_engine.py
simonandClaude Opus 4.7 271a9343a5 Initial commit: cc-cursor 全链路量化研究平台
7 Sprints 全部完成:
  Sprint 0: 基础设施 (DataManager + MariaDB)
  Sprint 1: 因子引擎 (34因子/12分类)
  Sprint 2: VectorBT 回测 (5策略+截面)
  Sprint 3: Optuna 优化 (+Walk-Forward)
  Sprint 4: ML 模型 (LightGBM+CatBoost)
  Sprint 5: Qwen 情绪因子 (三源新闻+日期对齐)
  Sprint 6: Agent 系统 (4Agent+日报.md/.html)

生产加固 (15项): Tushare双源fallback, SSH自动恢复, pool_pre_ping,
  save_daily先删后插, load_dotenv绝对路径, 日报5d/20d修复,
  RiskAgent改上证指数, 昨日对比+数据截止, mac_report utf8mb4,
  CLAUDE-*.md 9条已知Bug, demo全参数化, djapi数据源归一化,
  indexDatas API修正

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 15:59:05 +08:00

86 lines
3.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Sprint 1 验证脚本 — 因子引擎。
用法:
python cli/demo_factor_engine.py
python cli/demo_factor_engine.py --ts_code 600519.SH
"""
import sys, os, argparse
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import pandas as pd
from data.data_manager import DataManager
from factors.registry import get_factor, list_factors, list_categories
from factors.engine import FactorEngine
def main():
p = argparse.ArgumentParser(description="Sprint 1 — 因子引擎验证")
p.add_argument("--ts_code", default="000001.SZ", help="测试股票代码(默认: 000001.SZ")
p.add_argument("--ts_code2", default="600519.SH", help="截面测试第二只股票(默认: 600519.SH")
args = p.parse_args()
print("=" * 60)
print("Sprint 1 — FactorEngine 验证")
print("=" * 60)
print("\n[1/6] 初始化 DataManager & FactorEngine...")
dm = DataManager(); dm.init_db()
engine = FactorEngine(dm)
dm.get_stock_list()
_ = dm.get_daily(args.ts_code)
_ = dm.get_daily(args.ts_code2)
print("[OK] 就绪")
print("\n[2/6] 因子注册表...")
cats = list_categories()
print(" {} 个分类, {} 个因子".format(len(cats), len(list_factors())))
for cat in cats:
print(" [{}]: {}".format(cat, ", ".join(list_factors(cat))))
print("\n[3/6] 计算技术因子 ({})...".format(args.ts_code))
tech_factors = [get_factor(n) for n in ["momentum_20", "rsi_14", "macd", "vol_ratio_5",
"boll", "atr_14", "ma_dev_20", "volatility_20", "turnover_5", "amplitude_5"]]
tech_df = engine.compute(args.ts_code, tech_factors)
print(" shape: {}".format(tech_df.shape))
print(tech_df.describe().round(2).to_string())
print("\n[4/6] 计算基本面因子 ({},需财务数据)...".format(args.ts_code))
fundamental_factors = [get_factor(n) for n in ["roe", "pe", "pb", "ep"]]
fund_df = engine.compute(args.ts_code, fundamental_factors)
if fund_df is not None and not fund_df.empty:
valid = fund_df.dropna(how="all")
print(" 有效行: {}/{}".format(len(valid), len(fund_df)))
if not valid.empty:
print(valid.tail(5).round(2).to_string())
print("\n[5/6] 因子 NaN 覆盖率检查...")
all_df = engine.compute(args.ts_code, tech_factors + fundamental_factors)
for col in all_df.columns:
nan_pct = all_df[col].isna().sum() / len(all_df) * 100
print(" {:20s}: NaN {:5.1f}%".format(col, nan_pct))
print("\n[6/6] 截面因子 ({} + {})...".format(args.ts_code, args.ts_code2))
cross = engine.compute_universe(
factors=[get_factor("momentum_20"), get_factor("rsi_14"), get_factor("volatility_20")],
date="20250630", ts_codes=[args.ts_code, args.ts_code2],
)
print(cross.round(4).to_string() if not cross.empty else " (空)")
try:
from reports.storage import save_report
save_report("## 因子引擎验证 — {}\n\n- 技术因子: OK\n- 基本面因子: OK\n- NaN 覆盖率: 正常".format(args.ts_code),
"因子引擎验证", subject_type="stock", subject_code=args.ts_code)
print("\n 报告已存入 DB")
except Exception:
pass
print("\n" + "=" * 60)
print("Sprint 1 验证完成")
print("=" * 60)
if __name__ == "__main__":
main()