双均线策略构建及回测
# python构建双均线策略
# 均线策略中最常见的一种方法是根据长期均线和短期均线的交叉情况来确定交易信号,即:当短期均线从下往上穿越长期均线时,形成金叉,做多;
# 反之,当长期均线从上往下穿越短期均线时,形成死叉,做空或平仓。下面是常见的双均线策略如下:
# 均线:以 5 日均线为短期均线、以 20 日均线为长期均线;
# 买入开仓:当前无持仓,当日 5 日均线上穿 20 日均线,第二天以市价单买入,开仓;
# 卖出平仓:当前持有多单,当日 5 日均线下穿 20 日均线,第二天以市价单卖出,平仓。
# 三均线策略与双均线策略类似,只不过交易信号是由短期均线、中期均线、长期均线这 3 条均线共同确定的。如果只考虑做多的情况,一般是短期均线>中期均线>长期均线,
# 呈多头排列时,买入开仓;出现短期均线下穿中期均线时,卖出平仓。下面是案例具体的策略逻辑:
# 均线:5 日均线为短期均线、20 日均线为中期均线、60 日均线为长期均线;
# 买入开仓:当前无持仓,当开始出现 5 日均线>20 日均线>60 日均线多头排列时,第二天以市价单买入,开仓;
# 卖出平仓:当前持有多单,当日 5 日均线下穿 20 日均线,第二天以市价单卖出,平仓。
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
def get_data(code,start_time,end_time):df = get_price(security=code, start_date=start_time, end_date=end_time, frequency='daily')df.index=pd.to_datetime(df.index)df['openinterest']=0df=df[['open','high','low','close','volume','openinterest']] #按照back trader数据格式进行整合return df
code='600519.XSHG'
start_time='2016-9-7'
end_time='2022-5-20'
stock_df=get_data(code,start_time,end_time)
stock_df
open | high | low | close | volume | openinterest | |
---|---|---|---|---|---|---|
2016-09-07 | 290.26 | 290.26 | 286.34 | 287.37 | 3162902.0 | 0 |
2016-09-08 | 286.07 | 287.28 | 285.32 | 286.64 | 1782374.0 | 0 |
2016-09-09 | 287.42 | 287.61 | 284.93 | 285.52 | 1730813.0 | 0 |
2016-09-12 | 283.06 | 284.93 | 279.09 | 279.36 | 4079778.0 | 0 |
2016-09-13 | 281.14 | 282.09 | 280.53 | 281.59 | 1734373.0 | 0 |
... | ... | ... | ... | ... | ... | ... |
2022-05-16 | 1797.00 | 1797.00 | 1742.67 | 1758.00 | 2776531.0 | 0 |
2022-05-17 | 1769.88 | 1775.00 | 1755.00 | 1768.00 | 2014474.0 | 0 |
2022-05-18 | 1773.66 | 1773.92 | 1743.02 | 1761.00 | 2485044.0 | 0 |
2022-05-19 | 1745.00 | 1757.00 | 1739.00 | 1756.00 | 1977626.0 | 0 |
2022-05-20 | 1759.99 | 1801.15 | 1752.00 | 1800.01 | 4194960.0 | 0 |
1382 rows × 6 columns
# 计算均线
def sma_cal(df,window):sma=df['close'].rolling(window=window).mean()return sma
sma_df={}
window=[5,10,20,60]
for i in range(len(window)):sma_name=str(window[i])value=sma_cal(stock_df,window[i])sma_df['sma'+sma_name]=value
sma_df=pd.DataFrame(sma_df)
sma_df['close']=stock_df['close']
sma_df['ret']=sma_df['close'].pct_change()
sma_df=sma_df.dropna()
sma_df
sma5 | sma10 | sma20 | sma60 | close | ret | |
---|---|---|---|---|---|---|
2016-12-08 | 305.418 | 302.715 | 297.6910 | 290.251167 | 316.53 | 0.031345 |
2016-12-09 | 308.414 | 304.671 | 298.7535 | 290.737833 | 316.57 | 0.000126 |
2016-12-12 | 310.464 | 305.361 | 299.4975 | 291.066500 | 306.36 | -0.032252 |
2016-12-13 | 311.698 | 306.209 | 300.3880 | 291.509833 | 312.12 | 0.018801 |
2016-12-14 | 312.602 | 307.445 | 301.2840 | 292.044333 | 311.43 | -0.002211 |
... | ... | ... | ... | ... | ... | ... |
2022-05-16 | 1766.620 | 1787.848 | 1782.9985 | 1771.657833 | 1758.00 | -0.011382 |
2022-05-17 | 1766.982 | 1781.148 | 1780.9485 | 1769.614167 | 1768.00 | 0.005688 |
2022-05-18 | 1765.582 | 1774.410 | 1778.6990 | 1767.421667 | 1761.00 | -0.003959 |
2022-05-19 | 1764.248 | 1766.310 | 1776.9580 | 1765.238500 | 1756.00 | -0.002839 |
2022-05-20 | 1768.602 | 1767.011 | 1777.4435 | 1763.455333 | 1800.01 | 0.025063 |
1323 rows × 6 columns
import matplotlib.pyplot as plt
sma_df.iloc[:,0:5].plot(figsize=(12,7))
# 双均线
# 采用5日均线与20日均线
signal=pd.Series(0,index=sma_df.index)
for i in range(1,len(signal)):if all([sma_df['sma5'][i]>sma_df['sma20'][i],sma_df['sma5'][i-1]<sma_df['sma20'][i-1]]):signal[i]=1elif all([sma_df['sma5'][i]<sma_df['sma20'][i],sma_df['sma5'][i-1]>sma_df['sma20'][i-1]]):signal[i]=-1
signal
2016-12-08 0
2016-12-09 0
2016-12-12 0
2016-12-13 0
2016-12-14 0..
2022-05-16 0
2022-05-17 0
2022-05-18 0
2022-05-19 0
2022-05-20 0
Length: 1323, dtype: int64
trad_signal=signal.shift(1)
trad_signal
2016-12-08 NaN
2016-12-09 0.0
2016-12-12 0.0
2016-12-13 0.0
2016-12-14 0.0...
2022-05-16 0.0
2022-05-17 0.0
2022-05-18 0.0
2022-05-19 0.0
2022-05-20 0.0
Length: 1323, dtype: float64
long=pd.Series(0,index=sma_df.index)
long[trad_signal==1]=1
longret=long*sma_df['ret']
long_winret=len(longret[longret>0])/len(longret[longret!=0])
long_winret
0.43243243243243246
short=pd.Series(0,index=sma_df.index)
short[trad_signal==-1]=-1
shortret=short*sma_df['ret']
short_winret=len(shortret[shortret>0])/len(shortret[shortret!=0])
short_winret
0.5128205128205128
tradret=trad_signal*sma_df['ret'].dropna()
winret=len(tradret[tradret>0])/len(tradret[tradret!=0])
winret
0.4675324675324675
cumlong=np.cumprod(1+longret)-1
cumshort=np.cumprod(1+shortret)-1
cumret=np.cumprod(1+tradret)-1plt.figure(figsize=(12,8))
plt.plot(cumlong,label='long ret')
plt.plot(cumshort,label='short ret')
plt.plot(cumret,label='long_short ret')plt.legend()
plt.show()
# 三均线策略
# 采用5日均线、20日均线与60日均线
signal1=pd.Series(0,index=sma_df.index)
for i in range(1,len(signal1)):if all([sma_df['sma5'][i]>sma_df['sma20'][i]>sma_df['sma60'][i]]):signal1[i]=1elif all([sma_df['sma5'][i]<sma_df['sma20'][i],sma_df['sma5'][i-1]>sma_df['sma20'][i-1]]):signal1[i]=-1
signal1
2016-12-08 0
2016-12-09 1
2016-12-12 1
2016-12-13 1
2016-12-14 1..
2022-05-16 0
2022-05-17 0
2022-05-18 0
2022-05-19 0
2022-05-20 0
Length: 1323, dtype: int64
trad_signal1=signal1.shift(1)
trad_signal1
2016-12-08 NaN
2016-12-09 0.0
2016-12-12 1.0
2016-12-13 1.0
2016-12-14 1.0...
2022-05-16 0.0
2022-05-17 0.0
2022-05-18 0.0
2022-05-19 0.0
2022-05-20 0.0
Length: 1323, dtype: float64
long1=pd.Series(0,index=sma_df.index)
long1[trad_signal1==1]=1
longret1=long1*sma_df['ret']
long_winret1=len(longret1[longret1>0])/len(longret1[longret1!=0])
long_winret1
0.5363214837712519
short1=pd.Series(0,index=sma_df.index)
short1[trad_signal1==-1]=-1
shortret1=short1*sma_df['ret']
short_winret1=len(shortret1[shortret1>0])/len(shortret1[shortret1!=0])
short_winret1
0.5128205128205128
tradret1=trad_signal1*sma_df['ret'].dropna()
winret1=len(tradret1[tradret1>0])/len(tradret1[tradret1!=0])
winret1
0.5342066957787481
cumlong1=np.cumprod(1+longret1)-1
cumshort1=np.cumprod(1+shortret1)-1
cumret1=np.cumprod(1+tradret1)-1plt.figure(figsize=(12,8))plt.plot(cumlong1,label='long ret1')
plt.plot(cumshort1,label='short ret1')
plt.plot(cumret1,label='long_short ret1')
plt.legend()
plt.show()
import backtrader as bt
import pandas as pd
# 自定义信号指标
class MySignal(bt.Indicator):lines = ('signal',) # 声明 signal 线,交易信号放在 signal line 上params = dict(short_period=5,long_period=20)def __init__(self):self.dataclose=self.datas[0].closeself.s_ma = bt.ind.SMA(period=self.p.short_period)self.l_ma = bt.ind.SMA(period=self.p.long_period)# 短期均线上穿长期均线,取值为1;反之,短期均线下穿长期均线,取值为-1self.lines.signal = bt.ind.CrossOver(self.s_ma, self.l_ma)# 实例化大脑
cerebro = bt.Cerebro()
# 加载数据datafeed = bt.feeds.PandasData(dataname=stock_df,fromdate=pd.to_datetime('2019-01-02'),todate=pd.to_datetime('2021-01-28'))
cerebro.adddata(datafeed)
# 初始资金 1,000,000
cerebro.broker.setcash(1000000.0)
# 佣金,双边各 0.0003
cerebro.broker.setcommission(commission=0.0003)
# 滑点:双边各 0.0001
cerebro.broker.set_slippage_perc(perc=0.0001)
#每次固定交易100股
cerebro.addsizer(bt.sizers.FixedSize, stake=100)
# 添加交易信号
cerebro.add_signal(bt.SIGNAL_LONG, MySignal)
# 添加分析器
cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='pnl') # 返回收益率时序数据
cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='_AnnualReturn')
cerebro.addanalyzer(bt.analyzers.SharpeRatio, riskfreerate=0.003, annualize=True, _name='_SharpeRatio')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='_DrawDown')
# 添加观测器
cerebro.addobserver(bt.observers.Value) # 查看账户资产变动
print(f'组合初始价值:%.2f'%cerebro.broker.getvalue())
result = cerebro.run()
print(f'组合期末价值:%.2f'%cerebro.broker.getvalue())
cerebro.plot(iplot=False)
组合初始价值:1000000.00
组合期末价值:1055108.41
strat = result[0]
print("--------------- AnnualReturn -----------------")
print(strat.analyzers._AnnualReturn.get_analysis())
print("--------------- SharpeRatio -----------------")
print(strat.analyzers._SharpeRatio.get_analysis())
print("--------------- DrawDown -----------------")
print(strat.analyzers._DrawDown.get_analysis())
--------------- AnnualReturn -----------------
OrderedDict([(2019, 0.004241777782910017), (2020, 0.04177344221019741), (2021, 0.008522332874576044)])
--------------- SharpeRatio -----------------
OrderedDict([('sharperatio', 0.9048730445652012)])
--------------- DrawDown -----------------
AutoOrderedDict([('len', 3), ('drawdown', 0.8102639726476181), ('moneydown', 8619.0), ('max', AutoOrderedDict([('len', 172), ('drawdown', 3.3309121179482157), ('moneydown', 33756.307311659795)]))])
# 自定义信号指标
class MySignal(bt.Indicator):lines = ('signal',) # 声明 signal 线,交易信号放在 signal line 上params = dict(short_period=5,median_period=20,long_period=60)def __init__(self):self.s_ma = bt.ind.SMA(period=self.p.short_period)self.m_ma = bt.ind.SMA(period=self.p.median_period)self.l_ma = bt.ind.SMA(period=self.p.long_period)# 短期均线在中期均线上方,且中期均取也在长期均线上方,三线多头排列,取值为1;反之,取值为0self.signal1 = bt.And(self.m_ma>self.l_ma, self.s_ma>self.m_ma)# 求上面 self.signal1 的环比增量,可以判断得到第一次同时满足上述条件的时间,第一次满足条件为1,其余条件为0self.buy_signal = bt.If((self.signal1-self.signal1(-1))>0, 1, 0)# 短期均线下穿长期均线时,取值为1;反之取值为0self.sell_signal = bt.ind.CrossDown(self.s_ma, self.m_ma)# 将买卖信号合并成一个信号self.lines.signal = bt.Sum(self.buy_signal, self.sell_signal*(-1))# 实例化大脑
cerebro = bt.Cerebro()
# 加载数据
# 读取行情数据datafeed = bt.feeds.PandasData(dataname=stock_df,fromdate=pd.to_datetime('2019-01-02'),todate=pd.to_datetime('2021-01-28'))
cerebro.adddata(datafeed)
# 初始资金 1,000,000
cerebro.broker.setcash(1000000.0)
# 佣金,双边各 0.0003
cerebro.broker.setcommission(commission=0.0003)
# 滑点:双边各 0.0001
cerebro.broker.set_slippage_perc(perc=0.0001)
#每次固定交易100股
cerebro.addsizer(bt.sizers.FixedSize, stake=100)
# 添加交易信号
cerebro.add_signal(bt.SIGNAL_LONG, MySignal)
# 添加交易信号
cerebro.add_signal(bt.SIGNAL_LONG, MySignal)
# 添加分析器
cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='pnl') # 返回收益率时序数据
cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='_AnnualReturn')
cerebro.addanalyzer(bt.analyzers.SharpeRatio, riskfreerate=0.003, annualize=True, _name='_SharpeRatio')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='_DrawDown')# 回测时需要添加 PyFolio 分析器
cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
cerebro.addobserver(bt.observers.Value) # 查看账户资产变动
print(f'组合初始价值:%.2f'%cerebro.broker.getvalue())
result = cerebro.run()
print(f'组合期末价值:%.2f'%cerebro.broker.getvalue())
cerebro.plot(iplot=False)
组合初始价值:1000000.00
组合期末价值:1071222.93
strat = result[0]
print("--------------- AnnualReturn -----------------")
print(strat.analyzers._AnnualReturn.get_analysis())
print("--------------- SharpeRatio -----------------")
print(strat.analyzers._SharpeRatio.get_analysis())
print("--------------- DrawDown -----------------")
print(strat.analyzers._DrawDown.get_analysis())
--------------- AnnualReturn -----------------
OrderedDict([(2019, 0.007321597032730054), (2020, 0.0545856770351798), (2021, 0.008393054551040002)])
--------------- SharpeRatio -----------------
OrderedDict([('sharperatio', 0.9274312773078117)])
--------------- DrawDown -----------------
AutoOrderedDict([('len', 3), ('drawdown', 0.7981723781349532), ('moneydown', 8619.0), ('max', AutoOrderedDict([('len', 137), ('drawdown', 2.7729689789715986), ('moneydown', 29437.66091440979)]))])
双均线策略构建及回测相关推荐
- 量化交易之回测篇 - 重写vnpy自带的双均线策略
"""双均线策略作为测试期货回测器的一个测试策略, 在测试品种ag时出现了一个问题:回测时ag净值曲线无最大回撤值,说明ag在双均线策略上无回撤,理论上这是不可能的; 排 ...
- Python双均线策略回测(2021-10-12)
Python双均线策略回测 1.择时策略简介 根据百度百科的解释,择时交易是指利用某种方法来判断大势的走势情况,是上涨还是下跌或者是盘整.如果判断是上涨,则买入持有:如果判断是下跌,则卖出清仓,如果是 ...
- 量化投资01----股票_贵州茅台600519_双均线策略回测
贵州茅台 SH600519 双均线策略回测(5日均线.30日均线,金叉买入,死叉卖出) 交易从 2010年开始,2019年底结束. 10万初始资金 尽量买入(按手),尽量卖出,均以开盘价 未计算手续费 ...
- Python量化交易实战-38使用开源项目回测双均线策略
B站配套视频教程观看 使用PyAlgoTrade回测双均线策略 双均线策略:长短周期均线,通过金叉,死叉的方式买入卖出股票,获取收益的策略. 回顾上节课代码的部分,上节课完成了可视化代码的部分, 主要 ...
- Python量化交易02——双均线策略(移动平均线)
参考书目:深入浅出Python量化交易实战 本次带来最经典的交易策略,双均线策略的构建和其回测方法. 双均线一般采用5天均值和10天均值,如果5日均线上穿突破了10日均线,说明股价在最近的涨势很猛,买 ...
- Tushare + Backtrader实现双均线策略 以工商银行为例
参看文章: Welcome - Backtrader 6.如何用backtrader实现双均线策略?以工商银行为例_云金杞-CSDN博客 Python量化交易学习笔记(53)--backtrader的 ...
- 双均线策略(期货)——Python量化
双均线策略 目录 双均线策略 1. 原理 均线的"前世今生" 均线理论为什么有效? 均线理论的缺陷 均线理论的改进 2. 策略逻辑 3. 策略代码 4. 回测结果与稳健性分析 1. ...
- python量化 双均线策略(金叉死叉)
#小策略,策略逻辑是在金叉时候买进,死叉时候卖出,所谓金叉死叉是两条均线的交叉,当短期均线上穿长期均线为金叉,反之为死叉 #下面是策略代码及结构 # 导入函数库 from jqdata import ...
- 【手把手教你】趋势跟踪交易策略的量化回测
01 引言 趋势跟踪策略旨在根据市场趋势的方向来进行交易,即如果市场处于上升趋势,那么投资者应该买入,并且保持这种头寸直到趋势结束.同样地,如果市场处于下降趋势,投资者应该卖空,并且保持这种头寸直到趋 ...
最新文章
- python+requests+excel+unittest+ddt接口自动化数据驱动并生成html报告
- Oracle里default什么意思,ORACLE中默认值default的使用方法.doc
- 快速排序(quick sort)
- linux装Oracle需要jdk,告诉你Ubuntu Linux下安装Oracle JDK的方法及命令
- MySql(四):备份与恢复
- 航拍+AI︱paddlepaddle图像分割实现天空风格迁移(换天、漂浮城堡、宇宙飞船)
- 支付弹出php,PHP微信公众号支付弹出“NaN:undefined”解决方法
- android 游戏 柄,Android手柄游戏中心-葡萄游戏厅
- 鱼眼镜头的成像原理到畸变矫正(完整版)
- 四川农大2020计算机专业录取分数线,四川农业大学2020年美术类本科专业录取分数线...
- 有赞BI平台实现原理
- cocos2d-x-3.4-025-仿霍比特人3五军之战片尾动画
- IOS superView和subView
- 《软件测试》第十二章 文档测试
- 你真的了解Web Component吗?
- Android之多个View同时动画
- 服务器配置推荐,如何选择服务器配置
- 难道非要买正版的闪客精灵才可以解析FLASH吗
- springboot毕设项目网上点餐系统z5055(java+VUE+Mybatis+Maven+Mysql)
- Java 天选之子_ 巅峰体验 The Peak Experience