Python中文社区 (ID:python-china)
本篇文章中,我将用 Python 构建一个简单的移动平均线交叉交易策略进行回测,并使用 标准普尔 500 指数(S&P500) 进行测试。
一个简单的移动平均线交叉策略可能是使用技术指标的量化交易策略的最简单示例之一,在用 Python 进行与财务数据相关的分析时,首先导入我们所需的模块(扫描本文最下方二维码获取全部完整源码和Jupyter Notebook 文件打包下载。):
- import pandas as pd
- import numpy as np
- from pandas_datareader import data
我们将首先使用 pandas_datareader 从 Yahoo Finance 下载标准普尔 500 指数从 2000 年第一个交易日到今天的价格数据,如下所示:
- sp500 = data.DataReader('^GSPC', 'yahoo',start='1/1/2000')
让我们快速检查一下数据的格式。
- sp500.head()
让我们创建一个收盘价的折线图,看看标准普尔在此期间的表现如何。
- sp500['Close'].plot(grid=True,figsize=(8,5))
我们要实施的趋势策略是基于两条简单移动平均线的交叉;2 个月(42 个交易日)和 1 年(252 个交易日)移动平均线。我们的第一步是创建移动平均值并同时将它们附加到我们现有的 sp500 DataFrame 中的新列表中。
- sp500['42d'] = np.round(sp500['Close'].rolling(window=42).mean(),2)
- sp500['252d'] = np.round(sp500['Close'].rolling(window=252).mean(),2)
上面的代码既创建了列表,又自动将它们添加到我们的 DataFrame 中。我们可以看到如下图表(在这里使用.tail调用,因为移动平均线实际上直到第 42 天和第 252 天才有3值,因此在.head调用中仅显示为NaN):
在这里我们看到确实已正确添加了移动平均值列表。现在让我们继续在同一图表上绘制收盘价和移动平均线。
- sp500[['Close','42d','252d']].plot(grid=True,figsize=(8,5))
我们的基本数据集现在已经非常完整,剩下要做的就是设计一个规则来生成我们的交易信号。我们将有 3 个基本规则:
1) 买入信号(做多)——42 天移动平均线首次高于 252 天趋势线 X 点。
2) 暂停交易 – 无仓位。
3) 卖出信号(做空)– 42 天移动平均线首次低于 252 天趋势 X 点。
创建这些信号的第一步是向 DataFrame 添加一个新列,它只是两个移动平均线之间的差异:
- sp500['42-252'] = sp500['42d'] - sp500['252d']
下一步是通过添加我们称为 Stance 的另一列来形式化信号。我们还将信号阈值“X”设置为 50(这个值设置得很随意,可以在某些时候进行优化)。
- X = 50
- sp500['Stance'] = np.where(sp500['42-252'] > X, 1, 0)
- sp500['Stance'] = np.where(sp500['42-252'] < -X, -1, sp500['Stance'])
- sp500['Stance'].value_counts()
最后一行代码生成如下结果:
- -1 2077
- 1 1865
- 0 251
- Name: Stance, dtype: int64
表明在我们选择回测的时间段内,有2077个交易日,42天移动平均线位于252天移动平均线下方50多点,有1865个交易日,42天移动平均线位于252天移动平均线上方50多点。
绘图显示了“Stance”的视觉表现。我已将“ylim”(即 y 轴限制)设置为略高于 1 且略低于 -1,因此我们实际上可以看到线的水平部分。
- sp500['Stance'].plot(lw=1.5,ylim=[-1.1,1.1])
在这个模型中,我们的投资者要么做多市场,要么做空市场,要么持平——这使我们能够处理市场回报。如果他是空头,可以简单地将当天的市场回报乘以 -1 ,如果他是多头,则乘以1 ,如果持平,则是0。
因此,我们向 DataFrame 添加另一列以保存索引的每日日收益率,然后将该列乘以“Stance”列以获得策略回报:
- sp500['Market Returns'] = np.log(sp500['Close'] / sp500['Close'].shift(1))
- sp500['Strategy'] = sp500['Market Returns'] * sp500['Stance'].shift(1)
请注意我们如何将 sp[‘Close’] 列表向下移动,以便我们使用前一天收盘时的“Stance”来计算第二天的回报。
现在我们可以在同一图表上绘制标准普尔 500 指数的回报与移动平均交叉策略的回报进行比较:
- sp500[['Market Returns','Strategy']].cumsum().plot(grid=True,figsize=(8,5))
我们可以看到,尽管该策略在市场低迷期间表现得相当好,但在市场反弹或只是向上趋势时表现不佳。