本文将探索新的政策回測程式,主要是為了嘗試不同的技術名額在backtrader平台上的應用,為後續複雜政策的實作做準備。
本文将實作的政策是,當股票放量突破布林線中軌時進行買入,當股票收盤價低于短期均線時賣出。
買入條件中,放量突破布林線中軌具體指的是,當日股票開盤價在布林線中軌下方,收盤價在布林線中軌上方,當日成交量為10日以來的最高量。賣出條件中,短期均線選取為5日線。回測初始資金100000元,單筆操作機關1000股,傭金千分之一,回測時間自2018年1月1日至2020年3月20日。
政策核心代碼還是位于政策類的init方法中:
def __init__(self):
self.inds = dict()
for i, d in enumerate(self.datas):
self.inds[d] = dict()
# 布林線中軌
boll_mid = bt.ind.BBands(d.close).mid
# 買入條件
self.inds[d]['buy_con'] = bt.And( \
# 突破中軌
d.open < boll_mid, d.close > boll_mid, \
# 放量
d.volume == bt.ind.Highest(d.volume, period = self.p.p_period_volume, plot = False))
# 賣出條件
self.inds[d]['sell_con'] = d.close < bt.ind.SMA(d.close, period = self.p.p_sell_ma)
這裡需要注意的是,技術名額在backtrader裡是lines對象,而非數值,是以在使用與或操作時,不能使用python自帶的and和or操作符,而隻能調用backtrader的And和Or函數。對技術名額做比較時可以使用大于号、小于号等符号,這是因為backtrader對這些符号進行了重寫。
回測000001後的最終資産為101107.35元:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN2XjlGcjAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLwUEVNpXQq5kMRpHW3BjMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL1gDO4ETNzYTMxIzMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
可以看到,該政策并非每筆交易都會盈利,但是盈利額度較大,虧損額度較小。由于我們選取的交易機關是1000股,而000001的股價隻是10元左右,相當于我們隻動用了不到20%的資金,是以總盈利額顯得較小,如果提高交易手數,盈利總額也會随之提升。
同時回測000001、000002後的最終資産為100082.86元:
同時回測000001、000002、000004後的最終資産為100247.27元:
回測603999後的最終資産為98871.44元:
可以看到該政策也有總體虧損的情況,直覺上判斷,該政策可能比較适合大盤股,對小盤股而言,放量的條件比較容易達到,政策缺乏穩定性。
我們進一步嘗試将放量突破的标準選為20日以來的最高值時,回測000001的最終資産為100613.27元,可以看到3筆盈利交易,1筆虧損交易。
當我們把回測起始時間改為2008年1月1日後,回測0000001的最終資産為103137.30元,可以看到也存在大量的虧損交易,但是虧損值都較小。
友情提示:本系列學習筆記隻做資料分析,記錄個人學習過程,不作為交易依據,盈虧自負。
放量突破布林線中軌買入政策代碼:
from __future__ import (absolute_import, division, print_function, unicode_literals)
import datetime # 用于datetime對象操作
import os.path # 用于管理路徑
import sys # 用于在argvTo[0]中找到腳本名稱
import backtrader as bt # 引入backtrader架構
import pandas as pd
stk_num = 1 # 回測股票數目
# 建立政策
class BollStrategy(bt.Strategy):
# 可配置政策參數
params = dict(
p_period_volume = 10, # 前n日最大交易量
p_sell_ma = 5, # 跌破該均線賣出
p_oneplot = False, # 是否列印到同一張圖
pstake = 1000, # 單筆交易股票數
)
def __init__(self):
self.inds = dict()
for i, d in enumerate(self.datas):
self.inds[d] = dict()
# 布林線中軌
boll_mid = bt.ind.BBands(d.close).mid
# 買入條件
self.inds[d]['buy_con'] = bt.And( \
# 突破中軌
d.open < boll_mid, d.close > boll_mid, \
# 放量
d.volume == bt.ind.Highest(d.volume, period = self.p.p_period_volume, plot = False))
# 賣出條件
self.inds[d]['sell_con'] = d.close < bt.ind.SMA(d.close, period = self.p.p_sell_ma)
# 跳過第一隻股票data,第一隻股票data作為主圖資料
if i > 0:
if self.p.p_oneplot:
d.plotinfo.plotmaster = self.datas[0]
def next(self):
for i, d in enumerate(self.datas):
dt, dn = self.datetime.date(), d._name # 擷取時間及股票代碼
pos = self.getposition(d).size
if not pos: # 不在場内,則可以買入
if self.inds[d]['buy_con']: # 如果金叉
self.buy(data = d, size = self.p.pstake) # 買買買
elif self.inds[d]['sell_con']: # 在場内,且死叉
self.close(data = d) # 賣賣賣
def notify_trade(self, trade):
dt = self.data.datetime.date()
if trade.isclosed:
print('{} {} Closed: PnL Gross {}, Net {}'.format(
dt, trade.data._name, round(trade.pnl, 2), round(trade.pnlcomm, 2)
))
cerebro = bt.Cerebro() # 建立cerebro
# 讀入股票代碼
stk_code_file = '../TQDat/data/tq_stock_code.csv'
stk_pools = pd.read_csv(stk_code_file, encoding = 'gbk')
if stk_num > stk_pools.shape[0]:
print('股票數目不能大于%d' % stk_pools.shape[0])
exit()
for i in range(stk_num):
stk_code = stk_pools['code'][stk_pools.index[i]]
stk_code = '%06d' % stk_code
# 讀入資料
datapath = '../TQDat/day/stk/' + stk_code + '.csv'
# 建立價格資料
data = bt.feeds.GenericCSVData(
dataname = datapath,
fromdate = datetime.datetime(2018, 1, 1),
todate = datetime.datetime(2020, 3, 31),
nullvalue = 0.0,
dtformat = ('%Y-%m-%d'),
datetime = 0,
open = 1,
high = 2,
low = 3,
close = 4,
volume = 5,
openinterest = -1
)
# 在Cerebro中添加股票資料
cerebro.adddata(data, name = stk_code)
# 設定啟動資金
cerebro.broker.setcash(100000.0)
# 設定傭金為千分之一
cerebro.broker.setcommission(commission=0.001)
cerebro.addstrategy(BollStrategy, p_oneplot = False) # 添加政策
cerebro.run() # 周遊所有資料
# 列印最後結果
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.plot() # 繪圖
為了便于互相交流學習,建立了微信群,感興趣的讀者請加微信。