backtrader学习之二-配对交易策略回测

用backtrader做股票数据的配对策略回测,这次用配对策略对工商银行、兴业银行的数据进行回测。逻辑

是当zcore值大于2.1,卖股票1买股票2,zcore小于-2.1,卖股票2买股票1

数据源来自baostock.com,数据按照时间,holc排序。由于数据没有进行复权,貌似也不准,仅用于练手。

from __future__ import (absolute_import, division, print_function,
unicode_literals)

import datetime
import pandas as pd
from pylab import mpl
import numpy as np
# The above could be sent to an independent module
import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind

class PairTradingStrategy(bt.Strategy):
params = dict(
period=10,
stake=10,
qty1=0,
qty2=0,
printout=True,

#设置上限为2.1
upper=2.1,

#设置下限为-2.1
lower=-2.1,
up_medium=0.5,
low_medium=-0.5,
status=0,
portfolio_value=10000,
)

def log(self, txt, dt=None):
if self.p.printout:
dt = dt or self.data.datetime[0]
dt = bt.num2date(dt)
print('%s, %s' % (dt.isoformat(), txt))

def notify_order(self, order):
if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
return # Await further notifications

if order.status == order.Completed:
if order.isbuy():
buytxt = 'BUY COMPLETE, %.2f' % order.executed.price
self.log(buytxt, order.executed.dt)
else:
selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
self.log(selltxt, order.executed.dt)

elif order.status in [order.Expired, order.Canceled, order.Margin]:
self.log('%s ,' % order.Status[order.status])
pass # Simply log

# Allow new orders  允许开新单
self.orderid = None

def __init__(self):
# To control operation entries 
self.orderid = None
self.qty1 = self.p.qty1
self.qty2 = self.p.qty2
self.upper_limit = self.p.upper
self.lower_limit = self.p.lower
self.up_medium = self.p.up_medium
self.low_medium = self.p.low_medium
self.status = self.p.status
self.portfolio_value = self.p.portfolio_value

# Signals performed with PD.OLS :  计算data0上data1的回归和zscord值
self.transform = btind.OLS_TransformationN(self.data0, self.data1,
period=self.p.period)
self.zscore = self.transform.zscore

# Checking signals built with StatsModel.API :
# self.ols_transfo = btind.OLS_Transformation(self.data0, self.data1,
# period=self.p.period,
# plot=True)

def next(self):

if self.orderid:
return # if an order is active, no new orders are allowed

if self.p.printout:
print('Self len:', len(self))
print('Data0 len:', len(self.data0))
print('Data1 len:', len(self.data1))
print('Data0 len == Data1 len:',
len(self.data0) == len(self.data1))

print('Data0 dt:', self.data0.datetime.datetime())
print('Data1 dt:', self.data1.datetime.datetime())

print('status is', self.status)
print('zscore is', self.zscore[0])

# Step 2: Check conditions for SHORT & place the order 检查是否需要卖出
# Checking the condition for SHORT
if (self.zscore[0] > self.upper_limit) and (self.status != 1):

# Calculating the number of shares for each stock   计算股票的数量
value = 0.5 * self.portfolio_value # Divide the cash equally
x = int(value / (self.data0.close)) # Find the number of shares for Stock1
y = int(value / (self.data1.close)) # Find the number of shares for Stock2
print('x + self.qty1 is', x + self.qty1)
print('y + self.qty2 is', y + self.qty2)

# Placing the order  下单,卖1买2
self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("PEP", self.data0.close[0], x + self.qty1))
self.sell(data=self.data0, size=(x + self.qty1)) # Place an order for buying y + qty2 shares
self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("KO", self.data1.close[0], y + self.qty2))
self.buy(data=self.data1, size=(y + self.qty2)) # Place an order for selling x + qty1 shares

# Updating the counters with new value  更新持仓值
self.qty1 = x # The new open position quantity for Stock1 is x shares
self.qty2 = y # The new open position quantity for Stock2 is y shares

self.status = 1 # The current status is "short the spread"

# Step 3: Check conditions for LONG & place the order  检查是否买入
# Checking the condition for LONG
elif (self.zscore[0] < self.lower_limit) and (self.status != 2):

# Calculating the number of shares for each stock  计算买入数量
value = 0.5 * self.portfolio_value # Divide the cash equally
x = int(value / (self.data0.close)) # Find the number of shares for Stock1
y = int(value / (self.data1.close)) # Find the number of shares for Stock2
print('x + self.qty1 is', x + self.qty1)
print('y + self.qty2 is', y + self.qty2)

# Place the order  下单买入股票1,卖出股票2
self.log('BUY CREATE %s, price = %.2f, qty = %d' % ("PEP", self.data0.close[0], x + self.qty1))
self.buy(data=self.data0, size=(x + self.qty1)) # Place an order for buying x + qty1 shares
self.log('SELL CREATE %s, price = %.2f, qty = %d' % ("KO", self.data1.close[0], y + self.qty2))
self.sell(data=self.data1, size=(y + self.qty2)) # Place an order for selling y + qty2 shares

# Updating the counters with new value  刷新持仓值
self.qty1 = x # The new open position quantity for Stock1 is x shares
self.qty2 = y # The new open position quantity for Stock2 is y shares
self.status = 2 # The current status is "long the spread"


# Step 4: Check conditions for No Trade 检查是否无需交易
# If the z-score is within the two bounds, close all
"""
elif (self.zscore[0] < self.up_medium and self.zscore[0] > self.low_medium):
self.log('CLOSE LONG %s, price = %.2f' % ("PEP", self.data0.close[0]))
self.close(self.data0)
self.log('CLOSE LONG %s, price = %.2f' % ("KO", self.data1.close[0]))
self.close(self.data1)
"""

def stop(self):
print('==================================================')
print('Starting Value - %.2f' % self.broker.startingcash)
print('Ending Value - %.2f' % self.broker.getvalue())
print('==================================================')


def runstrategy():
args = parse_args()

# Create a cerebro  建立大脑
cerebro = bt.Cerebro()

# Get the dates from the args  从csv取数据

dataframe0 = pd.read_csv(r"C:\load\gsyh.csv", index_col=0, parse_dates=True)
dataframe0['openinterest'] = 0
data0 = bt.feeds.PandasData(dataname=dataframe0,
fromdate = datetime.datetime(2020, 1, 1),
todate = datetime.datetime(2021, 3, 31)
)

dataframe1 = pd.read_csv(r"C:\load\xyyh.csv", index_col=0, parse_dates=True)
dataframe1['openinterest'] = 0
data1 = bt.feeds.PandasData(dataname=dataframe1,
fromdate = datetime.datetime(2020, 1, 1),
todate = datetime.datetime(2021, 3, 31)
)

# fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
# todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')

# Add the 1st data to cerebro  引入数据
cerebro.adddata(data0)

# Add the 2nd data to cerebro
cerebro.adddata(data1)

# Add the strategy  引入策略
cerebro.addstrategy(PairTradingStrategy,
period=args.period,
stake=args.stake)

# Add the commission - only stocks like a for each operation  假如资金量
cerebro.broker.setcash(args.cash)

# Add the commission - only stocks like a for each operation  引入佣金率
cerebro.broker.setcommission(commission=args.commperc)

# And run it  运行
cerebro.run(runonce=not args.runnext,
preload=not args.nopreload,
oldsync=args.oldsync)

# Plot if requested  绘图
if args.plot:
cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False)

if __name__ == '__main__':
runstrategy()

执行后,1万变成了9932,貌似亏了。有问题请留言。

 

posted @ 2021-05-18 11:23  ZhongciWang  阅读(1363)  评论(0编辑  收藏  举报