量化交易学习-RSI策略1
介绍
RSI就不多介绍了,主要灵感来自于蔡立耑写的《量化投资 以Python为工具》,有不懂的基本知识也可以看这本书。回测框架用的是backtrader 详见 https://www.backtrader.com。主要RSI函数计算用的是 Ta-Lib金融包。数据来源是Tushare。大佬Ernest.p.chan(虽然不知道多厉害)在他的书《Quantitative Trading》说了,
把自己发现的交易“秘密”通过博客与他人分享,你会从读者那里获得更多的回赠。其实、那些你认为是秘密的策略多半也早已为他人所致。一项策略真正的独有价值和值得保密的地方是你自己的窍门和所进行的变形,而绝不是基础版本。
所以希望大家多给我意见。本人没有什么编程背景,有任何问题,欢迎在本文下方留言,或者将问题发送至邮箱: taohjk@hotmail.com
谢谢大家!
最后来个免责声明
本内容仅作为学术参考和代码实例,仅供参考,不对投资决策提供任何建议。本人不承担任何人因使用本系列中任何策略、观点等内容造成的任何直接或间接损失。
主要策略
信号一
- ris6低于超卖线30,预测未来价格要回升,释放出买入信号
- ris6高于超买线70,预测未来价格有回落趋势,释放出卖出信号
信号二 - 短期ris6从下向上穿过长期rsi24,释放出买入信号
- 短期ris6从上向下穿过长期rsi24,释放出卖出信号
获取金融数据
下载数据
import pandas as pd
# 导入tushare模块
import tushare as ts
# 历史数据移到了tushare pro中
pro=ts.pro_api()
df = pro.daily(ts_code='600519.Sh', start_date='20140601',end_date='20150701')
# 将交易时间转换为datetime格式并将trade-date设定为index
df.index=pd.to_datetime(df.trade_date)
# 按照交易时间升序排列
df = df.sort_index(ascending=True)
df
600519是茅台的股票。都说是股王啊,所以我就拿它做例子。出来的结果是这样的
ts_code trade_date open high low close pre_close change pct_chg vol amount
trade_date
2014-06-03 600519.SH 20140603 153.00 154.50 152.25 152.77 153.22 -0.45 -0.29 13048.48 199563.914
2014-06-04 600519.SH 20140604 152.99 153.38 150.02 151.15 152.77 -1.62 -1.06 10494.31 158991.999
2014-06-05 600519.SH 20140605 151.12 155.00 149.33 154.77 151.15 3.62 2.40 20678.63 314507.720
2014-06-06 600519.SH 20140606 154.68 155.65 152.29 153.59 154.77 -1.18 -0.76 9477.45 145936.330
2014-06-09 600519.SH 20140609 152.30 154.40 152.30 153.23 153.59 -0.36 -0.23 10034.81 154198.989
... ... ... ... ... ... ... ... ... ... ... ...
2015-06-25 600519.SH 20150625 261.50 262.03 250.21 251.79 261.21 -9.42 -3.61 66521.66 1707385.606
2015-06-26 600519.SH 20150626 250.02 255.75 235.30 242.45 251.79 -9.34 -3.71 99332.01 2451381.599
2015-06-29 600519.SH 20150629 248.37 256.50 227.80 246.64 242.45 4.19 1.73 165071.67 4057069.903
2015-06-30 600519.SH 20150630 246.64 259.82 246.00 257.65 246.64 11.01 4.46 126298.19 3205285.541
2015-07-01 600519.SH 20150701 255.00 268.50 250.98 257.49 257.65 -0.16 -0.06 103326.54 2696026.039
266 rows × 11 columns
# 保存为CSV文件
df.to_csv('600519.csv')
# 读取CSV文件
df=pd.read_csv('600519.csv',parse_dates = ['trade_date'])
df
出来的数据是这样的
ts_code trade_date open high low close pre_close change pct_chg vol amount
trade_date
2014-06-03 600519.SH 20140603 153.00 154.50 152.25 152.77 153.22 -0.45 -0.29 13048.48 199563.914
2014-06-04 600519.SH 20140604 152.99 153.38 150.02 151.15 152.77 -1.62 -1.06 10494.31 158991.999
2014-06-05 600519.SH 20140605 151.12 155.00 149.33 154.77 151.15 3.62 2.40 20678.63 314507.720
2014-06-06 600519.SH 20140606 154.68 155.65 152.29 153.59 154.77 -1.18 -0.76 9477.45 145936.330
2014-06-09 600519.SH 20140609 152.30 154.40 152.30 153.23 153.59 -0.36 -0.23 10034.81 154198.989
... ... ... ... ... ... ... ... ... ... ... ...
2015-06-25 600519.SH 20150625 261.50 262.03 250.21 251.79 261.21 -9.42 -3.61 66521.66 1707385.606
2015-06-26 600519.SH 20150626 250.02 255.75 235.30 242.45 251.79 -9.34 -3.71 99332.01 2451381.599
2015-06-29 600519.SH 20150629 248.37 256.50 227.80 246.64 242.45 4.19 1.73 165071.67 4057069.903
2015-06-30 600519.SH 20150630 246.64 259.82 246.00 257.65 246.64 11.01 4.46 126298.19 3205285.541
2015-07-01 600519.SH 20150701 255.00 268.50 250.98 257.49 257.65 -0.16 -0.06 103326.54 2696026.039
266 rows × 11 columns
然后我们把Tushare格式转换一下
df2 = pd.DataFrame({'date' : df['trade_date'], 'open' : df['open'],
'high' : df['high'],'close' : df['close'],
'low' : df['low'],'volume' : df['vol']})
# 按照Yahoo格式的要求,调整df2各段的顺序
dt = df2.pop('date')
df2.insert(0,'date',dt)
o = df2.pop('open')
df2.insert(1,'open',o)
h = df2.pop('high')
df2.insert(2,'high',h)
l = df2.pop('low')
df2.insert(3,'low',l)
c = df2.pop('close')
df2.insert(4,'close',c)
v = df2.pop('volume')
df2.insert(5,'volume',v)
# 新格式数据存盘,不保存索引编号
df2.to_csv("600519-1.csv", index=False)
这样数据已经处理完了
Backtrader
基础设置
# Basic Setup
from __future__ import (absolute_import, division, print_function,unicode_literals)
import backtrader as bt
if __name__ == '__main__':
cerebro = bt.Cerebro()
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
添加价格数据
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])
# Import the backtrader platform
import backtrader as bt
# Create a cerebro entity
cerebro = bt.Cerebro()
# Datas are in a subfolder of the samples. Need to find where the script is
# because it could have been called from anywhere
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
datapath = os.path.join(modpath, 'C:/Users/taohj/python-work/CopperBat-量化/600519-1.csv')
# Create a Data Feed
data = bt.feeds.GenericCSVData(
dataname = datapath,
nullvalue = 0.0,
dtformat = ('%Y-%m-%d'),
datetime = 0,
open = 1,
high = 2,
low = 3,
close = 4,
volume = 5,
openinterest = -1
)
# Add the Data Feed to Cerebro
cerebro.adddata(data)
# Set our desired cash start
cerebro.broker.setcash(100000.0)
# Print out the starting conditions
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
# Run over everything
cerebro.run()
# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
交易信号一 超买超卖线
用Ta-Lib计算6日RSI。
- 当RSI6大于70时,股票出现超买信号。股票买入力量过大,买入力量在未来可能会减小,所以股票未来价格可能会下跌。所以此时要卖出
- 当RSI6小于30时,股票出现超卖信号。股票卖出力量过大,卖出力量在未来终归回到正常,因此股票未来价格可能会上涨。所以此时要买入
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])
# Import the backtrader platform
import backtrader as bt
# Create a Stratey
class TestStrategy(bt.Strategy):
params = (('RSIperiod', 6),)
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
# To keep track of pending orders and buy price/commission
self.order = None
self.buyprice = None
self.buycomm = None
# 添加Ta-Lib RSI指标
self.RSI=bt.talib.RSI(
self.data, timeperiod= self.p.RSIperiod)
#
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# Buy/Sell order submitted/accepted to/by broker - Nothing to do
return
# Check if an order has been completed
# Attention: broker could reject order if not enough cash
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else: # Sell
self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
self.order = None
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
(trade.pnl, trade.pnlcomm))
def next(self):
# Simply log the closing price of the series from the reference
self.log('Close, %.2f' % self.dataclose[0])
# Check if an order is pending ... if yes, we cannot send a 2nd one
if self.order:
return
# Check if we are in the market
if not self.position:
if self.RSI[0] < 30:
# BUY, BUY, BUY!!! (with default parameters)
self.log('BUY CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.buy()
# 如果已经在场内,则可以进行卖出操作
else:
# Already in the market ... we might sell
if self.RSI[0] > 70:
# SELL, SELL, SELL!!! (with all possible default parameters)
self.log('SELL CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.sell()
# Create a cerebro entity
cerebro = bt.Cerebro()
# Add a strategy
cerebro.addstrategy(TestStrategy)
# Datas are in a subfolder of the samples. Need to find where the script is
# because it could have been called from anywhere
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
datapath = os.path.join(modpath, '600519-1.csv')
# Create a Data Feed
data = bt.feeds.GenericCSVData(
dataname = datapath,
nullvalue = 0.0,
dtformat = ('%Y-%m-%d'),
datetime = 0,
open = 1,
high = 2,
low = 3,
close = 4,
volume = 5,
openinterest = -1
)
# Add the Data Feed to Cerebro
cerebro.adddata(data)
# Set our desired cash start
cerebro.broker.setcash(100000.0)
# 设置交易单位大小 A股100股为一手
cerebro.addsizer(bt.sizers.FixedSize, stake = 100)
# Print out the starting conditions
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
# Run over everything
cerebro.run()
# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
# Plot the result
cerebro.plot()
结果如下,是盈利的,但是茅台股价那段时间好像一直是涨的。
Starting Portfolio Value: 100000.00
Final Portfolio Value: 104142.00
加入交易型号二,请看下一篇。