feat:添加了基本策略。
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 动量策略 — 构建与回测\n",
|
||||
"\n",
|
||||
"## 策略思路\n",
|
||||
"动量效应(Momentum Effect)指过去一段时间表现良好的股票,未来一段时间内往往继续表现良好。\n",
|
||||
"本策略使用 **20 日收益率** 作为动量因子,每月调仓,买入过去表现最好的一个分位组股票。\n",
|
||||
"\n",
|
||||
"## 研究流程\n",
|
||||
"1. 加载数据\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, ReportGenerator\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. 加载数据"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 回测区间\n",
|
||||
"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",
|
||||
"print(f'价格数据: {price_data.shape}, 日期: {price_data.index[0]} ~ {price_data.index[-1]}')\n",
|
||||
"print(f'股票数量: {price_data.shape[1]}')\n",
|
||||
"price_data.head()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2. 动量因子计算与 IC 分析"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 动量因子: 20日收益率(过去20个交易日涨跌幅)\n",
|
||||
"MOM_WINDOW = 20\n",
|
||||
"momentum = price_data.pct_change(MOM_WINDOW)\n",
|
||||
"\n",
|
||||
"# 未来 5 日收益率(验证短期预测能力)\n",
|
||||
"fwd_5d = price_data.pct_change(5).shift(-5)\n",
|
||||
"\n",
|
||||
"# 堆叠为长表 (MultiIndex: date, stock)\n",
|
||||
"factor_stacked = momentum.stack()\n",
|
||||
"fwd_stacked = fwd_5d.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 曲线\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": [
|
||||
"# 分 5 组,观察单调性\n",
|
||||
"quantile_ret = analyzer.quantile_returns(n_quantiles=5)\n",
|
||||
"print('各分位组平均收益 (Q1=最低动量, Q5=最高动量):')\n",
|
||||
"print(quantile_ret)\n",
|
||||
"\n",
|
||||
"# 绘制分组收益条形图\n",
|
||||
"quantile_ret['avg_return'].plot(kind='bar', figsize=(8, 4), color='steelblue')\n",
|
||||
"plt.title('动量因子分层收益 (未来5日)')\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",
|
||||
"# 动量策略:Top 20% (Q5) 长期持有,等权\n",
|
||||
"strategy = Strategy(\n",
|
||||
" name='动量策略-20日Top20%',\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='每月持有过去20日涨幅最高的20%股票,等权配置'\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# 回测引擎\n",
|
||||
"engine = BacktestEngine(config)\n",
|
||||
"equity_curve = engine.run(\n",
|
||||
" strategy=strategy,\n",
|
||||
" price_data=price_data,\n",
|
||||
" factor_data={'momentum': momentum},\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='steelblue')\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",
|
||||
"# 导出报告\n",
|
||||
"report_df = evaluator.full_report()\n",
|
||||
"pd.DataFrame(list(report_df.items()), columns=['指标', '数值']).set_index('指标')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 7. 交易记录分析"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"trades = engine.portfolio.trades\n",
|
||||
"if trades:\n",
|
||||
" trade_df = pd.DataFrame([\n",
|
||||
" {'日期': t.date, '股票': t.stock, '方向': t.side,\n",
|
||||
" '数量': t.quantity, '价格': t.price, '佣金': t.commission}\n",
|
||||
" for t in trades\n",
|
||||
" ])\n",
|
||||
" print(f'总交易数: {len(trade_df)}')\n",
|
||||
" print(f'买入: {(trade_df[\"方向\"]==\"buy\").sum()}, 卖出: {(trade_df[\"方向\"]==\"sell\").sum()}')\n",
|
||||
" display(trade_df.head(20))\n",
|
||||
"else:\n",
|
||||
" print('无交易记录(可能数据量不足)')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"## 参数敏感性分析\n",
|
||||
"\n",
|
||||
"动量窗口是影响策略表现的关键参数,可尝试不同窗口:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 不同动量窗口对比\n",
|
||||
"for win in [10, 20, 40, 60]:\n",
|
||||
" mom = price_data.pct_change(win)\n",
|
||||
" fwd = price_data.pct_change(5).shift(-5)\n",
|
||||
" a = FactorAnalyzer(mom.stack(), fwd.stack())\n",
|
||||
" summary = a.ic_summary()\n",
|
||||
" print(f'窗口 {win}日: IC均值={summary[\"IC_Mean\"]:.4f}, ICIR={summary[\"IR\"]:.4f}, '\n",
|
||||
" f'IC>0占比={summary[\"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
|
||||
}
|
||||
Reference in New Issue
Block a user