量化交易backtrader实践(五)_策略综合篇(1)_股票软件指标回测

在第三章6到9节,我们学习和实践了大部分股票软件指标,且这些指标是backtrader内置指标实践中没有讲到过的。然后,在进行策略综合之前,我们先热个身,把一些可能比较有参考意义的股票软件内置指标在backtrader里给实现了。同时也开始考虑每个指标的特点,以及应该使用什么样的综合策略。

目前backtrader只接收开、高、低、收以及成交量这五个数据,因此一些跟涨跌家数相关的大势型指标,一些与流通股本相关的参数等都无法在backtrader中简单的实现,那些我们就先忽略了。

01_超买超卖型

CC_01_MFI指标与CJ03_VRSI指标

MFI指标

MFI指标是基于RSI指标在成交量上的应用,所以是否可以直接继承RSI的指标,并在类中添加成交量的计算?这个我们还必须进到RSI的源码中去查看。

class RelativeStrengthIndex(Indicator):
    
    alias = ('RSI', 'RSI_SMMA', 'RSI_Wilder',)

    lines = ('rsi',)
    params = (
        ('period', 14),
        ('movav', MovAv.Smoothed),
        ('safediv', False),
    )

    def __init__(self):
        upday = UpDay(self.data, period=1)
        downday = DownDay(self.data, period=1)
        maup = self.p.movav(upday, period=self.p.period)
        madown = self.p.movav(downday, period=self.p.period)
        if not self.p.safediv:
            rs = maup / madown
 
        self.lines.rsi = 100.0 - 100.0 / (1.0 + rs)

class UpDay(Indicator):
    lines = ('upday',)
    params = (('period', 1),)

    def __init__(self):
        self.lines.upday = Max(self.data - self.data(-self.p.period), 0.0)

class DownDay(Indicator):
    lines = ('downday',)
    params = (('period', 1),)

    def __init__(self):
        self.lines.downday = Max(self.data(-self.p.period) - self.data, 0.0)

根据源码,首先计算UpDay和DownDay是判断收盘价与昨收价,若今收高于昨收(上涨)则把差值记录到upday中,否则把下跌的差值记录到downday中。这里出现了一个问题,就是默认写死了为self.data,也就是默认是收盘价,所以我们不能直接把upday和downday用在TYP的值计算里,也就不能直接继承它来计算MFI。

这里说明一下,我们从AI里获取的MFI的指标在backtrader中代码可能是有错误的,这时在next中打印MFI的值会出现全是NaN的情况,遇到这样的问题需要自己想办法分析和寻找方法解决。

class MFI2(Indicator):
    lines = ('mfi2', )  # 定义一个输出线,命名为'mfi'
    params = (
    ('period',14),)

    def __init__(self):
        
        self.tp = (self.data.high + self.data.low + self.data.close) / 3.0 # 计算典型价格
        self.rmf = self.tp * self.data.volume    # 计算资金流量(Raw Money Flow)
        
        upday = bt.And(Max(self.tp - self.tp(-1),0),1)  # 正向因子
        dnday = bt.And(Max(self.tp(-1)-self.tp,0),1)    # 负向因子
        # 由于这里不需要C-REF(C,1)的差值,只需要记录1,0即可
        # 这里使用bt.And()函数将最大值与上1,即如果最大值不为0,就会输出1
       
        self.pmf = self.rmf*upday   # 计算正向和负向资金流量
        self.nmf = self.rmf*dnday

        posmfi = bt.indicators.SumN(self.pmf,period=14) #使用indicators.SumN()进行累加
        negmfi = bt.indicators.SumN(self.nmf,period=self.p.period) 

        self.mfr = posmfi / negmfi   # 计算资金流动比率(Money Flow Ratio)

        self.lines.mfi2 = 100 -100 / (1 + self.mfr) # 计算MFI值

这里我们用到两个函数来解决过程中的2个问题,第1个问题是RSI要计算正向差值和负向差值,而MFI只需要计算是正向还是负向,然后乘上VOL * TYP的值,因此用了bt.And()函数,将正向的都放在upday中作为正向因子来解决它,当然我们也可以用bt.If()函数来进行。第2个问题是股票软件公式用的SUM(C,N)的函数,其实在backtrader中的indicators\basicops.py中有SumN的函数完全对应的。期间AI助手给出的例如cumcum()是无法正常使用的。

将MFI2的自定义指标放到最简单的策略类中进行构建和运行绘图,由于还没有添加买卖策略,只有主图和副图绘制显示,这里我们还添加了RSI的曲线与MFI进行比较,当然这里是为了确认我们的自定义指标输出的数量级基本是正确的,没有发生过程中的错误。

我们看到MFI与RSI的走势会比较相似,这一点在第6节实践股票软件中的MFI指标时已经确认过:

VRSI指标

在解决了MFI的指标问题后,我们再来看一下VRSI指标,从指标公式上看,VRSI才是严格的成交量在RSI指标上的应用,它与RSI的不同,仅仅是从C-REF(C,1)变成了VOL-REF(VOL,1)。

但是,由于这个改变是在RSI指标过程中的改变,而并非输入或输出的再加工,所以我们仍然不能直接继承RSI指标来使用,仍然需要自定义指标

class VRSI_test(Indicator):
    lines = ('vrsi',)  # 定义一个输出线,命名为'vrsi'
    params = (
    ('period',6),)
    plotinfo = dict(plotymargin=0.05, plotyhlines=[0, 100])

    def __init__(self):
        Vupday = Max(self.data.volume - self.data.volume(-1),0.0)
        Vdnday = Max(self.data.volume(-1) - self.data.volume,0.0)

        #
        Vmaup = bt.indicators.SMA(Vupday, period=self.p.period)
        Vmadn = bt.indicators.SMA(Vdnday, period=self.p.period)
        
        #
        rs = Vmaup/Vmadn
        
        self.lines.vrsi = 100.0-100.0/(1.0+rs)
        super(VRSI_test, self).__init__()

不过在自定义指标时,可以大部分借鉴RSI的代码,于是我们只需要把C-REF(C,1)改为V-REF(V,1)即可。从代码上可以很清楚的了解到VRSI这个指标只跟成交量的变化有关,量增为正,量减为负,周期内的量增平均值/量减平均值即量的相对强弱指标,数值越低表示缩量连续且缩量差值越大,数值越高则表示放量连续且放量数值越大。

再回过头来看MFI,MFI是TYP的值乘上VOL的值,它相当于典型股价的成交额,它即有了量的因素,又乘上了价的因素,属于量价合一的指标。而VRSI只有量的因素,RSI只有价的因素。不过反而单纯的RSI与VRSI才能组成一组比较严格的量价关系指标组合。

CC_02_BIAS指标

Backtrader中并没有内置的BIAS指标。BIAS指标,即乖离率指标,是一个超买超卖指标,其含义是收盘价与某一周期均线的乖离率,算法很简单:(close-ma)/ma。如果需要在Backtrader中使用BIAS指标,需要自定义实现。

class myBIAS(Indicator):
    lines = ('bias','bima')  # 定义输出线
    params = (
    ('p1',20),('p2',6))
    plotinfo = dict(plotymargin=0.05, )

    def __init__(self):
        ma1 = bt.indicators.SMA(self.data.close, period=self.p.p1)
        bias = (self.data.close - ma1)/ma1 * 100.0
        
        self.lines.bias = bias
        self.lines.bima = bt.indicators.SMA(bias, period = self.p.p2)
        
        super(myBIAS, self).__init__()

02_均线型与成交量型

JX02_ACD升降线指标

在第三章第7节中讨论过ACD升降线指标公式,ACD即Accumulation Distribution Line,所谓的累积分布线。这个公式的特点是根据当前收盘价是否大于昨收(即当天上涨还是下跌)来选择用TrueHigh(Max(HIGH, REF(C,1)))还是用TrueLow(Min(LOW,REF(C,1))),从实际的情况下看,上涨取最低值,下跌取最高值。然后用收盘价CLOSE去减,就得到所谓的多空力量或买卖盘力量。

M:=20;
LC:=REF(CLOSE,1);
DIF:=CLOSE-IF(CLOSE>LC,MIN(LOW,LC),MAX(HIGH,LC));
ACD:SUM(IF(CLOSE=LC,0,DIF),0);
MAACD:EXPMEMA(ACD,M);

前面也讨论过,由于ACD的数值没有做归一化,它在不同股价时取值范围是不同的,所以单是ACD没有定量的策略,仅仅可以支持双均线的金叉/死叉策略。不过粗略地看ACD的稳健性与KAMA差不多,所以这里我们就把ACD的指标做到backtrader中来进行回测检验一下。

from backtrader.indicators import Min, Max
class myACD(Indicator):
    lines = ('acd','maacd')  # 定义输出线
    params = (
    ('p1',20),)
    plotinfo = dict(plotymargin=0.05,)# plotyhlines=[-30, 30])

    def __init__(self):
        lc=self.data.close(-1)
        vmin = Min(self.data.low, lc)
        vmax = Max(self.data.high, lc)
        upday = Max(self.data.close - vmin, 0.0)
        dnday = Min(self.data.close - vmax, 0.0)
        
        cd = upday + dnday
        self.lines.acd = bt.indicators.CumulativeSum(cd)
        self.lines.maacd = bt.indicators.EMA(self.lines.acd, period=self.p.p1)
        
        super(myACD, self).__init__()

这里有一个问题要注意一下,单独使用Max()好像不会报错,但只要使用Min()的时候就会报错

NameError: name 'Min' is not defined

由于Max和Min都是backtrader的内置函数,需要从bt.indicators模块中导入

另外,__init__中的每一次赋值都是相当于给整列数据赋值,所以可以利用这一点,分别给正值项做一列、负值项做一列,最后两列相加即可。

从backtrader的ACD图线与股票软件的进行比较,大致的曲线是近似的,但取值范围稍微有些不一样,暂时不知道不完全相同的原因在哪,但并不影响我们的进度。

在这里,添加简单的交叉策略进行回测,结果与我们之前的预估差不多,大部分的股票ACD交叉策略的确与KAMA类似,但也有少数差别很多的,例如西部、蓝色等。

如果仔细的进入单支股票的回测进行观察,就不难发现,有些股票在震荡段时ACD与MAACD之间一直是纠缠在一起,两者并没有出现明显的分离情况,那么在这一段里并没有进入交易的必要(椭圆圈内),只有当ACD与MAACD有了明显的分离时,才需要进行交易(见右侧3个黄箭头)。

但是对于原生的ACD而言,是没有办法对不同股价的票进行区分的,它在股票软件里图线显示是人进行观察而得出的结论,人为根据经验觉得某一段ACD两线没有分开则可以不交易。

所以,要正常的使用这个指标,就需要再进一步对原始指标进行一些更改,比如归一化使其能用于各个股票,再比如计算两条线的分离程度,当大于某一值的时候才满足买入条件等。到这里我还是懒惰了,只想赶紧进行下一条,这些进阶的内容就先搁置在这里,什么时候有兴趣了再折腾吧。

JX08_BBIBOLL指标

在第三章第7节的JX03中,我们讨论了BBI多空均线指标,当时我们查到的资料是BBI=Bull and Bear Index,如果以这样的名字命名的话,按理这就是我们国家的硬翻译了。但后来在BBIBOLL的介绍中,又发现了一种解释BBI = Breakout Bands Indicator,中文叫“突破带指标”。到底哪一个是对的呢?不过争论名称并没有什么意义,我们需要知道它怎么用才行。

在前面的章节里,我们研究过,BBI的指标的计算逻辑与UDL引力线指标是一模一样的,只是4个参数周期的取值不同而已。并且我们发现UDL可以近似的使用EMA9来替代,但BBI与EMA11仍有小部分的差距在里面。典型的BBI用法是收盘价站上BBI买入,下破BBI卖出,并且BBI本身有支撑和阻力的作用(触及不破反向),这样的策略也可以放在UDL引力线上应用,也就相当于直接可以用EMA9来做单均线策略,这些都是几个指标之间的替代和近似关系,暂时就讨论这么多,还是回到BBIBOLL的应用上来。

BBIBOLL多空布林线指标是一种结合了多空线(BBI)和布林线(BOLL)的技术分析工具,它用BBI做了中轨,这就与标准BOLL线的中轨使用简单移动平均线MA会有一些差别。

根据上图,主图为BBIBOLL的指标,副图1是标准BOLL指标,副图2是计算了两者的极限带宽度的曲线。可以看到,首先中轨BBI会比MA要更贴近于K线的运动,它不会明显的远离K线。其次,求标准差的时候,BOLL的标准差是STD(C,20)即求20周期内收盘价的标准差,而BBIBOLL的标准差为STD(BBIBOLL,11),为什么使用11,这个是不是跟我们前面拟合的BBI大约是EMA11有关也不能确定,但似乎还真有那么一点关系;所以BBIBOLL的周期11比BOLL的周期20要短,它的上下轨的变化就更明显。然后,标准布林是中轨加减2倍的标准差,BBIBOLL是中轨加减6倍的标准差,这也就决定了BBIBOLL的上下轨的幅度要选大于标准BOLL带。这个也可以在副图2的两者极限宽度看出来,桃红的BBIBOLL宽度大部分都在标准BOLL带宽度之上。从这一点来说,BBIBOLL的上下轨并不适合用来做买卖点,反而是上下轨的距离(带宽度)是用来做辅助判断的。

对于BBIBOLL指标,中轨是BBI,它是不会变的,可以更改的2个参数都是对于上下轨的幅度和宽度的调节,通过调节参数我们发现,当默认11的参数减少,则STD(BBI,N)的标准差样本数减少,上下轨的变化速度更快了。而要让STD(BBI,N)的值变小,就是其标准差(与BBI的偏差)变小,就意味着BBI本身是斜率变缓或者走平。此时我们再来理解一下这几句引文

高价区收盘价跌破BBI线,并且上下轨相距非常远时,为卖出信号。
低价区收盘价突破BBI线,并且上下轨相距非常近时,为买入信号。

轨道收敛:说明行情即将变盘,向上或向下突破。
轨道发散:表明将向上或向下扩大趋势。

上下轨相距远,则代表BBI斜率大,也就是这段时间股价单边幅度大,于是跌破BBI代表当前为下跌趋势,是卖出止损的最后机会;轨道收敛,则代表BBI斜率变小或走平,代表股价进入一个阻力区,那么突破BBI向上则相当于突破阻力区开启一波行情,可以认为是买点。

可以尝试把BBIBOLL的N周期由11减小(比如6),并综合考虑BOLL的策略进行综合,例如BOLL可以在下轨买入,但如果发现BOLL的WID以及WIDBBI都在增大时,代表下跌行情没有完成,则不能买入;需要等到WID低于0.15并且WIDBBI拐头下跌时,BOLL下轨才可以买入。又例如经典的布林策略可以添加突破中轨的策略,把它跟收盘站上BBI结合起来,作为一个分支策略的买点。但要注意一个问题,BBIBOLL收口即WIDBBI非常小的时候,站上BBI上涨的可能性比较大,但也会遇到站下BBI只是一个陷阱,之后股价疯狂下跌的情况。

关于BBIBOLL+BOLL的策略研究后续再重点讨论,这里先研究到这里,我们先把这个指标在backtrader中实现了。BBIBOLL是主图指标,需要加上subplot=False的。

class myBBIBOLL(Indicator):
    lines = ('bbi','upr','dwn')  # 定义输出线
    params = (
    ('p1',11),('p2',6))
    plotinfo = dict(plotymargin=0.05, )
    plotinfo = dict(subplot=False)   
    def __init__(self):
        
        cv = self.data.close
        ma1 = bt.indicators.SMA(cv,period=3)
        ma2 = bt.indicators.SMA(cv,period=6)
        ma3 = bt.indicators.SMA(cv,period=12)
        ma4 = bt.indicators.SMA(cv,period=24)
        bbi = (ma1+ma2+ma3+ma4)/4.0
        
        std1 = bt.indicators.StdDev(bbi,period = self.p.p1)
        mstd1 = self.p.p2 * std1
        
        self.l.upr = bbi+mstd1
        self.l.dwn = bbi-mstd1
        self.l.bbi = bbi
        
        super(myBBIBOLL, self).__init__()

另外,我们再制作一个BBIBOLL线宽度的指标,由于这个指标是放副图的,还需要把上面的代码复制一下,但把plotinfo = dict(subplot=True)   写出来。完成后在策略中同时加载BBIBOLL和BBIBOLLWID两个指标,就可以分别在主图和副图上显示了。

CJ02_OBV能量潮指标

能量潮OBV是典型的成交量指标,它只与成交量相关,当上涨时取成交量正值,下跌时取成交量负值,然后进行累加。这是最简洁但又非常暴力的分析指标。

M:=30;
VA:=IF(CLOSE>REF(CLOSE,1),VOL,-VOL);
OBV:SUM(IF(CLOSE=REF(CLOSE,1),0,VA),0);
MAOBV:MA(OBV,M);

在backtrader中没有内置的OBV指标,前面我们在review内置指标时的确也很少看到与成交量相关的指标,这里需要把指标公式移植过来。这里我们使用bt.If()函数来得到成交量正,负的line的值。

class myOBV(Indicator):
    lines = ('obv','maobv',)  # 定义输出线
    params = (
    ('p1',30),)
    plotinfo = dict(plotymargin=0.05, )

    def __init__(self):
        cv = self.data.close - self.data.close(-1)
        va = bt.If(cv>0, self.data.volume, -1*self.data.volume)
        obv = bt.indicators.CumulativeSum(va)
        
        maobv = bt.indicators.SMA(obv,period=self.p.p1)
        
        self.lines.obv = obv
        self.lines.maobv = maobv
        super(myOBV, self).__init__()

以上,就把OBV的指标在backtrader中实现了。

03_趋势型指标

趋势型指标中有几个是与成交量相关的,这些指标再叠加上多空力量差值或比值来显示上涨的力量或趋势。

QS02_CHO佳庆指标

在OBV中,以今收>昨收为判断条件,只要是上涨的成交量取正值(多头动能),只要是下跌的成交量取负值(空头动能)。

对于佳庆指标,则使用收盘价减最低价表示多方力量而使用最高价减收盘价表示空方力量。

N1:=10;
N2:=20;
M:=6;
 
MID:=SUM(VOL*(2*CLOSE-HIGH-LOW)/(HIGH+LOW),0);
CHO:MA(MID,N1)-MA(MID,N2);
MACHO:MA(CHO,M);

移植到backtrader中的指标为

class myCHO(Indicator):
    lines = ('cho','macho',)  # 定义输出线
    params = (
    ('p1',10),('p2',20),('p3',6),)

    def __init__(self):
        power1= 2*self.data.close - self.data.high - self.data.low
        vpower = self.data.volume * power1 / (self.data.high + self.data.low)
        
        mid = bt.indicators.CumulativeSum(vpower)
        
        ma1 = bt.indicators.SMA(mid, period=self.p.p1)
        ma2 = bt.indicators.SMA(mid, period=self.p.p2)
        
        cho = ma1 - ma2
        macho= bt.indicators.SMA(cho, period=self.p.p3)
        
        self.lines.cho = cho
        self.lines.macho = macho

        super(myCHO, self).__init__()

QS05_EMV简易波动指标

EMV也是将价格和成交量的变化结合而成的指标,这里的成交量会使用平均值除以成交量,而价格的趋势采用的(H+L)/2的平均价格减去上一个周期的平均价格,然后除以平均价格得到了百分比,所以EMV对于所有的股票其数量级都是一致的,这一点比OBV以及CHO都要通用性强一些。

N:=14;
M:=9;
 
VOLUME:=MA(VOL,N)/VOL;
MID:=100*(HIGH+LOW-REF(HIGH+LOW,1))/(HIGH+LOW);
EMV:MA(MID*VOLUME*(HIGH-LOW)/MA(HIGH-LOW,N),N);
MAEMV:MA(EMV,M);

这里我们就直接在backtrader中实现这个指标

class myEMV(Indicator):
    lines = ('emv','maemv',)  # 定义输出线
    params = (
    ('p1',14),('p2',9),)

    def __init__(self):
        vol1 = bt.indicators.SMA(self.data.volume,period=self.p.p1)/self.data.volume
        mid1 = self.data.high+self.data.low
        mid = 100* (mid1 - mid1(-1))/ mid1
        ma2 = bt.indicators.SMA(self.data.high-self.data.low, period=self.p.p1)
        emv1 = bt.indicators.SMA(mid*vol1*(self.data.high-self.data.low)/ma2, period=self.p.p1)
        maemv = bt.indicators.SMA(emv1, period=self.p.p2)
        self.lines.emv = emv1
        self.lines.maemv = maemv

        super(myEMV, self).__init__()


class EMV_test_1(BaseSt):
     params = (
         ('stra_name','EMV_test_1'),
         ('p1',14),  
         ('tradeCnt',1),
         ('sucessCnt',0),)            
               
     def __init__(self): 
         self.order = None
         
         emv = myEMV(self.data)
         self.crs = bt.indicators.CrossOver(emv.l.emv, emv.l.maemv)

     def next(self):
         if self.order:  # 检查是否有指令等待执行
             return

         if not self.position:  # 没有持仓 才会进入 
             if self.crs>0:  
                 self.order = self.buy()  # 执行买入
         else:
             if self.crs<0:  
                 self.order = self.sell()  # 执行卖出          
        
run_main_plot_ex2 (df_list,EMV_test_1,0)

----------------------
策略为 EMV_test_1 , 期末总资金 105796.95  盈利为 5796.95 总共交易次数为 36 ,交易成功率为 36.1%

QS07_VPT量价曲线指标

跟上面的CHO,EMV差不多,VPT也是基于量和价关系的技术分析指标,价格的关系也很简单,就是日涨跌幅,涨为正跌为负,然后乘上成交量,并做N周期的累加,这个其实比前面两个更简单,因为CHO是计算的多方力量的差值,而EMV计算的是平均价格的涨跌。

N:=51;
M:=6;
 
VPT:SUM(VOL*(CLOSE-REF(CLOSE,1))/REF(CLOSE,1),N);
MAVPT:MA(VPT,M);

这里就直接在backtrader中把它实现。

class myVPT(Indicator):
    lines = ('vpt','mavpt',)  # 定义输出线
    params = (
    ('p1',51),('p2',6),)

    def __init__(self):
        cpct = (self.data.close - self.data.close(-1))/self.data.close(-1)
        power1 = cpct*self.data.volume
        
        vpt = bt.indicators.SumN(power1, period=self.p.p1)
        mavpt = bt.indicators.SMA(vpt, period=self.p.p2)
        
        self.lines.vpt = vpt
        self.lines.mavpt = mavpt

        super(myVPT, self).__init__()


class VPT_test_1(BaseSt):
    略

run_main_plot_ex2 (df_list,VPT_test_1,0)

----------------------------
策略为 VPT_test_1 , 期末总资金 98249.04  盈利为 -1750.96 总共交易次数为 47 ,交易成功率为 34.0%

QS08_WVAD威廉变异离散量指标

WVAD也是计算了成交量与价格两个部分,在价格方面使用的是收盘价减开盘价除以当天的振幅的计算方式,对应的是阳线为正,阴线为负,再乘上成交量就会将成交量变为有正有负的数值,这种思路跟上面的VPT是类似的,只是用到的价格计算的因子不一样。

N:=24;
M:=6;
 
WVAD:SUM((CLOSE-OPEN)/(HIGH-LOW)*VOL,N)/10000;
MAWVAD:MA(WVAD,M);

仍然是直接在backtrader中把它实现出来。

class myWVAD(Indicator):
    lines = ('wvad','mawvad',)  # 定义输出线
    params = (
    ('p1',24),('p2',6),)

    def __init__(self):
        cpct = (self.data.close - self.data.open)/(self.data.high-self.data.low)
        power1 = cpct*self.data.volume
        
        wvad = bt.indicators.SumN(power1, period=self.p.p1)/10000.0
        mawvad = bt.indicators.SMA(wvad, period=self.p.p2)
        
        self.lines.wvad = wvad
        self.lines.mawvad = mawvad

        super(myWVAD, self).__init__()


class WVAD_test_1(BaseSt):
     params = (
         ('stra_name','WVAD_test_1'),
         ('p1',24),  
         ('tradeCnt',1),
         ('sucessCnt',0),)            
               
     def __init__(self): 
         self.order = None
         
         wvad = myWVAD(self.data)
         self.crs = bt.indicators.CrossOver(wvad.l.wvad, wvad.l.mawvad)

     def next(self):
         if self.order:  # 检查是否有指令等待执行
             return

         if not self.position:  # 没有持仓 才会进入 
             if self.crs>0:  
                 self.order = self.buy()  # 执行买入
         else:
             if self.crs<0:  
                 self.order = self.sell()  # 执行卖出          
        
run_main_plot_ex2 (df_list,WVAD_test_1,0)

----------------------------
策略为 WVAD_test_1 , 期末总资金 106770.76  盈利为 6770.76 总共交易次数为 53 ,交易成功率为 45.3%

04_小结

把股票软件的指标移植到backtrader中来,一般采用2种方式。第一种会很简单,是直接在backtrader内置指标的基础上进行的小更改,那么我们可以直接继承这个指标的类,然后再进行简单的计算就可以了。第二种就是通过自定义指标类的方式进行,其实上面的几个指标都是采用这种方式进行的。

在自定义指标类中,对于指标的计算的逻辑实现也非常的自由,一般而言直接可以在__init__()中就把指标创建好,如果的确有困难也可以在next中来逐项实现。在__init__()中,每一个参与计算的都是一列(line)数据,相当于pandas表中的列。

在股票软件中一些常见的函数,其实在backtrader中也几乎都有对应的函数,

股票软件backtrader
IF( condition, s1, s2)

bt.IF(condition, S1,S2)

bt.And(Max(C,0),1)

SUM(C, N)       bt.indicators.SumN(self.data, period=N)
SUM(C, 0) 从头开始累加bt.indicators.CumulativeSum(self.data)
MA(C,5)        bt.indicators.SMA(self.data,period=5) 

只要我们清楚了这些函数,就会发现把股票软件中已经存在的指标公式移植过来并不是一件困难的事。

不过,backtrader默认接收的数据项只有开、高、低、收和成交量,而前面我们基本上也只从akshare或tushare.pro中获取这几个数据,其他的数据项被忽略的。但无论如何,股票软件本身可以获取的数据项是非常多的,比较WINNER函数获取某价格的获利比例等,另外比较典型的是涨跌家数比等,如果我们想在backtrader中使用这些数据,那就需要特殊的操作了,这个在后面的章节里再详细讨论。

经过到目前为止的这些实践,我们已经了解了许多backtrader内置的指标,股票软件内置的指标以及能够把一些股票软件内置的指标移植到backtrader中来进行回测,并且相应的根据backtrader的交易系统又在股票软件的指标上添加了更高级的功能,比如计算某个策略的收益率,比如对于连续的多个买点和卖点给予屏蔽等。在当前的基础上,我们对指标的认识有了较大的提高,接下来我们就尝试着进行复杂、综合策略的实践了。

股票量化交易中非常重要的一环,它可以通过历史数据对交易策略进行模拟和评估,从而评估策略的可行性和优劣性。在Python中,有很多开源的量化交易框架可以用来进行股票,如zipline、backtrader等。 下面是一个使用zipline框架进行简单交易策略的例子: 1. 安装zipline ```python pip install zipline ``` 2. 编写交易策略代码 ```python from zipline.api import order_target_percent, record, symbol def initialize(context): context.asset = symbol('AAPL') def handle_data(context, data): # 获取过去10天的收盘价 prices = data.history(context.asset, 'price', 10, '1d') # 计算平均价 mean_price = prices.mean() # 如果当前价格低于平均价,则买入 if data.current(context.asset, 'price') < mean_price: # 调整持仓比例至100% order_target_percent(context.asset, 1.0) # 否则卖出 else: # 调整持仓比例至0% order_target_percent(context.asset, 0.0) # 记录当前持仓比例 record(position=context.portfolio.positions[context.asset].amount) ``` 3. 运行 ```python from zipline import run_algorithm from zipline.api import symbol from datetime import datetime start = datetime(2016, 1, 1) end = datetime(2017, 1, 1) result = run_algorithm( start=start, end=end, initialize=initialize, capital_base=10000, handle_data=handle_data, bundle='quandl' ) ``` 在上述代码中,我们定义了一个简单的交易策略,即如果当前价格低于过去10天的平均价,则买入,否则卖出。然后我们使用zipline框架进行,设定开始和结束时间、初始资本、数据来源等参数,最终得到结果。 需要注意的是,这只是一个简单的例子,实际的交易策略可能会更加复杂,需要考虑更多的因素。另外,在进行股票时,也需要注意避免过度拟合或过度优化,以免出现虚高的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值