diff --git a/README.md b/README.md index 38aca67..c3a557b 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,16 @@ Event-driven quantitative backtesting framework for China A-share market. - **Event-driven backtesting** — `initialize` → `run_daily` → `handle_data`, compatible with JoinQuant / Zipline programming model - **A-share data** — daily OHLCV, minute K-lines, tick data, real-time quotes, fundamentals, money flow via free AKShare API - **Position management** — buy/sell by shares, value, or target; automatic lot-size rounding (100 shares), commission calculation -- **Risk analysis** — Sharpe, Sortino, max drawdown, alpha/beta, Brinson attribution, Fama-French factor analysis +- **Risk analysis** — Sharpe, Sortino, max drawdown, alpha/beta, Brinson attribution, simple factor analysis - **Portfolio optimization** — minimum variance, maximum Sharpe, risk parity - **Paper trading** — run strategies live with real-time market data before going live - **PTrade/QMT adapter** — one-click export to broker platforms - **Stock selection** — periodic rebalancing with factor screening (ST/PB/PE/momentum filters, Top-N, multi-factor scoring) -- **Utility library** — 30+ technical indicators (MA, MACD, RSI, KDJ, Bollinger, ATR, ADX), statistical tools, position sizing (Kelly, ATR-based, fixed fractional) +- **Utility library** — 40+ technical indicators (MA, MACD, RSI, KDJ, Bollinger, ATR, ADX), statistical tools, position sizing (Kelly, ATR-based, fixed fractional) - **Reports** — interactive HTML, chart PNG, Markdown, JSON with 20+ risk/return metrics - **Chainable stock screening** — fluent API (`query` / `valuation` / `get_fundamentals`) for fundamental analysis +- **Walk-forward analysis** — rolling in-sample/out-of-sample validation to detect overfitting (experimental) +- **Scientific validation** — overfitting detection, statistical confidence, bias detection, extended risk metrics (experimental) --- @@ -167,7 +169,7 @@ Full index at [`examples/Examples.md`](examples/Examples.md). | 06 | `06_advanced_api.py` | Scheduling, portfolio optimization, attribution | | 07 | `07_market_data.py` | Financials, industry, index, minute, tick data | | 08 | `08_lifecycle_callbacks.py` | Lifecycle callbacks & stock pool management | -| 09 | `09_attribution_analysis.py` | Brinson + Fama-French factor analysis | +| 09 | `09_attribution_analysis.py` | Brinson + simple factor analysis | | 10 | `10_index_concept.py` | Index & concept board strategies | | 11 | `11_utils_library.py` | Full utility library demonstration | | 12 | `12_portfolio_backtest.py` | Multi-stock portfolio backtest | diff --git a/README_zh.md b/README_zh.md index ccaadb6..2bad0f6 100644 --- a/README_zh.md +++ b/README_zh.md @@ -7,6 +7,7 @@ 面向中国 A 股市场的事件驱动量化回测框架。 [![Tests](https://img.shields.io/github/actions/workflow/status/AlanFokCo/EasyQuant/test.yml?label=Tests)](https://github.com/AlanFokCo/EasyQuant/actions/workflows/test.yml) +[![Studio CI](https://img.shields.io/github/actions/workflow/status/AlanFokCo/EasyQuant/studio-test.yml?label=Studio%20CI)](https://github.com/AlanFokCo/EasyQuant/actions/workflows/studio-test.yml) [![Docs](https://img.shields.io/github/actions/workflow/status/AlanFokCo/EasyQuant/deploy-docs.yml?label=Docs)](https://AlanFokCo.github.io/EasyQuant/) [![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue.svg)](https://www.python.org) [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/AlanFokCo/EasyQuant/blob/main/LICENSE) @@ -31,14 +32,16 @@ - **事件驱动回测** — `initialize` → `run_daily` → `handle_data`,与 JoinQuant / Zipline 一致的编程模型 - **A 股数据** — 日线 OHLCV、分钟 K 线、Tick 数据、实时行情、财务摘要、资金流向,通过 AKShare 免费获取 - **仓位管理** — 按股数 / 金额 / 目标值买卖,自动取整到 100 股,自动计算手续费 -- **风险分析** — 夏普 / 索提诺 / 最大回撤 / alpha & beta / Brinson 归因 / Fama-French 因子分析 +- **风险分析** — 夏普 / 索提诺 / 最大回撤 / alpha & beta / Brinson 归因 / 简化因子分析 - **组合优化** — 最小方差、最大夏普、风险平价 - **模拟盘** — 使用实时行情持续运行策略,实盘前验证 - **PTrade/QMT 适配** — 策略一键导出为券商平台格式 - **选股** — 按因子定期调仓(ST/PB/PE/动量过滤、Top-N、多因子评分) -- **工具库** — 30+ 技术指标(MA、MACD、RSI、KDJ、布林带、ATR、ADX)、统计分析、仓位管理(Kelly、ATR、固定比例) +- **工具库** — 40+ 技术指标(MA、MACD、RSI、KDJ、布林带、ATR、ADX)、统计分析、仓位管理(Kelly、ATR、固定比例) - **报告输出** — 交互式 HTML 报告、图表 PNG、Markdown、JSON,含 20+ 风险/收益指标 - **链式选股 API** — 流式接口(`query` / `valuation` / `get_fundamentals`)用于基本面筛选 +- **滚动验证 (WFA)** — 样本内/样本外滚动窗口验证,检测过拟合(实验性) +- **科学验证** — 过拟合检测、统计置信度、偏差检测、扩展风险指标(实验性) --- @@ -164,7 +167,7 @@ EasyQuant/ | 06 | `06_advanced_api.py` | 调度、组合优化、归因分析 | | 07 | `07_market_data.py` | 财务、行业、指数、分钟线、Tick 数据 | | 08 | `08_lifecycle_callbacks.py` | 生命周期回调与股票池管理 | -| 09 | `09_attribution_analysis.py` | Brinson 归因 + Fama-French 因子分析 | +| 09 | `09_attribution_analysis.py` | Brinson 归因 + 简化因子分析 | | 10 | `10_index_concept.py` | 指数与概念板块策略 | | 11 | `11_utils_library.py` | 全量工具库演示 | | 12 | `12_portfolio_backtest.py` | 多股票组合回测 | diff --git a/SECURITY.md b/SECURITY.md index fda3c5a..4ebd84b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,8 @@ | Version | Supported | | ------- | ------------------ | -| 0.1.x | Yes | +| 1.0.x | Yes | +| 0.1.x | No | ## Reporting a Vulnerability diff --git a/doc/api_reference.md b/doc/api_reference.md index c35d0fc..50a7f30 100644 --- a/doc/api_reference.md +++ b/doc/api_reference.md @@ -22,6 +22,8 @@ | 选股/行业轮动 | [选股策略 API](#选股策略-api) | | 优化仓位权重 | [组合优化 API](#组合优化-api) | | 本地缓存 | [缓存 API](#缓存-api) | +| 滚动验证 / 检测过拟合 | [滚动验证 API(实验性)](#滚动验证-api实验性) | +| 科学验证 / 偏差检测 | [科学验证 API(实验性)](#科学验证-api实验性) | **命名约定:** @@ -654,9 +656,22 @@ metrics = analyze_returns(result, risk_free_rate=0.03, trading_days=252) Brinson 归因分析。返回 `dict`(`allocation_effect`, `selection_effect`, `interaction_effect`, `total_active_return`)。 -### fama_french_analysis +### simple_factor_analysis -Fama-French 因子分析。返回 `dict`(`market_beta`, `market_exposure`, `alpha_annual`, `momentum_correlation`, `vol_of_vol`, `residual_volatility`, `explained_variance`)。 +简化因子分析。将策略收益分解为市场因子(beta)、动量代理(滞后收益自相关)和 Alpha(残差)。 + +> **注意:** 本函数**不实现**真正的 Fama-French 三因子模型。`momentum_correlation` 字段为收益自相关,非真正的动量因子暴露。 + +```python +from eqlib import simple_factor_analysis +ff = simple_factor_analysis(result) +``` + +返回 `dict`(`market_beta`, `market_exposure`, `alpha_annual`, `momentum_correlation`, `vol_of_vol`, `residual_volatility`, `explained_variance`)。 + +!!! warning "已弃用别名" + + `fama_french_analysis` 仍可使用,但已弃用。请改用 `simple_factor_analysis`。 --- @@ -808,6 +823,86 @@ result = run_strategy(initialize, selection_func=my_selection, selection_rebalan --- +## 滚动验证 API(实验性) + +### walk_forward + +滚动验证(Walk-Forward Analysis):将历史期间分为交替的样本内(IS)和样本外(OOS)窗口,用于检测过拟合。 + +```python +from eqlib import walk_forward + +wfa_result = walk_forward( + make_initialize, + optimize_fn=optimize, + start_date='2020-01-01', + end_date='2024-12-31', + train_months=12, + test_months=3, + step_months=3, + starting_cash=100_000, +) +``` + +| 参数 | 类型 | 说明 | +|------|------|------| +| `make_initialize` | `Callable` | 工厂函数,接受参数字典并返回 `initialize` 函数 | +| `optimize_fn` | `Callable` 或 `None` | 可选,`(train_result) -> dict` 参数选择函数 | +| `start_date` | `str` / `date` | 分析起始日期 | +| `end_date` | `str` / `date` | 分析结束日期 | +| `train_months` | `int` | 每个训练窗口长度(月) | +| `test_months` | `int` | 每个测试窗口长度(月) | +| `step_months` | `int` | 窗口滑动步长(月) | +| `starting_cash` | `float` | 每个窗口的初始资金 | +| `benchmark` | `str` | 基准代码 | +| `securities` | `list[str]` 或 `None` | 股票池 | + +返回 `WFAResult` 对象,包含: + +- `windows`:每个窗口的结果列表 +- `oos_equity`:拼接的 OOS 权益曲线(`pd.Series`) +- `summary`:聚合统计(`total_oos_return`、`oos_sharpe` 等) + +--- + +## 科学验证 API(实验性) + +`eqlib.scientific` 提供回测后的科学验证工具,用于过拟合检测、统计置信度测试、偏差检测和扩展风险指标。 + +### validate_backtest + +一键运行全部验证检查。 + +```python +from eqlib.scientific import validate_backtest, ValidationConfig + +config = ValidationConfig() # 可选自定义配置 +validation = validate_backtest(backtest_result, config=config) +validation.summary() +``` + +### 子模块 + +| 模块 | 主要函数 | 说明 | +|------|----------|------| +| `overfitting` | `out_of_sample_test`、`parameter_sensitivity`、`walk_forward_analysis` | 过拟合检测 | +| `statistics` | `bootstrap_metrics`、`monte_carlo_simulation`、`significance_test`、`sample_size_assessment` | 统计置信度 | +| `bias` | `check_lookahead_bias`、`check_survivorship_bias`、`check_selection_bias`、`check_data_bias` | 偏差检测 | +| `risk` | `extended_risk_metrics`、`value_at_risk`、`conditional_var`、`stress_test`、`tail_risk_analysis` | 扩展风险 | +| `comparison` | `compare_with_platform`、`compare_metrics`、`verify_trades` | 平台对比 | +| `report` | `generate_validation_report` | 验证报告生成 | + +### ValidationConfig + +验证配置对象,可自定义阈值和启用/禁用各验证模块。 + +```python +from eqlib import ValidationConfig +config = ValidationConfig() +``` + +--- + ## 参数化与优化约定 `eqlib` 的 API 可与任意 Python 流程配合(脚本、Notebook、定时任务)。典型调用链:`run_backtest()` → `analyze_returns()` → `brinson_attribution()` → 分析结果 → 修改参数 → 再回测。 @@ -852,7 +947,11 @@ from eqlib import ( # 组合优化 portfolio_optimizer, Bound, MinVariance, MaxSharpe, RiskParity, # 分析 - analyze_returns, brinson_attribution, fama_french_analysis, + analyze_returns, brinson_attribution, simple_factor_analysis, + # 滚动验证(实验性) + walk_forward, WFAResult, + # 科学验证(实验性) + ValidationConfig, # 选股 StockSelector, TopNSelector, MultiFactorSelector, filter_st_stocks, filter_paused_stocks, diff --git a/doc/user_guide.md b/doc/user_guide.md index 17edc4e..cd9a46d 100644 --- a/doc/user_guide.md +++ b/doc/user_guide.md @@ -607,14 +607,16 @@ print("配置效应: %.4f" % attr['allocation_effect']) print("选股效应: %.4f" % attr['selection_effect']) ``` -### 11.3 `fama_french_analysis`:因子分析 +### 11.3 `simple_factor_analysis`:因子分析 ```python -from eqlib import fama_french_analysis -ff = fama_french_analysis(result) +from eqlib import simple_factor_analysis +ff = simple_factor_analysis(result) print("市场 Beta: %.3f" % ff['market_beta']) ``` +> **注意:** 本函数**不实现**真正的 Fama-French 三因子模型。`momentum_correlation` 字段为收益自相关,非真正的动量因子暴露。`fama_french_analysis` 已弃用,请改用 `simple_factor_analysis`。 + --- ## 12. 模拟盘交易 @@ -764,6 +766,64 @@ audit_log/ --- +## 13A. 滚动验证(Walk-Forward Analysis)(实验性) + +滚动验证将历史期间分为交替的「样本内」(IS)训练窗口和「样本外」(OOS)测试窗口,在 IS 窗口上优化参数,在 OOS 窗口上验证,最后拼接 OOS 权益曲线。 + +```python +from eqlib import walk_forward + +def make_initialize(fast_period=5, slow_period=20): + def initialize(context): + set_benchmark('000300.XSHG') + context.fast = fast_period + context.slow = slow_period + run_daily(handle, time='every_bar') + return initialize + +def optimize(train_result): + return {'fast_period': 5, 'slow_period': 20} # 简化示例 + +wfa_result = walk_forward( + make_initialize, + optimize_fn=optimize, + start_date='2020-01-01', + end_date='2024-12-31', + train_months=12, + test_months=3, +) +print(wfa_result.summary) +``` + +WFA 结果包含:每个窗口的详细指标、拼接的 OOS 权益曲线、以及汇总统计(总 OOS 收益率、OOS 夏普比率等)。 + +--- + +## 13B. 科学验证(实验性) + +`eqlib.scientific` 提供回测后的科学验证工具,帮助判断策略是否可信: + +```python +from eqlib.scientific import validate_backtest + +validation = validate_backtest(result) +validation.summary() +``` + +包含以下验证模块: + +| 模块 | 功能 | +|------|------| +| **过拟合检测** | 样本外测试、参数敏感度分析、滚动验证 | +| **统计置信度** | Bootstrap 指标、蒙特卡罗模拟、显著性检验 | +| **偏差检测** | 前视偏差、幸存者偏差、选择偏差、数据偏差 | +| **扩展风险** | VaR、CVaR、压力测试、尾部风险分析 | +| **平台对比** | 与外部平台回测结果交叉验证 | + +详细 API 参考见 [API 参考 · 科学验证 API](api_reference.md#科学验证-api实验性)。 + +--- + ## 14. 常见问题 更完整的排错见 [FAQ.md](FAQ.md)。 diff --git a/docs/glossary.md b/docs/glossary.md index 4392554..8024d3d 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -28,6 +28,9 @@ **Brinson attribution (Brinson 归因)** 将超额收益分解为配置效应(allocation effect)、选股效应(selection effect)和交互效应(interaction effect)三部分,用于量化各层级决策的贡献。 +**bootstrap (自助法)** +一种重采样统计方法:从原始数据中有放回地抽样,生成大量模拟样本,以此估计指标的置信区间。EasyQuant 通过 `eqlib.scientific.bootstrap_metrics()` 提供支持。 + --- ## C @@ -86,6 +89,16 @@ A 股最小买入单位为 1 手 = 100 股。EasyQuant 的 `order()` 系列函 **max drawdown (最大回撤)** 见 drawdown。 +**Monte Carlo simulation (蒙特卡罗模拟)** +通过随机打乱收益序列的顺序来模拟大量可能的权益路径,以评估策略指标(如夏普比率、最大回撤)的分布范围。EasyQuant 通过 `eqlib.scientific.monte_carlo_simulation()` 提供支持。 + +--- + +## O + +**overfitting (过拟合)** +策略参数过度适配历史数据的特定噪声,导致在新数据上表现大幅下降。常见检测方法包括样本外测试、参数敏感度分析和滚动验证(Walk-Forward Analysis)。EasyQuant 的 `eqlib.scientific.overfitting` 模块提供自动检测工具。 + --- ## P @@ -126,6 +139,9 @@ A 股最小买入单位为 1 手 = 100 股。EasyQuant 的 `order()` 系列函 **ST (特别处理)** 沪深交易所对经营异常股票的特殊标记(*ST / ST)。ST 股涨跌幅限制为 ±5%,EasyQuant 的 `filter_st_stocks()` 函数可将 ST 股从股票池中排除。 +**survivorship bias (幸存者偏差)** +仅使用当前仍在交易的股票进行回测,忽略了已退市或被 ST 的股票,导致回测收益虚高。EasyQuant 的 `eqlib.scientific.check_survivorship_bias()` 可检测此类偏差。 + --- ## T @@ -145,6 +161,16 @@ A 股交易日历,排除节假日、周末。EasyQuant 优先使用新浪财 --- +## V + +**VaR (Value at Risk / 在险价值)** +在给定置信水平(如 95%)下,投资组合在一定持有期内可能遭受的最大损失。EasyQuant 通过 `eqlib.scientific.value_at_risk()` 和 `utils.value_at_risk()` 提供计算。 + +**CVaR (Conditional VaR / 条件在险价值)** +又称 Expected Shortfall(ES)。在损失超过 VaR 的情况下的期望损失,衡量尾部风险。EasyQuant 通过 `eqlib.scientific.conditional_var()` 和 `utils.conditional_var()` 提供计算。 + +--- + ## W **walk-forward analysis (滚动验证 / 走出样本)** diff --git a/examples/09_attribution_analysis.py b/examples/09_attribution_analysis.py index 74ac0f0..891549e 100644 --- a/examples/09_attribution_analysis.py +++ b/examples/09_attribution_analysis.py @@ -3,7 +3,7 @@ Demonstrates: - analyze_returns: Sharpe, Sortino, max drawdown, alpha, beta - brinson_attribution: allocation, selection, interaction effects -- fama_french_analysis: factor decomposition +- simple_factor_analysis: factor decomposition - Using the full reporting pipeline (chart + Markdown + JSON) Usage: @@ -122,12 +122,12 @@ def weekly_rebalance(context): else: print(" Insufficient data for Brinson attribution") - # 3. Fama-French factor analysis + # 3. Simple factor analysis print(f"\n{'=' * 60}") print("Factor Analysis (simplified)") print(f"{'=' * 60}") - ff = fama_french_analysis(result) + ff = simple_factor_analysis(result) if ff: print(f" Market beta: {ff['market_beta']:.2f}") print(f" Market exposure: {ff['market_exposure']:+.2f}") diff --git a/examples/Examples.md b/examples/Examples.md index 07cd454..f13c619 100644 --- a/examples/Examples.md +++ b/examples/Examples.md @@ -60,7 +60,7 @@ python examples/.py 演示 eqlib 高级功能:策略调度(`run_weekly`/`run_monthly`)、组合优化(最小方差/最大夏普/风险平价)、归因分析(Brinson + Fama-French)。 -**涉及 API:** `portfolio_optimizer`、`MinVariance`、`MaxSharpe`、`RiskParity`、`analyze_returns`、`brinson_attribution`、`fama_french_analysis` +**涉及 API:** `portfolio_optimizer`、`MinVariance`、`MaxSharpe`、`RiskParity`、`analyze_returns`、`brinson_attribution`、`simple_factor_analysis` ## 07 — 扩展数据 API @@ -78,7 +78,7 @@ python examples/.py 多股票动量策略 + 完整的回测后分析:绩效指标、Brinson 归因、Fama-French 因子分析、报告生成。 -**涉及 API:** `analyze_returns`、`brinson_attribution`、`fama_french_analysis`、`generate_chart`、`generate_report_md`、`generate_report_json` +**涉及 API:** `analyze_returns`、`brinson_attribution`、`simple_factor_analysis`、`generate_chart`、`generate_report_md`、`generate_report_json` ## 10 — 指数与概念策略 diff --git a/tutorials/03_backtesting.md b/tutorials/03_backtesting.md index 33ed4fb..d19ebf0 100644 --- a/tutorials/03_backtesting.md +++ b/tutorials/03_backtesting.md @@ -434,19 +434,21 @@ print("交互效应: %.4f ← 配置与选股的交互作用" % attr['interac - **选股效应 > 0**:说明你在板块内选的股票是对的 - **交互效应**:通常是小的调整项 -### 5.3 Fama-French 因子分析 +### 5.3 简化因子分析 ```python -from eqlib import fama_french_analysis +from eqlib import simple_factor_analysis -ff = fama_french_analysis(result) +ff = simple_factor_analysis(result) print("市场 Beta: %.3f ← 大盘敏感度" % ff['market_beta']) print("年化 Alpha: %.4f ← 市场无法解释的超额收益" % ff['alpha_annual']) -print("动量相关性: %.3f" % ff['momentum_corr']) +print("动量相关性: %.3f" % ff['momentum_correlation']) print("残差波动率: %.3f" % ff['residual_volatility']) ``` +> **注意:** `fama_french_analysis` 已弃用,请改用 `simple_factor_analysis`。 + --- ## 6. 判断策略是否有效 diff --git a/tutorials/04_strategy_optimization.md b/tutorials/04_strategy_optimization.md index 3a0eb2c..95f9b7f 100644 --- a/tutorials/04_strategy_optimization.md +++ b/tutorials/04_strategy_optimization.md @@ -336,7 +336,7 @@ def initialize(context): 回测完成后,深入了解收益来源: ```python -from eqlib import analyze_returns, brinson_attribution, fama_french_analysis +from eqlib import analyze_returns, brinson_attribution, simple_factor_analysis # 综合风险指标 metrics = analyze_returns(result, risk_free_rate=0.03) @@ -349,8 +349,8 @@ attr = brinson_attribution(result) print("配置效应: %.4f" % attr['allocation_effect']) print("选股效应: %.4f" % attr['selection_effect']) -# Fama-French 因子 -ff = fama_french_analysis(result) +# 简化因子分析 +ff = simple_factor_analysis(result) print("市场 Beta: %.3f" % ff['market_beta']) print("年化 Alpha: %.4f" % ff['alpha_annual']) ```