Skip to content

Latest commit

 

History

History
207 lines (166 loc) · 18.2 KB

File metadata and controls

207 lines (166 loc) · 18.2 KB

quantcore 模块

独立 Git 仓库github.com/W-M-R/quantcore(见 ADR-0007)。本目录在主仓库已 gitignore,通过 quantcore @ file://./quantcore 安装。 纯量化计算库,独立可发布。零 IO,零网络,零数据库 — 输入 DataFrame/dict,输出 dataclass。

一句话定位

把项目里所有"纯算法"代码(评分模型 + 技术指标 + 风险/相关性统计)抽离到这里,让它们:

  • 不依赖 stocklens / api / bot / tickbridge / querybus / pulsefan
  • 仅依赖 numpy + pandas
  • 可独立测试、独立版本、独立发布到 PyPI

目录结构

quantcore/
├── pyproject.toml          独立包定义(pip install -e quantcore/)
├── __init__.py             暴露 ScoringEngine + ScoringOverview + BacktestEngine + EvaluationConfig
├── scoring/                量化评分(18 模型)
│   ├── __init__.py
│   ├── types.py            所有 dataclass 结果类型 + classify_industry / _sf / _unwrap_block + FundamentalContext (M5, 2026-05)
│   ├── fundamental.py      Piotroski F-Score / Altman Z / Ohlson O / Composite Distress
│   ├── valuation.py        Rule of 40 / PEG / FCF Yield / Graham / Beneish M / DuPont / Magic Formula / Dividend Safety
│   ├── sentiment.py        Insider / Options(CBOE 数据,纯计算) / Momentum / Earnings Surprise / Short Interest
│   ├── technical.py        SCTR / Risk(Sharpe/Sortino/Beta) / Macro / Market Regime / Peer Rank
│   └── engine.py           ScoringEngine — 编排 18 模型 + composite_score(0-100) + format_overview_table
├── indicators/             纯技术指标
│   ├── __init__.py
│   ├── trend.py            StockTrendAnalyzer / TrendSnapshot — MA/MACD/RSI/BB Squeeze/RS vs SPY
│   ├── bollinger.py        BollingerResult / MultiBollingerSnapshot + compute_bollinger + update_position_signal
│   ├── resample.py         OHLCV 通用 resample 原语:resample_ohlcv + 5 个具名 helper
│   │                        (weekly / monthly / quarterly / yearly / N-hour) — 双列名约定 ('date' / 'datetime')
│   ├── labels.py           中文标签函数:describe_volume_ratio / compute_ma_status
│   ├── tech.py             FibonacciLevels / SwingPoint / KlinePattern / TechIndicatorSnapshot
│   │                        + detect_swing_points / select_fibonacci_swing / compute_tech_indicators
│   └── changepoint.py      Changepoint / ChangepointResult + detect_changepoints
│                            (二分段 + BIC 惩罚的变点检测,O(N²) 但 N 通常 < 500 故无性能问题)
├── stats/                  统计/风险原语
│   ├── __init__.py
│   ├── risk.py             compute_sharpe / compute_sortino / compute_max_drawdown / compute_volatility
│   │                        + compute_calmar / compute_omega / compute_tail_ratio (2026-05)
│   ├── correlation.py      compute_correlation_pair(成对 stock/benchmark DataFrame → CorrelationResult)
│   ├── hurst.py            compute_hurst — Hurst exponent (R/S analysis), 检测均值回归/趋势型
│   └── garch.py            fit_garch — GARCH(1,1) 波动率预测,纯 numpy MLE + Nelder-Mead 兜底
└── backtest/               回测评估引擎(DB-agnostic 协议接口)
    ├── __init__.py
    └── engine.py           BacktestEngine(中英双语关键词 + 否定识别)/ EvaluationConfig /
                              DailyBarLike, BacktestResultLike Protocol

公开 API

# 评分
from quantcore.scoring import (
    ScoringEngine, ScoringOverview, FundamentalContext,
    calc_piotroski, calc_distress,
    calc_valuation, calc_beneish, calc_dupont,
    calc_options_sentiment, calc_momentum,
    calc_sctr, calc_risk, calc_macro, calc_market_regime,
    classify_industry,
)

# 指标
from quantcore.indicators import (
    StockTrendAnalyzer, TrendSnapshot,
    BollingerResult, MultiBollingerSnapshot, compute_bollinger,
    TechIndicatorSnapshot, FibonacciLevels, compute_tech_indicators,
    resample_ohlcv, resample_to_weekly, resample_to_monthly,
    resample_to_quarterly, resample_to_yearly, resample_hourly,
    describe_volume_ratio, compute_ma_status,
)

# 统计
from quantcore.stats import (
    compute_sharpe, compute_sortino, compute_max_drawdown, compute_volatility,
    compute_calmar, compute_omega, compute_tail_ratio,
    compute_correlation_pair,
    compute_hurst, HurstResult,           # 均值回归 vs 趋势型检测
    fit_garch, GarchResult,               # GARCH(1,1) 波动率预测
)

# 指标 — 变点检测
from quantcore.indicators import detect_changepoints, Changepoint, ChangepointResult

# 回测
from quantcore.backtest import (
    BacktestEngine, EvaluationConfig,
    DailyBarLike, BacktestResultLike, OVERALL_SENTINEL_CODE,
)

关键设计

  • 零 IO 边界:quantcore 内部不可有 requests / akshare / sqlite / 文件读写。所有需要外部数据的模型(如 CBOE 期权情绪)通过 callable 注入直接传 dict 的方式接收已抓取的数据。
  • CBOE 期权情绪的边界calc_options_sentiment(symbol, current_price, cboe_data) 是纯计算;fetch_cboe_options(symbol) 这个网络调用留在 stocklens.services.scoring._cboe_fetcher,由 stocklens 侧的 ScoringService 在构造时通过 cboe_fetcher 参数注入。
  • 基准相关性的边界compute_correlation_pair(stock_df, benchmark_df, ...) 是纯计算;通过 akshare 拉 SPY/QQQ 的 4h 缓存逻辑留在 stocklens.services.tech_indicators_service.compute_correlation
  • 多周期布林带的边界compute_bollinger / resample_to_weekly / resample_hourly 等是纯计算;从 akshare 拉日线 + 雪球拉 60m + 二级缓存的 fetch_bollinger_multi_timeframe 留在 stocklens.services.bollinger_service
  • scoring 命名兼容stocklens.services.scoring.ScoringService 仍可用(它现在是 quantcore.scoring.ScoringEngine 的子类,注入了 CBOE fetcher)。

性能优化(2026-05)

quantcore 内多个热点函数从 Python 循环改为 numpy/pandas 向量化:

函数 文件 优化 预期提速
compute_tech_indicators 中的 OBV indicators/tech.py np.cumsum(np.sign(diff) * volume) 替换 for i in range(...).iloc[i] 30-100×
detect_swing_points indicators/tech.py Series.rolling(window, center=True).max()/min() 替换 O(n × window) 双循环 显著(对长 df)
StockTrendAnalyzer._calc_volume indicators/trend.py rolling(5).mean().shift(1) 一次预计算 5 日均量基线 lookback 倍数级别
calc_options_sentiment 中的 Max Pain scoring/sentiment.py numpy 广播 (strikes[:, None] - strikes[None, :]) 替换 O(M²) Python 双循环 100× (SPY/QQQ 等 200+ 行权价名尤其显著)
compute_correlation_pair Beta/Alpha (F7) stats/correlation.py np.corrcoef / np.cov 替换 tail.cov().iloc[0,1] + 重复 tail[bm].var();从原始 numpy array 直接计算,省 2x2 协方差矩阵的 pandas 对象构造 50-100×

compute_correlation_pair 数值正确性由 tests/unit/test_correlation.py::test_numpy_matches_pandas_reference 锁定(金标准等价性测试,6 位小数精度)。

另外,scoring/types.py:classify_industrylru_cache(maxsize=512) —— 一次 pipeline 运行 industry/sector pair 基数极小(≤30 唯一值),memoize 完美命中。

stocklens 侧薄壳的职责

stocklens 文件 保留职责(quantcore 不做) 委托给 quantcore 的部分
stocklens/services/scoring/__init__.py ScoringService 子类 + 注入 CBOE fetcher + 兼容性 re-export ScoringEngine / 全部 calc_* / 18 个 dataclass
stocklens/services/scoring/{types,fundamental,valuation,sentiment,technical,engine}.py 纯 re-export shim(保留旧导入路径) 全部内容
stocklens/services/scoring/_cboe_fetcher.py fetch_cboe_options 网络调用
stocklens/analyzer/trend_analyzer.py 纯 re-export shim StockTrendAnalyzer / TrendSnapshot
stocklens/services/bollinger_service.py fetch_bollinger_multi_timeframe(akshare/雪球抓取 + 二级缓存) compute_bollinger / 全部 resample / dataclass
stocklens/services/tech_indicators_service.py compute_correlation(akshare 抓 SPY/QQQ + 4h 缓存) compute_tech_indicators / 全部纯算法 helper
stocklens/backtest/engine.py 纯 re-export shim 全部 555 行回测算法(关键词识别 / 止损止盈命中 / 胜率聚合)
stocklens/pipeline/data_collector.py DataCollector 类(IO + 业务编排) _resample_daily / _resample_daily_to_weekly / describe_volume_ratio / compute_ma_status 退化为 quantcore 调用

测试

quantcore 没有自己的 tests/(暂时复用 stocklens 现有测试)。新增独立测试套件的工作量较小,建议拆出后再补。

现有 stocklens 测试中以下文件直接覆盖 quantcore 内核:

  • tests/unit/test_scoring_types.py_sf / classify_industry / dataclass label 推断
  • tests/unit/test_composite_score.pyScoringEngine.calc_composite_score 14 个固定用例

pytest tests/ -q → 341 用例全部通过(拆包前后无差异)。

依赖图

quantcore  ──┐
             ├──→(无任何下游依赖)
             └── 仅 numpy / pandas

stocklens  ──→ quantcore (单向,stocklens 包装 quantcore 加 IO/缓存)
api / bot ──→ stocklens

quantcore 与 tickbridge / querybus / pulsefan 同级,互不依赖。

注意事项(变更日志)

  • 2026-05 M5 — FundamentalContext 共享解析视图:8+ 个评分函数过去都重复 5 行的嵌套 dict 解包:

    earn_raw = fundamental.get("earnings", {}) if isinstance(fundamental, dict) else {}
    earn = _unwrap_block(earn_raw)
    fr = earn.get("financial_report", {}) or {}
    bs = earn.get("balance_sheet", {}) or {}
    bs_prev = earn.get("balance_sheet_prev", {}) or {}

    quantcore.scoring.types.FundamentalContextfrom_dict(fundamental) classmethod)一次性解析为 earnings / financial_report / balance_sheet / balance_sheet_prev / financial_indicators / financial_indicators_prev / cash_flow / income_history / growth / valuation / shares_outstanding / dividend 字段,模型可以按属性访问。便利访问器:net_income() / total_assets() / total_liabilities() / total_equity() / revenue() / operating_cash_flow() / prev_net_income() / ebit()

    FundamentalContext.ebit() 是 EBIT 兜底链的单一来源(先取 income_history[0].operating_income,再取 financial_report.operating_profit,最后 net_profit_parent + |interest_expense| + |income_tax|)—— 之前 calc_altman_z / calc_distress / calc_magic_formula 各自重复这套兜底,细节不完全一致。

  • 2026-05 F1 / F2 — 全部 13 个评分函数迁移到 FundamentalContext:M5 的工具落地。fundamental.py 中的 calc_piotroski / calc_mohanram / calc_altman_z / calc_ohlson_o / calc_distress,以及 valuation.py 中的 calc_valuation / calc_beneish / calc_dupont / calc_earnings_quality / calc_magic_formula 全部改为 ctx = FundamentalContext.from_dict(fundamental) 然后用 ctx.financial_report / ctx.net_income() / ctx.ebit() 访问。

    _unwrap_block 仍然导出(外部用户可能用),但 quantcore 内部已经不再调用。新增 30 个回归测试 (tests/unit/test_fundamental_context.py) 覆盖:dataclass 构造(含 None / 非 dict / 部分缺失的 5 个边界)、所有便利访问器、3 级 EBIT 兜底链(含负值 abs 处理)、与旧 _unwrap_block 路径的等价性、以及 8 个迁移后的评分函数端到端冒烟。

  • 2026-05 高级量化原语接入(开源算法替代品)

    • stats/hurst.pyHurst 指数 (R/S analysis),区分均值回归 (H<0.45) / 随机游走 (0.45≤H≤0.55) / 趋势型 (H>0.55)。Mandelbrot & Wallis (1969) 标准 R/S 算法,纯 numpy 实现,无需引入 hurst 第三方库。直接影响策略选择(趋势策略 vs 均值回归策略)。
    • stats/garch.pyGARCH(1,1) 波动率预测,纯 numpy MLE + Nelder-Mead 单纯形优化器作为 scipy 不可用时的兜底(替代 arch 第三方库)。输出 ω/α/β/persistence + 长期波动率 + H 步预测。比静态历史标准差准 30-50%,对风险评估和期权定价价值大。
    • indicators/changepoint.py变点检测 (Binary Segmentation + BIC penalty),纯 numpy 实现(替代 ruptures 第三方库)。识别价格/收益序列中的均值漂移点,比 MA 交叉更早识别趋势转折,自动附 ISO 日期串方便报告。复杂度 O(N²) 在 N≤500 时无性能问题。
    • 新增 tests/unit/test_advanced_quant.py — 14 个测试用例覆盖 Hurst(5)/ GARCH(4)/ Changepoint(5),合成数据验证已知性质(H≈0.5 of 随机游走、α+β<1 of 平稳 GARCH、变点位置接近真实分割点)。
    • 测试套件总数 511 → 525,全部通过。
  • 2026-05 新增模型与风险指标

    • scoring/fundamental.py — 新增 Mohanram G-Score (0-8):成长股质量评分(Mohanram 2005),8 个信号覆盖盈利能力/稳定性/成长投入。与 Piotroski F-Score 互补(F-Score 适合价值股,G-Score 适合成长股)。engine 中两者共存,composite_score 在两者都有时分配 F-Score 10pts + G-Score 2pts,仅有 F-Score 时保持原 12pts(向后兼容)。
    • scoring/fundamental.py — 新增 Altman Z'' Emerging Market 变体(Altman 2005):calc_altman_z 新增 market 参数,当 market="A""HK" 时使用 Z''_EM = 3.25 + 6.56X1 + 3.26X2 + 6.72X3 + 1.05X4,阈值 5.85/4.15(比 US modified 版更适合新兴市场资产结构)。calc_distress 同步透传 market 参数。
    • stats/risk.py — 新增 3 个风险指标(纯 numpy/pandas 实现,无外部依赖):
      • Calmar ratio(Young 1991):年化收益 / |最大回撤|,衡量每单位回撤风险的回报
      • Omega ratio(Keating & Shadwick 2002):概率加权收益 / 概率加权损失,比 Sharpe 更全面(不假设正态分布)
      • Tail ratio:|95th percentile| / |5th percentile|,衡量收益分布尾部不对称性
    • stats/risk.pySortino ratio 修正:从 excess[excess < 0].std()(仅负收益的 std)改为连续下行偏差 sqrt(mean(min(R-MAR, 0)²)),与 Sortino & van der Meer (1991) 原始定义一致(所有观测值参与计算,正收益贡献 0)。
    • scoring/types.pyRiskResult 新增 calmar_60d / omega_60d / tail_ratio_60d 字段;新增 MohanramResult dataclass;ScoringOverview 新增 mohanram 字段;AltmanResult.version 新增 "emerging" 取值。
  • 2026-05 量化模型正确性修复

    • scoring/technical.py — Sortino ratio 不再用 Sharpe * 1.2 伪近似(无学术依据)。改为从上游 tech_indicators 对象直接读取 sortino_20d / sortino_60d(由 quantcore.stats.risk.compute_sortino 从原始收益序列计算下行标准差得出)。无数据时保持 None
    • scoring/fundamental.py — Altman Z-Score 和 Composite Distress 的 EBIT 估算从 NI * 1.25(无依据的固定倍数)改为 NI + |Interest Expense| + |Income Tax|(标准会计恒等式 EBIT = NI + IE + Tax)。
    • scoring/fundamental.py — Ohlson O-Score 的 GNP deflator 从硬编码 650.0 改为可配置参数 gnp_index(默认 700.0,更接近 2025 BEA 实际值)。函数签名新增 gnp_index: float = DEFAULT_GNP_INDEX
    • scoring/valuation.py — Beneish M-Score 修复 DSRI bug:原代码 (recv/revenue)/(recv/rev_prev) 数学退化为 rev_prev/revenue(分子分母用同一个 recv)。修正为正确公式 (recv_t/revenue_t) / (recv_prev/rev_prev),需要上期应收账款。同时补全 AQI(Asset Quality Index)、DEPI(Depreciation Index)、SGAI(SGA Expense Index)三个原先硬编码为 1.0 的变量——当数据可用时计算真实值,不可用时标注为 defaulted。结果 details 字段标注 N/8-var 和 defaulted 变量列表。
    • scoring/valuation.py — Graham Number 的 EPS 优先级修正:优先使用 trailing EPS(符合 Benjamin Graham 原始公式),其次从 NI/shares 推导,最后才 fallback 到 forward EPS(标注为 less accurate)。
    • scoring/valuation.py — Magic Formula 从概念错误的 1/PE(E/P on equity)改为正确的 EBIT/EV(Greenblatt 原始定义),ROIC 从 ROE fallback 改为优先计算 EBIT / Invested Capital(IC = NWC + Net Fixed Assets)。当数据不足时 fallback 到旧方法并在 details 中标注 (fallback)
    • scoring/sentiment.py — Earnings Surprise 诚实标注:实际计算的是 EPS QoQ Growth(环比增长率),不是真正的 SUE(需要分析师一致预期数据)。zone 标签从"超预期/不及预期"改为"正增长/下滑",details 标注 (QoQ growth, not analyst surprise)
    • scoring/sentiment.py — Momentum 改进:优先使用真实 12-1 month momentum(P_1m_ago / P_12m_ago - 1,Jegadeesh & Titman 1993),当 trend dict 提供 price_12m_agoprice_1m_ago 时启用。无历史价格时 fallback 到 price vs MA200 并标注 (proxy, not true 12-1M)
    • scoring/sentiment.py — Options Max Pain 计算修复变量遮蔽:numpy 数组从 calls/puts/strikes 重命名为 call_oi_arr/put_oi_arr/strikes_arr,避免覆盖外层同名 list 变量。
  • 2026-05 清理死变量

    • quantcore/scoring/fundamental.py — Ohlson O-Score 计算只需 total_assets/total_liab/net_profit_parent/operating_cash_flow/current_ratio,原代码多提取了 total_equity/total_cash 但从未使用;composite distress 函数也多余提取了 cash_flow/total_assets。已删除,函数行为不变。
    • quantcore/indicators/tech.py — 晨星模式(Morning Star Doji)的判定逻辑里 body2 = abs(c2["c"] - c2["o"])range2 = c2["h"] - c2["l"] 计算了但从未参与判定(公式只需 c2["c"] < c2["o"]body1/range1 < 0.2),属研究层面遗留。已删除。