原创文章第74篇,专注“个人成长与财富自由、世界运作的逻辑, AI量化投资”。
前面系列文章,把backtrader的方方面面介绍了一下,如何使用,内部运作机制,如何扩展。
客观讲,熟悉了bt之后,发现这个框架着实易用,易扩展。更重要的是,对于实战有很多贴心的考量,是可以应用于生产环境的”工业级“应用。
我们今天来实战几个策略。
01 双均线——信号策略
自定义一个信号,这个信号比较简单,就是短周期与长周期两条均线”交叉“作为信号signal。
注意:这里包含”金叉“和”死叉“
import backtrader as bt # 自定义信号指标 class SignalDoubleSMA(bt.Indicator): lines = ('signal',) # 声明 signal 线,交易信号放在 signal line 上 params = dict( short_period=5, long_period=20) def __init__(self): self.s_ma = bt.ind.SMA(period=self.p.short_period) self.l_ma = bt.ind.SMA(period=self.p.long_period) # 短期均线上穿长期均线,取值为1;反之,短期均线下穿长期均线,取值为-1 self.lines.signal = bt.ind.CrossOver(self.s_ma, self.l_ma)
我们看下CrossOver这个指标,它是upcross - downcross。也就是”上穿”值为1,下窗值为-1。
02 三均线多头策略
三条均线,短,中,长三期,短期>中期>长期,即呈现“多头排列”时买入,而短线下穿中线时平仓。
import backtrader as bt # 自定义信号指标 class SignalTripleSMA(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;反之,取值为0 self.signal1 = bt.And(self.m_ma > self.l_ma, self.s_ma > self.m_ma) # 求上面 self.signal1 的环比增量,可以判断得到第一次同时满足上述条件的时间,第一次满足条件为1,其余条件为0 self.buy_signal = bt.If((self.signal1 - self.signal1(-1)) > 0, 1, 0) # 短期均线下穿长期均线时,取值为1;反之取值为0 self.sell_signal = bt.ind.CrossDown(self.s_ma, self.m_ma) # 将买卖信号合并成一个信号 self.lines.signal = bt.Sum(self.buy_signal, self.sell_signal * (-1))
03 海龟策略
import backtrader as bt from engine.strategy.strategy_base import StrategyBase class TurtleTradingStrategy(StrategyBase): params = dict( N1=20, # 唐奇安通道上轨的t N2=10, # 唐奇安通道下轨的t ) def __init__(self): self.order = None self.buy_count = 0 # 记录买入次数 self.last_price = 0 # 记录买入价格 # 准备第一个标的沪深300主力合约的close、high、low 行情数据 self.close = self.datas[0].close self.high = self.datas[0].high self.low = self.datas[0].low # 计算唐奇安通道上轨:过去20日的最高价 self.DonchianH = bt.ind.Highest(self.high(-1), period=self.p.N1, subplot=True) # 计算唐奇安通道下轨:过去10日的最低价 self.DonchianL = bt.ind.Lowest(self.low(-1), period=self.p.N2, subplot=True) # 生成唐奇安通道上轨突破:close>DonchianH,取值为1.0;反之为 -1.0 self.CrossoverH = bt.ind.CrossOver(self.close(0), self.DonchianH, subplot=False) # 生成唐奇安通道下轨突破: self.CrossoverL = bt.ind.CrossOver(self.close(0), self.DonchianL, subplot=False) # 计算 ATR self.TR = bt.ind.Max((self.high(0) - self.low(0)), # 当日最高价-当日最低价 abs(self.high(0) - self.close(-1)), # abs(当日最高价−前一日收盘价) abs(self.low(0) - self.close(-1))) # abs(当日最低价-前一日收盘价) self.ATR = bt.ind.SimpleMovingAverage(self.TR, period=self.p.N1, subplot=False) # 计算 ATR,直接调用 talib ,使用前需要安装 python3 -m pip install TA-Lib # self.ATR = bt.talib.ATR(self.high, self.low, self.close, timeperiod=self.p.N1, subplot=True) def next(self): # 如果还有订单在执行中,就不做新的仓位调整 if self.order: return # 如果当前持有多单 if self.position.size > 0: # 多单加仓:价格上涨了买入价的0.5的ATR且加仓次数少于等于3次 if self.datas[0].close > self.last_price + 0.5 * self.ATR[0] and self.buy_count <= 4: print('if self.datas[0].close >self.last_price + 0.5*self.ATR[0] and self.buy_count <= 4:') print('self.buy_count', self.buy_count) # 计算建仓单位:self.ATR*期货合约乘数300*保证金比例0.1 self.buy_unit = max((self.broker.getvalue() * 0.005) / (self.ATR * 300 * 0.1), 1) self.buy_unit = int(self.buy_unit) # 交易单位为手 # self.sizer.p.stake = self.buy_unit self.order = self.buy(size=self.buy_unit) self.last_price = self.position.price # 获取买入价格 self.buy_count = self.buy_count + 1 # 多单止损:当价格回落2倍ATR时止损平仓 elif self.datas[0].close < (self.last_price - 2 * self.ATR[0]): print('elif self.datas[0].close < (self.last_price - 2*self.ATR[0]):') self.order = self.sell(size=abs(self.position.size)) self.buy_count = 0 # 多单止盈:当价格突破10日最低点时止盈离场 平仓 elif self.CrossoverL < 0: print('self.CrossoverL < 0') self.order = self.sell(size=abs(self.position.size)) self.buy_count = 0 # 如果当前持有空单 elif self.position.size < 0: # 空单加仓:价格小于买入价的0.5的ATR且加仓次数少于等于3次 if self.datas[0].close < self.last_price - 0.5 * self.ATR[0] and self.buy_count <= 4: print('self.datas[0].close<self.last_price-0.5*self.ATR[0] and self.buy_count <= 4') # 计算建仓单位:self.ATR*期货合约乘数300*保证金比例0.1 self.buy_unit = max((self.broker.getvalue() * 0.005) / (self.ATR * 300 * 0.1), 1) self.buy_unit = int(self.buy_unit) # 交易单位为手 # self.sizer.p.stake = self.buy_unit self.order = self.sell(size=self.buy_unit) self.last_price = self.position.price # 获取买入价格 self.buy_count = self.buy_count + 1 # 空单止损:当价格上涨至2倍ATR时止损平仓 elif self.datas[0].close < (self.last_price + 2 * self.ATR[0]): print('self.datas[0].close < (self.last_price+2*self.ATR[0])') self.order = self.buy(size=abs(self.position.size)) self.buy_count = 0 # 多单止盈:当价格突破20日最高点时止盈平仓 elif self.CrossoverH > 0: print('self.CrossoverH>0') self.order = self.buy(size=abs(self.position.size)) self.buy_count = 0 else: # 如果没有持仓,等待入场时机 # 入场: 价格突破上轨线且空仓时,做多 if self.CrossoverH > 0 and self.buy_count == 0: print('if self.CrossoverH > 0 and self.buy_count == 0:') # 计算建仓单位:self.ATR*期货合约乘数300*保证金比例0.1 self.buy_unit = max((self.broker.getvalue() * 0.005) / (self.ATR * 300 * 0.1), 1) self.buy_unit = int(self.buy_unit) # 交易单位为手 self.order = self.buy(size=self.buy_unit) self.last_price = self.position.price # 记录买入价格 self.buy_count = 1 # 记录本次交易价格 # 入场: 价格跌破下轨线且空仓时,做空 elif self.CrossoverL < 0 and self.buy_count == 0: print('self.CrossoverL < 0 and self.buy_count == 0') # 计算建仓单位:self.ATR*期货合约乘数300*保证金比例0.1 self.buy_unit = max((self.broker.getvalue() * 0.005) / (self.ATR * 300 * 0.1), 1) self.buy_unit = int(self.buy_unit) # 交易单位为手 self.order = self.sell(size=self.buy_unit) self.last_price = self.position.price # 记录买入价格 self.buy_count = 1 # 记录本次交易价格
-
指标计算:
-
用 20 日的最高、最低、收盘价计算平均真实波幅 ATR;
-
计算出近 20 日的最高与 20 日最低价,构建唐奇安通道。
-
-
交易信号:
-
入场:价格突破 20 日价格高点时,入场;
-
加仓:价格继续上涨至 0.5 倍 ATR ,再次加仓,加仓次数不超过 3 次;
-
止损:价格回落 2 倍 ATR 时止损离场;
-
止盈:价格突破 10 日最低点时止盈离场;
-
做空与做多的逻辑相反。
-
本篇主要演示bt框架里,指标如何计算,尤其是一些复合的向量计算,以及策略如何写,包括止损、止盈,仓位管理等。
传统量化策略其实就是一个小交易系统,包括了选股、择时与仓位管理。而机器学习模型应该更接近是一个择时过程,应当是系统中的一个环节。而qlib的TopK回测,分高就all in,显然是不够“科学”,只是一种学术上的论证。