Backtrader 文档学习-Platform Concepts

Backtrader 文档学习-Platform Concepts

1.开始之前

导入backtrader ,以及backtrader 的指示器、数据反馈的模块 。

import backtrader as bt
import backtrader.indicators as btind
import backtrader.feeds as btfeeds

看看btind模块下有什么方法和属性:

obj_str = ''
for i in dir(btind):
    if i[:1] != '_' :
        obj_str += i + ','    
print(obj_str)

指示器中的指标函数很多,方法属性如下:

ADX,ADXR,AO,APO,ATR,AbsPriceOsc,AbsolutePriceOscillator,AccDeOsc,AccelerationDecelerationOscillator,Accum,AdaptiveMovingAverage,AdaptiveMovingAverageEnvelope,AdaptiveMovingAverageOsc,AdaptiveMovingAverageOscillator,All,AllN,And,Any,AnyN,ApplyN,ArithmeticMean,AroonDown,AroonIndicator,AroonOsc,AroonOscillator,AroonUp,AroonUpDown,AroonUpDownOsc,AroonUpDownOscillator,Average,AverageDirectionalMovementIndex,AverageDirectionalMovementIndexRating,AverageTrueRange,AverageWeighted,AwesomeOsc,AwesomeOscillator,BBands,BaseApplyN,BollingerBands,BollingerBandsPct,CCI,Cmp,CmpEx,CointN,CommodityChannelIndex,CrossDown,CrossOver,CrossUp,CumSum,CumulativeSum,DEMA,DEMAEnvelope,DEMAOsc,DEMAOscillator,DI,DM,DMA,DMAEnvelope,DMAOsc,DMAOscillator,DMI,DPO,DV2,DemarkPivotPoint,DetrendedPriceOscillator,DicksonMA,DicksonMAEnvelope,DicksonMAOsc,DicksonMAOscillator,DicksonMovingAverage,DicksonMovingAverageEnvelope,DicksonMovingAverageOsc,DicksonMovingAverageOscillator,DirectionalIndicator,DirectionalMovement,DirectionalMovementIndex,DivByZero,DivZeroByZero,DoubleExponentialMovingAverage,DoubleExponentialMovingAverageEnvelope,DoubleExponentialMovingAverageOsc,DoubleExponentialMovingAverageOscillator,DownDay,DownDayBool,DownMove,EC,ECEnvelope,ECOsc,ECOscillator,EMA,EMAEnvelope,EMAOsc,EMAOscillator,Envelope,EnvelopeMixIn,ErrorCorrecting,ErrorCorrectingEnvelope,ErrorCorrectingOsc,ErrorCorrectingOscillator,ExpSmoothing,ExpSmoothingDynamic,ExponentialMovingAverage,ExponentialMovingAverageEnvelope,ExponentialMovingAverageOsc,ExponentialMovingAverageOscillator,ExponentialSmoothing,ExponentialSmoothingDynamic,FibonacciPivotPoint,FindFirstIndex,FindFirstIndexHighest,FindFirstIndexLowest,FindLastIndex,FindLastIndexHighest,FindLastIndexLowest,HMA,HMAEnvelope,HMAOsc,HMAOscillator,HeikinAshi,Highest,HullMA,HullMAEnvelope,HullMAOsc,HullMAOscillator,HullMovingAverage,HullMovingAverageEnvelope,HullMovingAverageOsc,HullMovingAverageOscillator,Hurst,HurstExponent,Ichimoku,If,Indicator,KAMA,KAMAEnvelope,KAMAOsc,KAMAOscillator,KST,KnowSureThing,LAGF,LRSI,LaguerreFilter,LaguerreRSI,LineActions,List,Logic,Lowest,MACD,MACDHisto,MACDHistogram,MAXINT,Max,MaxN,Mean,MeanDev,MeanDeviation,MetaMovAvBase,Min,MinN,MinusDI,MinusDirectionalIndicator,ModifiedMovingAverage,ModifiedMovingAverageEnvelope,ModifiedMovingAverageOsc,ModifiedMovingAverageOscillator,Momentum,MomentumOsc,MomentumOscillator,MovAv,MovingAverage,MovingAverageAdaptive,MovingAverageAdaptiveEnvelope,MovingAverageAdaptiveOsc,MovingAverageAdaptiveOscillator,MovingAverageBase,MovingAverageDoubleExponential,MovingAverageDoubleExponentialEnvelope,MovingAverageDoubleExponentialOsc,MovingAverageDoubleExponentialOscillator,MovingAverageExponential,MovingAverageExponentialEnvelope,MovingAverageExponentialOsc,MovingAverageExponentialOscillator,MovingAverageSimple,MovingAverageSimpleEnvelope,MovingAverageSimpleOsc,MovingAverageSimpleOscillator,MovingAverageSmoothed,MovingAverageSmoothedEnvelope,MovingAverageSmoothedOsc,MovingAverageSmoothedOscillator,MovingAverageTripleExponential,MovingAverageTripleExponentialEnvelope,MovingAverageTripleExponentialOsc,MovingAverageTripleExponentialOscillator,MovingAverageWeighted,MovingAverageWeightedEnvelope,MovingAverageWeightedOsc,MovingAverageWeightedOscillator,MovingAverageWilder,MovingAverageWilderEnvelope,MovingAverageWilderOsc,MovingAverageWilderOscillator,MultiLogic,MultiLogicReduce,NZD,NonZeroDifference,OLS_BetaN,OLS_Slope_InterceptN,OLS_TransformationN,OperationN,Or,Oscillator,OscillatorMixIn,PGO,PPO,PPOShort,PSAR,ParabolicSAR,PctChange,PctRank,PercPriceOsc,PercPriceOscShort,PercentChange,PercentRank,PercentagePriceOscillator,PercentagePriceOscillatorShort,PeriodN,PivotPoint,PlusDI,PlusDirectionalIndicator,PrettyGoodOsc,PrettyGoodOscillator,PriceOsc,PriceOscillator,RMI,ROC,ROC100,RSI,RSI_Cutler,RSI_EMA,RSI_SMA,RSI_SMMA,RSI_Safe,RSI_Wilder,RateOfChange,RateOfChange100,Reduce,ReduceN,RelativeMomentumIndex,RelativeStrengthIndex,SMA,SMAEnvelope,SMAOsc,SMAOscillator,SMMA,SMMAEnvelope,SMMAOsc,SMMAOscillator,SimpleMovingAverage,SimpleMovingAverageEnvelope,SimpleMovingAverageOsc,SimpleMovingAverageOscillator,SmoothedMovingAverage,SmoothedMovingAverageEnvelope,SmoothedMovingAverageOsc,SmoothedMovingAverageOscillator,StandardDeviation,StdDev,Stochastic,StochasticFast,StochasticFull,StochasticSlow,Sum,SumN,TEMA,TEMAEnvelope,TEMAOsc,TEMAOscillator,TR,TRIX,TSI,TripleExponentialMovingAverage,TripleExponentialMovingAverageEnvelope,TripleExponentialMovingAverageOsc,TripleExponentialMovingAverageOscillator,Trix,TrixSignal,TrueHigh,TrueLow,TrueRange,TrueStrengthIndicator,UltimateOscillator,UpDay,UpDayBool,UpMove,Vortex,WMA,WMAEnvelope,WMAOsc,WMAOscillator,WeightedAverage,WeightedMovingAverage,WeightedMovingAverageEnvelope,WeightedMovingAverageOsc,WeightedMovingAverageOscillator,WilderMA,WilderMAEnvelope,WilderMAOsc,WilderMAOscillator,WilliamsAD,WilliamsR,ZLEMA,ZLEMAEnvelope,ZLEMAOsc,ZLEMAOscillator,ZLInd,ZLIndEnvelope,ZLIndOsc,ZLIndOscillator,ZLIndicator,ZLIndicatorEnvelope,ZLIndicatorOsc,ZLIndicatorOscillator,ZeroLagEma,ZeroLagEmaEnvelope,ZeroLagEmaOsc,ZeroLagEmaOscillator,ZeroLagExponentialMovingAverage,ZeroLagExponentialMovingAverageEnvelope,ZeroLagExponentialMovingAverageOsc,ZeroLagExponentialMovingAverageOscillator,ZeroLagIndicator,ZeroLagIndicatorEnvelope,ZeroLagIndicatorOsc,ZeroLagIndicatorOscillator,absolute_import,accdecoscillator,alias,aroon,atr,awesomeoscillator,basicops,bollinger,bt,cci,cmp,contrib,crossover,dema,deviation,directionalmove,division,dma,dpo,dv2,ema,envelope,functools,haD,haDelta,hadelta,heikinashi,hma,hurst,ichimoku,kama,kst,linename,lrsi,mabase,macd,map,math,module,momentum,movav,movname,newaliases,newcls,newclsdct,newclsdoc,newclsname,ols,operator,oscillator,percentchange,percentrank,pivotpoint,prettygoodoscillator,priceoscillator,print_function,psar,range,rmi,rsi,sma,smma,stochastic,suffix,sys,trix,tsi,ultimateoscillator,unicode_literals,williams,with_metaclass,wma,zlema,zlind,

也可以直接通过bt导入下面的模块feeds indicators

import backtrader as bt
thefeed = bt.feeds.OneOfTheFeeds(...)
theind = bt.indicators.SimpleMovingAverage(...)

2.数据读入-无处不在

所有的策略都基于数据,平台端用户不用考虑如何接收数据。

Data Feeds are automagically provided member variables to the strategy in the form of an array and shortcuts to the array positions

数据源是以数组的形式或者对数组位置快捷访问的方式提供给strategy(策略)作为成员变量使用的。
这句话需要好好理解。

数组的形式: self.datas[0].close
数组位置快捷访问:self.data0.close

class MyStrategy(bt.Strategy):
    params = dict(period=20)

    def __init__(self):

        sma = btind.SimpleMovingAverage(self.datas[0], period=self.params.period)
        print(sma)
        # Keep a reference to the "close" line in the data[0] dataseries
        self.dataclose = self.datas[0].close
        self.dataclosetest = self.data0.close

        print('-'* 20,'close','-' * 20 )
        print(self.dataclose[0])
        print(self.dataclosetest[0])        

执行结果
sma 的数据类型backtrader.indicators.sma.SimpleMovingAverage

<backtrader.indicators.sma.SimpleMovingAverage object at 0x7f616bd7c820>
-------------------- close --------------------
133.01
133.01

Data Feeds get added to the platform and they will show up inside the strategy in the sequential order in which they were added to the system.

SQL语句按日期排序,不论是正序还是逆序desc ,到BackTrader中,都是按时间倒序排列,[0]是最后的日期。

3.快捷访问数据

self.datas数组项可以通过自动成员变量直接访问:

self.data targets self.datas[0]
self.dataX targets self.datas[X]

下面好好测试一下BackTrader的数据访问,数据之间的关系。

#<class 'backtrader.linebuffer.LineBuffer'>
print(type(cerebro.datas[0].close))

#<class 'backtrader.feeds.pandafeed.PandasData'>
print(type(cerebro.datas[0]))

#<class 'list'>
print(type(cerebro.datas))

# ('close', 'low', 'high', 'open', 'volume', 'openinterest', 'datetime')
print(cerebro.datas[0].getlinealiases())

结果如下:

<class 'backtrader.linebuffer.LineBuffer'>
<class 'backtrader.feeds.pandafeed.PandasData'>
<class 'list'>
('close', 'low', 'high', 'open', 'volume', 'openinterest', 'datetime')

在这里插入图片描述

说明:

  • 1、cerebro.datas数据集的类型是 <class ‘list’> ,对应载入cerebro的股票数据,可以是多个股票,就是股票的集合。
  • 2、cerebro.datas[0]是<class ‘backtrader.feeds.pandafeed.PandasData’>,其实与dataframe类似,包括以下列:(‘close’, ‘low’, ‘high’, ‘open’, ‘volume’, ‘openinterest’, ‘datetime’)的集合。
    -3、cerebro.datas[0].close就是dataframe的一列数据,BackTrader称之为Line <class ‘backtrader.linebuffer.LineBuffer’> 。
  • 4、cerebro.datas[0].close[0] 就是访问到数据的元素,实际数据值。
  • 5、cerebro.datas[0]都是按时间顺序逆序的,第一个元素index是0 ,下一个是-1 ,以此类推。
  • 6、时间序列必须用内置方法处理
print(cerebro.datas[0].datetime[0]) 
print(pd.to_datetime(cerebro.datas[0].datetime[0],unit='s'))
print(bt.num2date(cerebro.datas[0].datetime[0]))

结果如下:

737424.0
1970-01-09 12:50:24
2019-12-31 00:00:00

直接访问datetime列 ,是浮点数。
开始用pd.to_datetime 函数,转换的时间是1970年,迷惑很长时间。

重要:必须用自带的函数转换时间 bt.num2date()!!!

4.默认数据载入

class MyStrategy(bt.Strategy):
    params = dict(period=20)

    def __init__(self):

        sma = btind.SimpleMovingAverage(period=self.params.period)

    ...

self.data has been completely removed from the invocation of SimpleMovingAverage. If this is done, the indicator (in this case the SimpleMovingAverage) receives the first data of the object in which is being created (the Strategy), which is self.data (aka self.data0 or self.datas[0])

默认使用的self是什么?
如果只有一个数据源,cerebro只载入一个股票数据,不用显式指定,系统会缺省使用self.datas[0],self.data=self.data0=self.datas[0] ,也就是第一个加入的数据源 。所以说self.data 就没有显式出现在调用SMA指示器 。
如果有多个载入数据,还是注明好,清晰可读。

5. 一切都是数据载入

class MyStrategy(bt.Strategy):
    #定义策略全局参数
    params = dict(period1=20, period2=25, period3=10, period4)

    def __init__(self):
        # 使用SMA指示器载入datas[0],使用参数1
        sma1 = btind.SimpleMovingAverage(self.datas[0], period=self.p.period1)

        # This 2nd Moving Average operates using sma1 as "data"
        # 使用第一个SMA指示器数据再次作为SMA的数据载入,使用参数2
        sma2 = btind.SimpleMovingAverage(sma1, period=self.p.period2)

        # New data created via arithmetic operation
        # 随意的计算  
        something = sma2 - sma1 + self.data.close

        # This 3rd Moving Average operates using something  as "data"
        # 随意计算的结果作为第三次SMA数据载入,使用参数3
        sma3 = btind.SimpleMovingAverage(something, period=self.p.period3)

        # Comparison operators work too ...
        # 比较后取大值 ,有点疑问 ?
        greater = sma3 > sma1

        # Pointless Moving Average of True/False values but valid
        # This 4th Moving Average operates using greater  as "data"
        # 比较后结果第四次载入SMA指示器,使用参数4
        sma3 = btind.SimpleMovingAverage(greater, period=self.p.period4)

    ...

总而言之,所有的数据转换变化后,都可以作为数据载入,再次进行运算。

6. 参数

在backtrader中,所有的类都按照如下方法来使用参数:

1、声明一个带有缺省值的参数作为类的一个属性(元组或者字典结构)。
2、关键字类型参数(kwargs)会扫描匹配的参数,将值赋值给对应的参数,完成后从kwargs删除。
3、这些参数都可以在类实例中通过访问成员变量self.params(可以简写为self.p)来使用。
使用元组 tuple

class MyStrategy(bt.Strategy):
    params = (('period', 20),)

    def __init__(self):
        sma = btind.SimpleMovingAverage(self.data, period=self.p.period)

使用字典dict

class MyStrategy(bt.Strategy):
    params = dict(period=20)

    def __init__(self):
        sma = btind.SimpleMovingAverage(self.data, period=self.p.period)

7.Lines 关键的概念

从使用用户(通常是strategy)的角度来说,就是包括一个或多个line,这些line是一系列的数据,在图中可以形成一条线(line)。line是数组,不是pd.series ,没有index 。

class MyStrategy(bt.Strategy):
    params = dict(period=20)

    def __init__(self):

        self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period)

    def next(self):
        if self.movav.lines.sma[0] > self.data.lines.close[0]:
            print(round(self.movav.lines.sma[0],2),self.data.lines.close[0],'Simple Moving Average is greater than the closing price')

执行结果:

83.34 74.4 Simple Moving Average is greater than the closing price
83.16 75.1 Simple Moving Average is greater than the closing price
83.01 76.6 Simple Moving Average is greater than the closing price
82.92 80.1 Simple Moving Average is greater than the closing price
82.82 79.64 Simple Moving Average is greater than the closing price
82.69 78.52 Simple Moving Average is greater than the closing price
82.36 76.1 Simple Moving Average is greater than the closing price
81.87 74.28 Simple Moving Average is greater than the closing price
... ... 

self.movav:这个是一个SimpleMovingAverage的指标(indicator),本身就包含一个具有lines属性的sma。这里特殊注意一下,计算SimpleMovingAverage使用的self.data,没有指定具体Line的话,缺省用的是close价进行计算。

简单的方法访问lines:

xxx.lines 可以简化为 xxx.l
xxx.lines.name可以简化为xxx.lines_name

一些复杂的对象也可以通过如下方法访问:

self.data_name 等于 self.data.lines.name

如果有多个变量的话,也可以self.data1_name 替代self.data1.lines.name

此外,Line的名字也可以通过如下方式访问:

self.data.close and self.movav.sma

class MyStrategy(bt.Strategy):
    params = dict(period=20)

    def __init__(self):

        self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period)

    def next(self):
        # 数据集0 收盘价在110和120
        if (self.datas[0].close[0] > 110.0) & (self.datas[0].close[0] < 120.0):
            print(bt.num2date(self.datas[0].datetime[0]),'self.datas[0].close[0] > 110.0')
        
        # 数据集1 收盘价在110和120    
        if self.datas[1].close[0] > 9.0:
            print(bt.num2date(self.datas[1].datetime[0]),'self.datas[1].close[0] > 9.0')
        
        # 数据集SMA 收盘价在88和90
        if (self.movav.lines.sma[0] > 88) & (self.movav.sma[0] < 90) :
            print(round(self.movav.lines.sma[0],2),'self.movav.lines.sma[0] > 88')
            print(self.movav.getlinealiases())
            #print(cerebro.datas[0].getlinealiases())

结果如下:

2018-04-18 00:00:00 self.datas[1].close[0] > 9.0
2018-04-24 00:00:00 self.datas[1].close[0] > 9.0
2018-04-25 00:00:00 self.datas[1].close[0] > 9.0
2018-04-26 00:00:00 self.datas[1].close[0] > 9.0
2019-04-09 00:00:00 self.datas[0].close[0] > 110.0
88.78 self.movav.lines.sma[0] > 88
('sma',)
89.64 self.movav.lines.sma[0] > 88
('sma',)
2019-06-20 00:00:00 self.datas[0].close[0] > 110.0
2019-06-21 00:00:00 self.datas[0].close[0] > 110.0
2019-06-24 00:00:00 self.datas[0].close[0] > 110.0
2019-06-25 00:00:00 self.datas[0].close[0] > 110.0
2019-06-26 00:00:00 self.datas[0].close[0] > 110.0
2019-06-27 00:00:00 self.datas[0].close[0] > 110.0
2019-06-28 00:00:00 self.datas[0].close[0] > 110.0
2019-07-24 00:00:00 self.datas[0].close[0] > 110.0
2019-08-01 00:00:00 self.datas[0].close[0] > 110.0
2019-08-02 00:00:00 self.datas[0].close[0] > 110.0
2019-08-05 00:00:00 self.datas[0].close[0] > 110.0
2019-08-07 00:00:00 self.datas[0].close[0] > 110.0
2019-08-09 00:00:00 self.datas[0].close[0] > 110.0

需要好好说明一下:

  • 简写示例

if (self.movav.lines.sma[0] > 88) & (self.movav.sma[0] < 90) :

  • 多数据源

两种方式引用
self.movav.lines.sma[0] = self.movav.sma[0]

cerebro载入两个股票数据。
特别注意:调用datas ,有s
self.datas[0].close[0]
self.datas[0].close[0]
self.datas[0].close[0]

  • SMA指示器调用后,是数组,没有日期字段!!!
    开始也想增加打印日期,

print(bt.num2date(self.movav.datetime[0]) 无日期

只有一列

print(self.movav.getlinealiases())
('sma',)

未完待续!!!

8.访问Lines

data = btfeeds.BacktraderCSVData(dataname='mydata.csv')

...

class MyStrategy(bt.Strategy):

    ...

    def next(self):

        if self.data.close[0] > 30.0:
            ...

两个表达式等价

if self.data.close[0] > 30.0:
if self.data.lines.close[0] > 30.0:

9. Lines长度

Lines是随时变化的,run的时候,next不断改变Lines的长度,在数据载入,策略,指示器应用中,需要测量Lines的长度。
两个函数:len和buflen之间的区别:

  • len已处理了Lines
  • buflen为数据加载Lines的总数 ,buflen是
    还是载入的两个数据集,验证一下:
class MyStrategy1(bt.Strategy):
    params = dict(period=20)

    def __init__(self):

        self.movav0 = btind.SimpleMovingAverage(self.data0, period=self.p.period)
        self.movav1 = btind.SimpleMovingAverage(self.data1, period=self.p.period)
        print('self.p.period:',self.p.period)

    def next(self):
        print('self.datas[0].lines.buflen()',self.datas[0].lines.buflen())
        print('self.datas[1].buflen()',self.datas[1].buflen())
        print('len(self.datas[0])',len(self.datas[0]))
        print('len(self.datas[1])',len(self.datas[1]))
            
cerebro.addstrategy(MyStrategy1,period=30)

# Run over everything
cerebro.run()
            

结果如下:

self.p.period: 30
self.p.period: 30
self.datas[0].lines.buflen() 244
self.datas[1].buflen() 244
len(self.datas[0]) 30
len(self.datas[1]) 30
self.datas[0].lines.buflen() 244
self.datas[1].buflen() 244
len(self.datas[0]) 31
len(self.datas[1]) 31
... ...
len(self.datas[0]) 243
len(self.datas[1]) 243
self.datas[0].lines.buflen() 244
self.datas[1].buflen() 244
len(self.datas[0]) 244
len(self.datas[1]) 244

分析说明:

  • self.datas[0].lines.buflen() 和self.datas[1].buflen() 一样,可以忽略lines对象。
    print('self.datas[0].lines.buflen()',self.datas[0].lines.buflen())
    print('self.datas[1].buflen()',self.datas[1].buflen())
  • 在init中print(‘self.p.period:’,self.p.period)执行了两次,因为每次加载策略的时候,不同的数据集都执行init() 。
  • print(‘self.p.period:’,self.p.period) 打印值是30 ,非默认的20 。
  • buflen()是lines的方法

10.Lines和参数的继承

  • 支持多重继承
  • 继承基类的参数
  • 如果多个基类定义相同的参数,则使用继承列表中最后一个类的默认值
  • 如果在子类中重新定义了相同的参数,则新的默认值将接管基类的默认值

lines还有什么方法属性:

class MyStrategy2(bt.Strategy):
    params = dict(period=20)

    def __init__(self):

        self.movav0 = btind.SimpleMovingAverage(self.data0, period=self.p.period)
        self.movav1 = btind.SimpleMovingAverage(self.data1, period=self.p.period)
        print('self.p.period:',self.p.period)
        obj_str = ''
        for i in dir(self.datas[0].lines):
            if i[:1] != '_' :
                obj_str += i + ','    
        print(obj_str)

    def next(self):
        pass

            
cerebro.addstrategy(MyStrategy2,period=30)

# Run over everything
cerebro.run()
           

结果如下:

self.p.period: 30
advance,backwards,buflen,close,datetime,extend,extrasize,forward,fullsize,get,getlinealiases,high,home,itersize,lines,low,open,openinterest,reset,rewind,size,volume,

11.索引0和-1

重点:
0指的是系统当前正在处理的数据,而不是第一个数据。
strategy类只会进行取值操作,而indicator只会进行赋值操作。
在backtrader中,-1指的是当前处理数据(索引为0)的上一个数据。

def next(self):
    if self.data.close[0] > self.data.close[-1]:
        print('Closing price is higher today')

以当前值0为基准,上一个值索引-1,上上一个值为-2,还可以继续-3,-4…

12.切片

BackTrader的切片和Python的概念不同:
用Python的list切片从头到尾的切片,在BackTrader不能用。

myslice = self.my_sma[0:]  # slice from the beginning til the end
myslice = self.my_sma[0:-1]  # slice from the beginning til the end

按逆序也不行

myslice = cerebro.datas[0].close[-1:-3]  # from last value backwards to the 3rd last value
myslice = cerebro.datas[0].close[-3:-1]  # from last value backwards to the 3rd last value

BackTrader的切片用函数:

myslice = self.my_sma.get(ago=0, size=1)  # default values shown

测试一下:

cerebro.datas[1].close.get(ago=0, size=3)
结果:
array('d', [5.82, 5.85, 5.78])

遍历处理日期:

for i in cerebro.datas[1].datetime.get(ago=0, size=3) :
    print(bt.num2date(i))
结果:

2019-12-27 00:00:00
2019-12-30 00:00:00
2019-12-31 00:00:00    

数组是支持Python的切片:

cerebro.datas[1].datetime.get(ago=0, size=5)[0:3]
结果:
array('d', [737418.0, 737419.0, 737420.0])

需要注意返回值的顺序:
最左的值对应离ago最远的值,最右的是ago索引对应的值。
例如如下是返回5最新的个值(不包括正在处理的值),通过下图理解一下:
在这里插入图片描述
在这里插入图片描述

13.Lines 的索引延迟

文档上的有点小瑕疵

self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period)

定义变量应该是 self.sma

class MyStrategy4(bt.Strategy):
    params = dict(period=20)

    def __init__(self):

        self.sma = btind.SimpleMovingAverage(self.datas[1], period=self.p.period)
        self.cmpval = self.datas[1].close(-1) > self.sma

    def next(self):
        if self.cmpval[0]:
            print(self.cmpval[0],self.sma[0],self.datas[1].close[0],self.datas[1].close[-1])

cerebro.addstrategy(MyStrategy4)

# Run over everything
cerebro.run()                         

分析说明:
关键是close(-1) 是圆括号,不是取值的方括号!!!
对象类型是:<backtrader.linebuffer._LineDelay object at 0x7f616a25e1c0>

self.datas[1].close(-1)

重新简化,测试:

class MyStrategy5(bt.Strategy):
    params = dict(period=20)

    def __init__(self):

        self.cmpval = self.datas[1].close(-1) 

    def next(self):
        print(self.datas[1].close[0],self.cmpval[0])
    
cerebro.addstrategy(MyStrategy5)

# Run over everything
cerebro.run()         
                        

结果 ,第一列第一个 = 第二列第二个,以此类推,

5.12 5.03
5.49 5.12
5.79 5.49
5.79 5.79
5.8 5.79
5.84 5.8

14. Lines 耦合

应用场景:
耦合主要用于将时间窗口不同的两个line建立关系。比如:
不同时间窗口的数据源具有不同的长度,indcator在使用这些数据的时候会复制这个长度。
股票的日线数据,每年大约250个(bar,对应250个工作日)
股票的周线数据,每年大约52个(bar,对应52周)

原文:

The reader could imagine a date comparison taking place in the background to find out a day - week correspondence, but:

  • Indicators are just mathematical formulas and have no datetime information

They know nothing about the environment, just that if the data provides enough values, a calculation can take place.

读者可以想象一个日期比较的场景,找出日和星期的通信 ,但是:
指示器可以调整数学公式,没有(不考虑)日期信息
读者对于场景一无所知,如能够提供足够的数据,就可以计算 。

测试一下效果:

class MyStrategy6(bt.Strategy):
    params = dict(period=20)
 
    def __init__(self):
 
        # data0 是日线数据
        self.sma0 = btind.SMA(self.data1.close, period=5)  # 5 日线的平均
        
        # data1 是周线数据
        self.sma1 = btind.SMA(self.data1.close, period=5)  # 5 周线的平均
        self.couplesma = self.sma1()
        self.buysig = self.sma0 > self.couplesma
        
        #print(type(sma1())) # <class 'backtrader.metabase.LinesCoupler_14'>
        #print(self.sma1.get(ago=0, size=5)) # <class 'backtrader.indicators.sma.SMA'>
        
 
    def next(self):
        print('sma1:',self.sma1.get(ago=0,size = len(self.sma1)))
        print('self.couplesma:',self.couplesma.get(ago=0,size = len(self.sma1)))
        if self.buysig[0] :
            print(self.buysig[0],'日均线大于周均线!')

# 初始化一个空的cerebor             
cerebro = declare_cerebar()
# 加入策略
cerebro.addstrategy(MyStrategy6)

# Run over everything
cerebro.run()        

sma1通过加一个括号()将数据适配到每日的时间窗口
程序做了调整,说明如下:

  • 新的数据类型

#print(type(sma1())) # <class ‘backtrader.metabase.LinesCoupler_14’>

sma1()就是耦合,数据类型:<class 'backtrader.metabase.LinesCoupler> ,是一个新的数据类型 !
self.couplesma也应该是<class 'backtrader.metabase.LinesCoupler>

  • 没有触发判断语句
    按说明补齐长度,应该能够进行比较,有是1的值。

  • 输出结果,报错,超出:IndexError: array index out of range

日线的bar远远大于周线的bar,做SMA ,都用5个周期做,效果一样,还是应该长度不一致。

改日再看看,是数据问题,还是没有理解 line耦合的概念

15. 自然构造 操作符

第一步:用操作符创建对象

在Indicators(指标)和strategy(策略)的初始化阶段init函数,通过操作符创建对象 。

class MyStrategy7(bt.Strategy):

    def __init__(self):

        sma = btind.SimpleMovingAverage(self.data, period=20)

        close_over_sma = self.data.close > sma
        sma_dist_to_high = self.data.high - sma

        sma_dist_small = sma_dist_to_high < 3.5

        # Unfortunately "and" cannot be overridden in Python being
        # a language construct and not an operator and thus a
        # function has to be provided by the platform to emulate it

        sell_sig = bt.And(close_over_sma, sma_dist_small)
        print('sell_sig',type(sell_sig))
        print('close_over_sma',type(close_over_sma),'sma_dist_small',type(sma_dist_small))

cerebro = declare_cerebar()

# 加入策略
cerebro.addstrategy(MyStrategy7)            
cerebro.run()
        

结果:

sell_sig <class 'backtrader.functions.And'>
close_over_sma <class 'backtrader.linebuffer.LinesOperation'> sma_dist_small <class 'backtrader.linebuffer.LinesOperation'>

还有<class ‘backtrader.functions.And’>数据类型!

第二步:用操作符创建对象

if self.sma > 30.0: 比较的是self.sma[0] to 30.0 (第一个Line 的当前值)
if self.sma > self.data.close: 比较 self.sma[0] 和self.data.close[0]。

class MyStrategy7(bt.Strategy):

    def __init__(self):

        self.sma = sma = btind.SimpleMovingAverage(self.data0, period=20)

        close_over_sma = self.data0.close > sma
        
        self.sma_dist_to_high = self.data0.high - sma

        sma_dist_small = self.sma_dist_to_high < 3.5

        print(type(sma),type(close_over_sma))
        print(type(self.sma_dist_to_high),type(self.data0.high))
        
        # Unfortunately "and" cannot be overridden in Python being
        # a language construct and not an operator and thus a
        # function has to be provided by the platform to emulate it

        self.sell_sig = bt.And(close_over_sma, sma_dist_small)

    def next(self):

        # Although this does not seem like an "operator" it actually is
        # in the sense that the object is being tested for a True/False
        # response

        if self.sma > 30.0:
            print('sma is greater than 30.0!',self.sma[0])

        if self.sma > self.data.close:
            print('sma is above the close price!',self.sma,self.data.close,self.data.close[0])

        if self.sell_sig:  # if sell_sig == True: would also be valid
            print('sell sig is True!',self.sell_sig[0])
        else:
            print('sell sig is False!',self.sell_sig[0])

        if self.sma_dist_to_high > 5.0:
            print('distance from sma to hig is greater than 5.0!',self.sma_dist_to_high[0])

self.sma_dist_to_high = self.data0.high - sma

说明:
LineBuffer 类型 减 SimpleMovingAverage 类型 结果是 LinesOperation 类型,也是数值!
有点迷惑 ?!

self.sma_dist_to_high <class ‘backtrader.linebuffer.LinesOperation’>
self.data0.high <class ‘backtrader.linebuffer.LineBuffer’>
sma <class ‘backtrader.indicators.sma.SimpleMovingAverage’>

结果:

<class 'backtrader.indicators.sma.SimpleMovingAverage'> <class 'backtrader.linebuffer.LinesOperation'>
<class 'backtrader.linebuffer.LinesOperation'> <class 'backtrader.linebuffer.LineBuffer'>
sma is greater than 30.0! 52.5345
sell sig is False! 0.0
distance from sma to hig is greater than 5.0! 6.045499999999997
sma is greater than 30.0! 52.9015
sell sig is False! 0.0
... ...

不可重写的操作符/函数

如下操作符/函数以及对应重写的函数。

操作符:

  • and -> And
  • or -> Or

逻辑控制:

  • if -> If

函数:

  • any -> Any
  • all -> All
  • cmp -> Cmp
  • max -> Max
  • min -> Min
  • sum -> Sum(Sum实际上是使用math.fsum作为底层操作,因为backtrader应用中需要处理大量浮点数据,而普通的sum函数精度会有影响。)
  • reduce -> Reduce

操作符/函数针对迭代器运行。迭代器中的元素可以是常规的Python数值类型(int、float等),也可以是Lines的对象。
bt.And示例:

class MyStrategy(bt.Strategy):

    def __init__(self):

        sma1 = btind.SMA(self.data.close, period=15)
        self.buysig = bt.And(sma1 > self.data.close, sma1 > self.data.high)

    def next(self):
        if self.buysig[0]:
            pass  # do something here

bt.If示例:

class MyStrategy(bt.Strategy):

    def __init__(self):

        sma1 = btind.SMA(self.data.close, period=15)
        high_or_low = bt.If(sma1 > self.data.close, self.data.low, self.data.high)
        sma2 = btind.SMA(high_or_low, period=15)

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>