Files
quanxiel/alpha/价值投资策略.ipynb
T

296 lines
8.4 KiB
Plaintext
Raw 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.
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 价值投资策略 — 构建与回测\n",
"\n",
"## 策略思路\n",
"价值投资(Value Investing):买入相对于基本面被低估的股票。\n",
"本策略使用 **市盈率 PE 倒数(EP)** 与 **市净率 PB 倒数(BP** 合成价值因子,\n",
"每月调仓,持有估值最低(价值因子最高)的一批股票。\n",
"\n",
"## 研究流程\n",
"1. 加载数据(价格 + PE/PB 估值指标)\n",
"2. 价值因子计算与 IC 分析\n",
"3. 分层回测验证因子有效性\n",
"4. 构建策略并回测\n",
"5. 绩效评估"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"sys.path.insert(0, '..')\n",
"\n",
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"\n",
"from alpha.config import AlphaConfig\n",
"from alpha.factors import FactorAnalyzer\n",
"from alpha.strategy import Strategy, QuantileSignal, EqualWeightAllocator\n",
"from alpha.backtest import BacktestEngine\n",
"from alpha.evaluation import PerformanceEvaluator\n",
"from alpha.data_loader import DataLoader\n",
"\n",
"%matplotlib inline\n",
"plt.rcParams['font.sans-serif'] = ['SimHei']\n",
"plt.rcParams['axes.unicode_minus'] = False\n",
"\n",
"print('模块导入成功')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. 加载数据\n",
"\n",
"价值因子需要估值数据(PE / PB),通过 `DataLoader.load_factor` 加载。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"START_DATE = '2020-01-01'\n",
"END_DATE = '2024-12-31'\n",
"\n",
"loader = DataLoader()\n",
"\n",
"# 价格数据 + 估值数据\n",
"price_data = loader.load_prices(START_DATE, END_DATE)\n",
"pe_data = loader.load_factor('pe_ttm', START_DATE, END_DATE) # 市盈率 TTM\n",
"pb_data = loader.load_factor('pb', START_DATE, END_DATE) # 市净率\n",
"\n",
"print(f'价格数据: {price_data.shape}')\n",
"print(f'PE(TTM)数据: {pe_data.shape}')\n",
"print(f'PB数据: {pb_data.shape}')\n",
"\n",
"# 展示某天的估值截面\n",
"sample = pd.DataFrame({\n",
" 'PE_TTM': pe_data.iloc[-1],\n",
" 'PB': pb_data.iloc[-1],\n",
"}).dropna()\n",
"sample.head(10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. 价值因子计算与 IC 分析\n",
"\n",
"价值因子 = 标准化后的 (1/PE + 1/PB)\n",
"$$\n",
"Value = Z\\left(\\frac{1}{PE}\\right) + Z\\left(\\frac{1}{PB}\\right)\n",
"$$\n",
"PE/PB 越低 → 价值因子越高 → 越被低估。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 剔除 PE/PB 非正值(亏损或净资产为负)\n",
"ep = 1.0 / pe_data.where(pe_data > 0)\n",
"bp = 1.0 / pb_data.where(pb_data > 0)\n",
"\n",
"# 截面标准化后合成\n",
"def zscore_cross_section(df):\n",
" return (df - df.mean(axis=1)) / (df.std(axis=1) + 1e-12)\n",
"\n",
"value_factor = zscore_cross_section(ep) + zscore_cross_section(bp)\n",
"\n",
"# 未来 20 日收益率(价值因子预测周期较长)\n",
"fwd_20d = price_data.pct_change(20).shift(-20)\n",
"\n",
"factor_stacked = value_factor.stack()\n",
"fwd_stacked = fwd_20d.stack()\n",
"\n",
"analyzer = FactorAnalyzer(factor_stacked, fwd_stacked)\n",
"ic = analyzer.compute_ic(method='rank')\n",
"\n",
"print('价值因子 IC 汇总:')\n",
"for k, v in analyzer.ic_summary().items():\n",
" print(f' {k}: {v:.4f}')\n",
"\n",
"ic.plot(figsize=(12, 3), title='价值因子 Rank IC 时间序列')\n",
"plt.axhline(y=0, color='r', linestyle='--')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. 分层回测"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"quantile_ret = analyzer.quantile_returns(n_quantiles=5)\n",
"print('各分位组平均收益 (Q1=最贵, Q5=最便宜):')\n",
"print(quantile_ret)\n",
"\n",
"quantile_ret['avg_return'].plot(kind='bar', figsize=(8, 4), color='steelblue')\n",
"plt.title('价值因子分层收益 (未来20日)')\n",
"plt.ylabel('平均收益率')\n",
"plt.grid(True, alpha=0.3)\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. 构建策略并回测"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 回测配置\n",
"config = AlphaConfig(\n",
" initial_cash=1_000_000,\n",
" commission_rate=0.0003,\n",
" slippage=0.001,\n",
" stamp_tax=0.001,\n",
")\n",
"\n",
"# 价值策略:持有价值因子最高的 20% 股票(最被低估)\n",
"strategy = Strategy(\n",
" name='价值投资策略-EP+BP',\n",
" factors=[],\n",
" signal_generator=QuantileSignal(\n",
" n_quantiles=5, long_quantile=5, short_quantile=0,\n",
" ),\n",
" weight_allocator=EqualWeightAllocator(max_positions=10),\n",
" description='每月持有EP+BP合成的价值因子最高的20%股票'\n",
")\n",
"\n",
"engine = BacktestEngine(config)\n",
"equity_curve = engine.run(\n",
" strategy=strategy,\n",
" price_data=price_data,\n",
" factor_data={'value': value_factor},\n",
" rebalance_freq='M',\n",
")\n",
"\n",
"print(f'回测完成, 共 {len(equity_curve)} 个交易日')\n",
"print(f'累计收益率: {(equity_curve[\"nav\"].iloc[-1] - 1) * 100:.2f}%')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. 净值曲线与回撤"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig, axes = plt.subplots(2, 1, figsize=(14, 8))\n",
"\n",
"# 净值曲线\n",
"axes[0].plot(equity_curve.index, equity_curve['nav'], label='策略净值', color='purple')\n",
"axes[0].axhline(y=1.0, color='gray', linestyle='--')\n",
"axes[0].set_title('价值投资策略净值曲线')\n",
"axes[0].legend()\n",
"axes[0].grid(True, alpha=0.3)\n",
"\n",
"# 回撤曲线\n",
"nav = equity_curve['nav']\n",
"running_max = nav.cummax()\n",
"drawdown = (nav - running_max) / running_max\n",
"axes[1].fill_between(equity_curve.index, 0, drawdown.values, color='red', alpha=0.3)\n",
"axes[1].set_title('回撤曲线')\n",
"axes[1].grid(True, alpha=0.3)\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. 绩效评估"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 基准:股票池等权组合\n",
"bench_ret = price_data.pct_change().mean(axis=1)\n",
"\n",
"evaluator = PerformanceEvaluator(equity_curve, benchmark_returns=bench_ret, risk_free_rate=0.03)\n",
"print(evaluator.summary())\n",
"\n",
"report_df = evaluator.full_report()\n",
"pd.DataFrame(list(report_df.items()), columns=['指标', '数值']).set_index('指标')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## 单独估值指标对比\n",
"\n",
"对比单独使用 EP、BP 与合成价值因子的效果:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 单独因子的 IC 对比\n",
"for name, fac in [('EP', ep), ('BP', bp), ('EP+BP', value_factor)]:\n",
" a = FactorAnalyzer(fac.stack(), fwd_stacked)\n",
" s = a.ic_summary()\n",
" print(f'{name}: IC均值={s[\"IC_Mean\"]:.4f}, ICIR={s[\"IR\"]:.4f}, '\n",
" f'IC>0占比={s[\"IC>0_Ratio\"]:.2%}')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}