Backtrader 文档学习-Strategy(中)

Backtrader 文档学习-Strategy(中)

Strategy是BT的核心模块,需要慢慢摸索内部设计的流程,方法的用途,使用场景,还要一些常用的状态值。
本章主要介绍关于strategy的生存周期,notify_order方法,notify_trade方法,通过代码结合参数变化,理解订单消息和交易消息的使用情况,应用场景。

需要时间探索,同时可以参考源码,理解具体的参数和方法。

Slow is stable, stable is fast!

1. 关于生存期测试

class Test_Strategy(bt.Strategy):  
    # 策略通用初始参数
    params = (
        ('maperiod1', 5),
        ('maperiod2', 20),
        ('printlog', True),  # 写入日志标志
        ('logfilename', 'Test_Strategy.log'), # 日志文件名
        ('counter', 0), # 计数器
    )
    
    def __init__(self):  
        
        #Open, High, Low, Close, Volume
        
        self.dataopen = self.datas[0].open
        self.datahigh = self.datas[0].high
        self.datalow = self.datas[0].low
        self.dataclose = self.datas[0].close  
        self.datavol = self.datas[0].volume
        
        # 5 20 日均线
        self.sma5 = bt.indicators.SimpleMovingAverage(self.dataclose, period=self.params.maperiod1)  
        self.sma20 = bt.indicators.SimpleMovingAverage(self.dataclose, period=self.params.maperiod2)  

    # doprint 打印日志标志
    def log(self, txt, dt=None, doprint=False):
        ''' Logging function fot this strategy'''
        if self.params.printlog or doprint:
            dt = dt or self.datas[0].datetime.date(0)
            #print('%s, %s' % (dt.isoformat(), txt))
            with open(self.params.logfilename, 'a') as file:
                file.write('%s, %s' % (dt.isoformat(), txt))
                file.write('\n')

    def start(self):    
        # 从0 开始
        #self.params.counter += 1
        self.log('Test_Strategy start %s' % self.params.counter, doprint=True)
    
    def prenext(self):
        self.params.counter += 1
        self.log('Test_Strategy prenext  %s' % self.params.counter, doprint=True)

    def nextstart(self):
        self.params.counter += 1
        self.log('Test_Strategy nextstart %s' % self.params.counter, doprint=True)  
                      
    def next(self):  
        # 最简单的策略,观察各个属性和方法
        if self.position:  
            self.close()  
        else:  
            if self.sma5 > self.sma20:  
                self.buy()  
            else:  
                self.sell()  
        
        self.params.counter += 1        
        self.log('Test_Strategy next %s' % self.params.counter, doprint=True)
        
        
    def stop(self):
        self.params.counter += 1        
        self.log('Test_Strategy stop  %s' % self.params.counter, doprint=True)
        self.log('(MA Period %2d) Ending Value %.2f' %
                 (self.params.maperiod1, self.broker.getvalue()), doprint=True)
        
        #最后打印所有属性和方法
        #self.log(dir(self), doprint=True)
        
if __name__ == '__main__':
    
    # delete log file
    log_file = './Test_Strategy.log'
    delete_file(log_file)
    
    # 创建Cerebro引擎  导入2019-1-1 到 2019-3-31 三个月数据
    cerebro = declare_cerebar()

    cerebro.addstrategy(Test_Strategy)  
    cerebro.run()
    

说明:

  • start()时,数据是日期最末一天。3月29日。30 31日非交易日。

2019-03-29, Test_Strategy start 0

  • 2个周期,5天和20天,以最长20天的周期为准,执行prenext方法。

2019-01-28, Test_Strategy prenext 19

  • nextstart() ,在最长周期20天触发执行

2019-01-29, Test_Strategy nextstart 20

  • stop(),停止在日期的最末一天,3月29日。

2019-03-29, Test_Strategy stop 59

输出结果:

# cat Test_Strategy.log 
2019-03-29, Test_Strategy start 0
2019-01-02, Test_Strategy prenext  1
2019-01-03, Test_Strategy prenext  2
2019-01-04, Test_Strategy prenext  3
2019-01-07, Test_Strategy prenext  4
2019-01-08, Test_Strategy prenext  5
2019-01-09, Test_Strategy prenext  6
2019-01-10, Test_Strategy prenext  7
2019-01-11, Test_Strategy prenext  8
2019-01-14, Test_Strategy prenext  9
2019-01-15, Test_Strategy prenext  10
2019-01-16, Test_Strategy prenext  11
2019-01-17, Test_Strategy prenext  12
2019-01-18, Test_Strategy prenext  13
2019-01-21, Test_Strategy prenext  14
2019-01-22, Test_Strategy prenext  15
2019-01-23, Test_Strategy prenext  16
2019-01-24, Test_Strategy prenext  17
2019-01-25, Test_Strategy prenext  18
2019-01-28, Test_Strategy prenext  19
2019-01-29, Test_Strategy nextstart 20
2019-01-30, Test_Strategy next 21
2019-01-31, Test_Strategy next 22
2019-02-01, Test_Strategy next 23
2019-02-11, Test_Strategy next 24
2019-02-12, Test_Strategy next 25
2019-02-13, Test_Strategy next 26
2019-02-14, Test_Strategy next 27
2019-02-15, Test_Strategy next 28
2019-02-18, Test_Strategy next 29
2019-02-19, Test_Strategy next 30
2019-02-20, Test_Strategy next 31
2019-02-21, Test_Strategy next 32
2019-02-22, Test_Strategy next 33
2019-02-25, Test_Strategy next 34
2019-02-26, Test_Strategy next 35
2019-02-27, Test_Strategy next 36
2019-02-28, Test_Strategy next 37
2019-03-01, Test_Strategy next 38
2019-03-04, Test_Strategy next 39
2019-03-05, Test_Strategy next 40
2019-03-06, Test_Strategy next 41
2019-03-07, Test_Strategy next 42
2019-03-08, Test_Strategy next 43
2019-03-11, Test_Strategy next 44
2019-03-12, Test_Strategy next 45
2019-03-13, Test_Strategy next 46
2019-03-14, Test_Strategy next 47
2019-03-15, Test_Strategy next 48
2019-03-18, Test_Strategy next 49
2019-03-19, Test_Strategy next 50
2019-03-20, Test_Strategy next 51
2019-03-21, Test_Strategy next 52
2019-03-22, Test_Strategy next 53
2019-03-25, Test_Strategy next 54
2019-03-26, Test_Strategy next 55
2019-03-27, Test_Strategy next 56
2019-03-28, Test_Strategy next 57
2019-03-29, Test_Strategy next 58
2019-03-29, Test_Strategy stop  59
2019-03-29, (MA Period  5) Ending Value 10020.11

2. notify_order

获得订单状态变化的通知,只要next触发了买卖操作,就会有通知订单消息,通过订单状态,可以进行相应操作处理:

Status:[‘Created’, ‘Submitted’, ‘Accepted’, ‘Partial’, ‘Completed’, ‘Canceled’, ‘Expired’, ‘Margin’, ‘Rejected’]

# 测试 notify_order #notify_trade notify_cashvalue notify_fund notify_store 方法特点
class Test_Strategy(bt.Strategy):  
    # 策略通用初始参数
    params = (
        ('maperiod1', 5),
        ('maperiod2', 20),
        ('printlog', True),  # 写入日志标志
        ('logfilename', 'Test_Strategy.log'), # 日志文件名
        ('counter', 0), # 计数器
    )
    
    def __init__(self):  
        
        #Open, High, Low, Close, Volume
        
        self.dataopen = self.datas[0].open
        self.datahigh = self.datas[0].high
        self.datalow = self.datas[0].low
        self.dataclose = self.datas[0].close  
        self.datavol = self.datas[0].volume
        
        # 5 20 日均线
        self.sma5 = bt.indicators.SimpleMovingAverage(self.dataclose, period=self.params.maperiod1)  
        self.sma20 = bt.indicators.SimpleMovingAverage(self.dataclose, period=self.params.maperiod2)  

    # doprint 打印日志标志
    def log(self, txt, dt=None, doprint=False):
        ''' Logging function for this strategy'''
        if self.params.printlog or doprint:
            dt = dt or self.datas[0].datetime.date(0)
            #print('%s, %s' % (dt.isoformat(), txt))
            with open(self.params.logfilename, 'a') as file:
                file.write('%s, %s' % (dt.isoformat(), txt))
                file.write('\n')

    def start(self):    
        # 从0 开始
        #self.params.counter += 1
        #self.log('Test_Strategy start %s' % self.params.counter, doprint=True)
        pass
    
    def nextstart(self):
        self.params.counter += 1
        #self.log('Test_Strategy nextstart %s' % self.params.counter, doprint=True)
        
    def prenext(self):
        self.params.counter += 1
        #self.log('Test_Strategy prenext  %s' % self.params.counter, doprint=True)
                
    def next(self):  
        # 最简单的策略,观察各个属性和方法
        if self.sma5 > self.sma20:  
            self.order = self.buy()  
        else:  
            self.order = self.sell()  
        
        self.params.counter += 1        
        
        self.order = None
        
        #self.log('Test_Strategy next %s' % self.params.counter, doprint=True)
            
    #获得订单状态变化的通知    
    def notify_order(self, order):
        #self.log('order:', doprint=True)
        #Status:['Created', 'Submitted', 'Accepted', 'Partial', 'Completed', 'Canceled', 'Expired', 'Margin', 'Rejected']
        
        if order.status == order.Submitted :
            self.log("Order Submitted!", doprint=True)

        elif order.status == order.Created :
            self.log("Order Created!", doprint=True)
            
        elif order.status == order.Accepted :
            self.log("Order Accepted!", doprint=True)
            self.log("Order position size:%.2f,avg price: %.2f,open:%.2f,close:%.2f,high:%.2f,low:%.2f" 
             % (self.getposition().size, self.getposition().price,self.dataopen[0],self.dataclose[0],self.datahigh[0],self.datalow[0]),doprint=True)
            
        elif order.status == order.Completed :
            if order.isbuy():
                self.log(
                    "BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f"
                    % (order.executed.price, order.executed.value, order.executed.comm), doprint=True)
                
            else:
                self.log(
                    "SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f"
                    % (order.executed.price, order.executed.value, order.executed.comm), doprint=True)
            self.bar_executed = len(self)
            self.log("Order executed bar: %.0f, position: %.2f" % (len(self), order.position), doprint=True)
            
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log("Order Canceled/Margin/Rejected", doprint=True)
            
        #self.order = None
       
        
    def notify_trade(self,trade) :
        pass
            
    def notify_fund(self,cash,value,fundvalue,shares):
        pass
    
    def notify_store(self ,msg):
        pass
    
    def notify_cashvalue(self,cash, value):
        #self.log('cash:', doprint=True)
        #self.log(dir(cash), doprint=True)
        
        #self.log('value:', doprint=True)
        #self.log(dir(value), doprint=True)
        pass
        
    
    def stop(self):
        self.params.counter += 1        
        #self.log('Test_Strategy stop  %s' % self.params.counter, doprint=True)
        self.log('(MA Period %2d) Ending Value %.2f' %
                 (self.params.maperiod1, self.broker.getvalue()), doprint=True)
        
            
if __name__ == '__main__':
    
    # delete log file
    log_file = './Test_Strategy.log'
    delete_file(log_file)
    
    # 创建Cerebro引擎  导入2019-1-1 到 2019-3-31 三个月数据
    cerebro = declare_cerebar()
    
    # Set our desired cash start
    cerebro.broker.setcash(100000.0)

    # Set the commission - 0.1% ... divide by 100 to remove the %
    # 按万一的佣金 ,买卖操作都要扣除
    cerebro.broker.setcommission(commission=0.0001)
    
    # Add a FixedSize sizer according to the stake
    cerebro.addsizer(bt.sizers.FixedSize, stake=10)

    cerebro.addstrategy(Test_Strategy)  

    cerebro.run()
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    
    cerebro.plot(iplot=False)

执行结果:
用的数据是2020-01-01到2020-03-31时间周期:

# cat Test_Strategy.log 
2020-02-10, Order Submitted!
2020-02-10, Order Accepted!
2020-02-10, Order position size:-10.00,avg price: 119.50,open:119.50,close:121.13,high:122.33,low:118.58
2020-02-10, SELL EXECUTED, Price: 119.50, Cost: -1195.00, Comm 0.12
2020-02-10, Order executed bar: 22, position: 0.00
2020-02-11, Order Submitted!
2020-02-11, Order Accepted!
2020-02-11, Order position size:-20.00,avg price: 120.33,open:121.15,close:124.79,high:125.50,low:121.15
2020-02-11, SELL EXECUTED, Price: 121.15, Cost: -1211.50, Comm 0.12
2020-02-11, Order executed bar: 23, position: 0.00
2020-02-12, Order Submitted!
2020-02-12, Order Accepted!
2020-02-12, Order position size:-30.00,avg price: 121.72,open:124.50,close:124.49,high:125.25,low:122.77
2020-02-12, SELL EXECUTED, Price: 124.50, Cost: -1245.00, Comm 0.12
2020-02-12, Order executed bar: 24, position: 0.00
2020-02-13, Order Submitted!
2020-02-13, Order Accepted!
2020-02-13, Order position size:-40.00,avg price: 122.51,open:124.88,close:124.14,high:126.66,low:123.37
2020-02-13, SELL EXECUTED, Price: 124.88, Cost: -1248.80, Comm 0.12
2020-02-13, Order executed bar: 25, position: 0.00
2020-02-14, Order Submitted!
2020-02-14, Order Accepted!
2020-02-14, Order position size:-50.00,avg price: 122.85,open:124.20,close:123.43,high:125.88,low:122.45
2020-02-14, SELL EXECUTED, Price: 124.20, Cost: -1242.00, Comm 0.12
2020-02-14, Order executed bar: 26, position: 0.00
2020-02-17, Order Submitted!
2020-02-17, Order Accepted!
2020-02-17, Order position size:-60.00,avg price: 122.90,open:123.18,close:124.30,high:124.30,low:122.58
2020-02-17, SELL EXECUTED, Price: 123.18, Cost: -1231.80, Comm 0.12
2020-02-17, Order executed bar: 27, position: 0.00
2020-02-18, Order Submitted!
2020-02-18, Order Accepted!
2020-02-18, Order position size:-70.00,avg price: 123.03,open:123.80,close:123.39,high:124.30,low:123.01
2020-02-18, SELL EXECUTED, Price: 123.80, Cost: -1238.00, Comm 0.12
2020-02-18, Order executed bar: 28, position: 0.00
2020-02-19, Order Submitted!
2020-02-19, Order Accepted!
2020-02-19, Order position size:-80.00,avg price: 123.12,open:123.78,close:126.00,high:127.18,low:122.68
2020-02-19, SELL EXECUTED, Price: 123.78, Cost: -1237.80, Comm 0.12
2020-02-19, Order executed bar: 29, position: 0.00
2020-02-20, Order Submitted!
2020-02-20, Order Accepted!
2020-02-20, Order position size:-90.00,avg price: 123.55,open:127.00,close:130.15,high:130.58,low:126.89
2020-02-20, SELL EXECUTED, Price: 127.00, Cost: -1270.00, Comm 0.13
2020-02-20, Order executed bar: 30, position: 0.00
2020-02-21, Order Submitted!
2020-02-21, Order Accepted!
2020-02-21, Order position size:-100.00,avg price: 124.20,open:130.00,close:130.00,high:131.20,low:129.02
2020-02-21, SELL EXECUTED, Price: 130.00, Cost: -1300.00, Comm 0.13
2020-02-21, Order executed bar: 31, position: 0.00
2020-02-24, Order Submitted!
2020-02-24, Order Accepted!
2020-02-24, Order position size:-90.00,avg price: 124.20,open:129.40,close:127.10,high:129.40,low:126.70
2020-02-24, BUY EXECUTED, Price: 129.40, Cost: -1241.99, Comm 0.13
2020-02-24, Order executed bar: 32, position: 0.00
2020-02-25, Order Submitted!
2020-02-25, Order Accepted!
2020-02-25, Order position size:-80.00,avg price: 124.20,open:124.80,close:125.30,high:127.00,low:123.86
2020-02-25, BUY EXECUTED, Price: 124.80, Cost: -1241.99, Comm 0.12
2020-02-25, Order executed bar: 33, position: 0.00
2020-02-26, Order Submitted!
2020-02-26, Order Accepted!
2020-02-26, Order position size:-70.00,avg price: 124.20,open:123.83,close:124.10,high:126.42,low:123.00
2020-02-26, BUY EXECUTED, Price: 123.83, Cost: -1241.99, Comm 0.12
2020-02-26, Order executed bar: 34, position: 0.00
2020-02-27, Order Submitted!
2020-02-27, Order Accepted!
2020-02-27, Order position size:-60.00,avg price: 124.20,open:124.80,close:126.30,high:127.75,low:124.50
2020-02-27, BUY EXECUTED, Price: 124.80, Cost: -1241.99, Comm 0.12
2020-02-27, Order executed bar: 35, position: 0.00
2020-02-28, Order Submitted!
2020-02-28, Order Accepted!
2020-02-28, Order position size:-50.00,avg price: 124.20,open:124.00,close:120.60,high:124.03,low:119.00
2020-02-28, BUY EXECUTED, Price: 124.00, Cost: -1241.99, Comm 0.12
2020-02-28, Order executed bar: 36, position: 0.00
2020-03-02, Order Submitted!
2020-03-02, Order Accepted!
2020-03-02, Order position size:-40.00,avg price: 124.20,open:120.10,close:122.65,high:123.60,low:119.60
2020-03-02, BUY EXECUTED, Price: 120.10, Cost: -1241.99, Comm 0.12
2020-03-02, Order executed bar: 37, position: 0.00
2020-03-03, Order Submitted!
2020-03-03, Order Accepted!
2020-03-03, Order position size:-30.00,avg price: 124.20,open:123.70,close:124.37,high:125.88,low:123.70
2020-03-03, BUY EXECUTED, Price: 123.70, Cost: -1241.99, Comm 0.12
2020-03-03, Order executed bar: 38, position: 0.00
2020-03-04, Order Submitted!
2020-03-04, Order Accepted!
2020-03-04, Order position size:-40.00,avg price: 124.15,open:124.00,close:125.27,high:125.93,low:124.00
2020-03-04, SELL EXECUTED, Price: 124.00, Cost: -1240.00, Comm 0.12
2020-03-04, Order executed bar: 39, position: 0.00
2020-03-05, Order Submitted!
2020-03-05, Order Accepted!
2020-03-05, Order position size:-50.00,avg price: 124.63,open:126.55,close:133.20,high:134.88,low:126.00
2020-03-05, SELL EXECUTED, Price: 126.55, Cost: -1265.50, Comm 0.13
2020-03-05, Order executed bar: 40, position: 0.00
2020-03-06, Order Submitted!
2020-03-06, Order Accepted!
2020-03-06, Order position size:-40.00,avg price: 124.63,open:132.00,close:130.07,high:134.88,low:130.00
2020-03-06, BUY EXECUTED, Price: 132.00, Cost: -1246.29, Comm 0.13
2020-03-06, Order executed bar: 41, position: 0.00
2020-03-09, Order Submitted!
2020-03-09, Order Accepted!
2020-03-09, Order position size:-30.00,avg price: 124.63,open:128.00,close:126.30,high:128.25,low:126.04
2020-03-09, BUY EXECUTED, Price: 128.00, Cost: -1246.29, Comm 0.13
2020-03-09, Order executed bar: 42, position: 0.00
2020-03-10, Order Submitted!
2020-03-10, Order Accepted!
2020-03-10, Order position size:-20.00,avg price: 124.63,open:126.30,close:130.72,high:131.63,low:126.20
2020-03-10, BUY EXECUTED, Price: 126.30, Cost: -1246.29, Comm 0.13
2020-03-10, Order executed bar: 43, position: 0.00
2020-03-11, Order Submitted!
2020-03-11, Order Accepted!
2020-03-11, Order position size:-10.00,avg price: 124.63,open:132.50,close:129.90,high:132.80,low:129.50
2020-03-11, BUY EXECUTED, Price: 132.50, Cost: -1246.29, Comm 0.13
2020-03-11, Order executed bar: 44, position: 0.00
2020-03-12, Order Submitted!
2020-03-12, Order Accepted!
2020-03-12, Order position size:0.00,avg price: 0.00,open:126.88,close:126.04,high:128.11,low:125.86
2020-03-12, BUY EXECUTED, Price: 126.88, Cost: -1246.29, Comm 0.13
2020-03-12, Order executed bar: 45, position: 0.00
2020-03-13, Order Submitted!
2020-03-13, Order Accepted!
2020-03-13, Order position size:10.00,avg price: 120.00,open:120.00,close:122.60,high:125.22,low:118.88
2020-03-13, BUY EXECUTED, Price: 120.00, Cost: 1200.00, Comm 0.12
2020-03-13, Order executed bar: 46, position: 0.00
2020-03-16, Order Submitted!
2020-03-16, Order Accepted!
2020-03-16, Order position size:20.00,avg price: 120.70,open:121.40,close:115.50,high:122.40,low:114.41
2020-03-16, BUY EXECUTED, Price: 121.40, Cost: 1214.00, Comm 0.12
2020-03-16, Order executed bar: 47, position: 0.00
2020-03-17, Order Submitted!
2020-03-17, Order Accepted!
2020-03-17, Order position size:10.00,avg price: 120.70,open:114.50,close:112.36,high:117.58,low:108.58
2020-03-17, SELL EXECUTED, Price: 114.50, Cost: 1207.00, Comm 0.11
2020-03-17, Order executed bar: 48, position: 0.00
2020-03-18, Order Submitted!
2020-03-18, Order Accepted!
2020-03-18, Order position size:0.00,avg price: 0.00,open:113.20,close:107.59,high:115.97,low:106.69
2020-03-18, SELL EXECUTED, Price: 113.20, Cost: 1207.00, Comm 0.11
2020-03-18, Order executed bar: 49, position: 0.00
2020-03-19, Order Submitted!
2020-03-19, Order Accepted!
2020-03-19, Order position size:-10.00,avg price: 105.80,open:105.80,close:102.11,high:107.00,low:98.63
2020-03-19, SELL EXECUTED, Price: 105.80, Cost: -1058.00, Comm 0.11
2020-03-19, Order executed bar: 50, position: 0.00
2020-03-20, Order Submitted!
2020-03-20, Order Accepted!
2020-03-20, Order position size:-20.00,avg price: 105.06,open:104.32,close:108.51,high:109.66,low:104.21
2020-03-20, SELL EXECUTED, Price: 104.32, Cost: -1043.20, Comm 0.10
2020-03-20, Order executed bar: 51, position: 0.00
2020-03-23, Order Submitted!
2020-03-23, Order Accepted!
2020-03-23, Order position size:-30.00,avg price: 104.71,open:104.00,close:104.51,high:106.50,low:103.04
2020-03-23, SELL EXECUTED, Price: 104.00, Cost: -1040.00, Comm 0.10
2020-03-23, Order executed bar: 52, position: 0.00
2020-03-24, Order Submitted!
2020-03-24, Order Accepted!
2020-03-24, Order position size:-40.00,avg price: 105.30,open:107.08,close:109.05,high:109.89,low:106.04
2020-03-24, SELL EXECUTED, Price: 107.08, Cost: -1070.80, Comm 0.11
2020-03-24, Order executed bar: 53, position: 0.00
2020-03-25, Order Submitted!
2020-03-25, Order Accepted!
2020-03-25, Order position size:-50.00,avg price: 106.86,open:113.10,close:114.00,high:114.96,low:111.84
2020-03-25, SELL EXECUTED, Price: 113.10, Cost: -1131.00, Comm 0.11
2020-03-25, Order executed bar: 54, position: 0.00
2020-03-26, Order Submitted!
2020-03-26, Order Accepted!
2020-03-26, Order position size:-60.00,avg price: 107.88,open:112.98,close:113.89,high:116.30,low:111.90
2020-03-26, SELL EXECUTED, Price: 112.98, Cost: -1129.80, Comm 0.11
2020-03-26, Order executed bar: 55, position: 0.00
2020-03-27, Order Submitted!
2020-03-27, Order Accepted!
2020-03-27, Order position size:-70.00,avg price: 108.98,open:115.60,close:115.81,high:117.80,low:115.60
2020-03-27, SELL EXECUTED, Price: 115.60, Cost: -1156.00, Comm 0.12
2020-03-27, Order executed bar: 56, position: 0.00
2020-03-30, Order Submitted!
2020-03-30, Order Accepted!
2020-03-30, Order position size:-80.00,avg price: 109.46,open:112.82,close:113.23,high:114.02,low:111.70
2020-03-30, SELL EXECUTED, Price: 112.82, Cost: -1128.20, Comm 0.11
2020-03-30, Order executed bar: 57, position: 0.00
2020-03-31, Order Submitted!
2020-03-31, Order Accepted!
2020-03-31, Order position size:-90.00,avg price: 110.08,open:115.00,close:115.20,high:116.59,low:114.06
2020-03-31, SELL EXECUTED, Price: 115.00, Cost: -1150.00, Comm 0.12
2020-03-31, Order executed bar: 58, position: 0.00
2020-03-31, (MA Period  5) Ending Value 99159.83

执行结果说明理解:

(1)next执行

由于next中执行条件很简单,所以超过20日周期后,每天都有买卖操作被触发,能够观察变化。

self.sma5 > self.sma20

(2)通知触发顺序

next触发订单通知,先是Submitted状态,然后是Accepted ,成交是Completed状态,其实在实际情况中,还有部分成交,也有相应的状态支持,但是没有想出来如何触发。
在Completed状态中,判断是什么操作,是买还是卖 。

2020-02-10, Order Submitted!
2020-02-10, Order Accepted!
2020-02-10, Order position size:-10.00,avg price: 119.50,open:119.50,close:121.13,high:122.33,low:118.58
2020-02-10, SELL EXECUTED, Price: 119.50, Cost: -1195.00, Comm 0.12
2020-02-10, Order executed bar: 22, position: 0.00
2020-02-11, Order Submitted!
2020-02-11, Order Accepted!
2020-02-11, Order position size:-20.00,avg price: 120.33,open:121.15,close:124.79,high:125.50,low:121.15
2020-02-11, SELL EXECUTED, Price: 121.15, Cost: -1211.50, Comm 0.12
2020-02-11, Order executed bar: 23, position: 0.00
(3)默认能够卖空

2020-02-10, Order position size:-10.00,avg price: 119.50,open:119.50,close:121.13,high:122.33,low:118.58

第一次就是卖出操作,仓位直接就是-10 。

(4)获取仓位

是当前的仓位数量,可以看到随时间变化,仓位在增减。

self.getposition().size

(5)获取价格

被这个价格困扰了好一会!!
所以把所有的OLHC都打印出来对比。

self.getposition().price

还是用下面的数据说明
第一次的price是119.50 ,就是当天的open 。
第二次的price是120.33,和哪一天 的open都对不上,实际上是
(119.50+121.15)/ 2 = 120.325 四舍五入120.33 。就是平均的成本价。但是没有考虑comm佣金 。

2020-02-10, Order Submitted!
2020-02-10, Order Accepted!
2020-02-10, Order position size:-10.00,avg price: 119.50,open:119.50,close:121.13,high:122.33,low:118.58
2020-02-10, SELL EXECUTED, Price: 119.50, Cost: -1195.00, Comm 0.12
2020-02-10, Order executed bar: 22, position: 0.00
2020-02-11, Order Submitted!
2020-02-11, Order Accepted!
2020-02-11, Order position size:-20.00,avg price: 120.33,open:121.15,close:124.79,high:125.50,low:121.15
2020-02-11, SELL EXECUTED, Price: 121.15, Cost: -1211.50, Comm 0.12
2020-02-11, Order executed bar: 23, position: 0.00
(6)没有Created状态

个人理解应该是next触发买卖操作,就会有notify_order状态的变化,第一个状态应该是created,但是实际上程序并没有捕捉到这个状态。

(7)order的属性和方法
order:
['Accepted', 'Buy', 'Canceled', 'Cancelled', 'Close', 'Completed', 'Created', 'DAY', 'ExecType', 'ExecTypes', 'Expired', 'Historical', 'Limit', 'Margin', 'Market', 'OrdTypes', 'Partial', 'Rejected', 'Sell', 'Status', 'Stop', 'StopLimit', 'StopTrail', 'StopTrailLimit', 'Submitted', 'T_Close', 'T_Date', 'T_Day', 'T_None', 'V_None', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setattribute__', '__sizeof__', '__slotnames__', '__str__', '__subclasshook__', '__weakref__', '_active', '_getplimit', '_limitoffset', '_plimit', '_setplimit', 'accept', 'activate', 'active', 'addcomminfo', 'addinfo', 'alive', 'broker', 'brokerstatus', 'cancel', 'clone', 'comminfo', 'completed', 'created', 'dteos', 'exectype', 'execute', 'executed', 'expire', 'frompackages', 'getordername', 'getstatusname', 'info', 'isbuy', 'issell', 'margin', 'ordtype', 'ordtypename', 'p', 'packages', 'pannotated', 'params', 'partial', 'plen', 'plimit', 'position', 'ref', 'refbasis', 'reject', 'setposition', 'status', 'submit', 'trailadjust', 'triggered']

通过上面可以看到,还要很多的属性或方法,可以尝试。

(8)self.getposition()的属性和方法

主要用了两个price和size ,参见程序。

self.getposition()
 ['__bool__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'adjbase', 'clone', 'datetime', 'fix', 'price', 'price_orig', 'pseudoupdate', 'set', 'size', 'upclosed', 'update', 'updt', 'upopened']

3.notify_trade

(1)主要属性方法

看源码trade.py

Member Attributes:

      - ``ref``: unique trade identifier
      - ``status`` (``int``): one of Created, Open, Closed
      - ``tradeid``: grouping tradeid passed to orders during creation
        The default in orders is 0
      - ``size`` (``int``): current size of the trade
      - ``price`` (``float``): current price of the trade
      - ``value`` (``float``): current value of the trade
      - ``commission`` (``float``): current accumulated commission
      - ``pnl`` (``float``): current profit and loss of the trade (gross pnl)
      - ``pnlcomm`` (``float``): current profit and loss of the trade minus
        commission (net pnl)
      - ``isclosed`` (``bool``): records if the last update closed (set size to
        null the trade
      - ``isopen`` (``bool``): records if any update has opened the trade
      - ``justopened`` (``bool``): if the trade was just opened
      - ``baropen`` (``int``): bar in which this trade was opened

      - ``dtopen`` (``float``): float coded datetime in which the trade was
        opened

        - Use method ``open_datetime`` to get a Python datetime.datetime
          or use the platform provided ``num2date`` method

      - ``barclose`` (``int``): bar in which this trade was closed

      - ``dtclose`` (``float``): float coded datetime in which the trade was
        closed

        - Use method ``close_datetime`` to get a Python datetime.datetime
          or use the platform provided ``num2date`` method

      - ``barlen`` (``int``): number of bars this trade was open
      - ``historyon`` (``bool``): whether history has to be recorded
      - ``history`` (``list``): holds a list updated with each "update" event
        containing the resulting status and parameters used in the update

        The first entry in the history is the Opening Event
        The last entry in the history is the Closing Event

说明:

trade.status_names:[‘Created’, ‘Open’, ‘Closed’]
trade.status:1

定义了状态值,trade的状态分别是int ,0 1 2,对应到[‘Created’, ‘Open’, ‘Closed’] 。

(2)测试程序

测试trade的属性、方法。
参数notify_trade_trigger 用于计算交易过程中触发的次数。
平仓、开仓的状态写日志,其他的打印输出。
trade与交易最相关的参数:
pnl毛利,净利pnlcomm=pnl - comm ,交易价格price,交易金额value :

    print('trade.pnl:'+str(trade.pnl))
    print('trade.pnlcomm:'+str(trade.pnlcomm))
    print('trade.price:'+str(trade.price))
    print('trade.value:'+str(trade.value))   
# 测试 #notify_trade 方法特点
class Test_Strategy(bt.Strategy):  
    # 策略通用初始参数
    params = (
        ('maperiod1', 5),
        ('maperiod2', 20),
        ('printlog', True),  # 写入日志标志
        ('logfilename', 'Test_Strategy.log'), # 日志文件名
        ('counter', 0), # 计数器
        ('notify_trade_trigger',0),  # notify_trade触发计数        
    )
    
    def __init__(self):  
        
        #Open, High, Low, Close, Volume
        
        self.dataopen = self.datas[0].open
        self.datahigh = self.datas[0].high
        self.datalow = self.datas[0].low
        self.dataclose = self.datas[0].close  
        self.datavol = self.datas[0].volume
        
        # 5 20 日均线
        self.sma5 = bt.indicators.SimpleMovingAverage(self.dataclose, period=self.params.maperiod1)  
        self.sma20 = bt.indicators.SimpleMovingAverage(self.dataclose, period=self.params.maperiod2)  

    # doprint 打印日志标志
    def log(self, txt, dt=None, doprint=False):
        ''' Logging function for this strategy'''
        if self.params.printlog or doprint:
            dt = dt or self.datas[0].datetime.date(0)
            #print('%s, %s' % (dt.isoformat(), txt))
            with open(self.params.logfilename, 'a') as file:
                file.write('%s, %s' % (dt.isoformat(), txt))
                file.write('\n')

    def start(self):    
        # 从0 开始
        #self.params.counter += 1
        #self.log('Test_Strategy start %s' % self.params.counter, doprint=True)
        pass
    
    def nextstart(self):
        self.params.counter += 1
        #self.log('Test_Strategy nextstart %s' % self.params.counter, doprint=True)
        
    def prenext(self):
        self.params.counter += 1
        #self.log('Test_Strategy prenext  %s' % self.params.counter, doprint=True)
                
    def next(self):  
        # 最简单的策略,观察各个属性和方法
        if self.sma5 > self.sma20:  
            self.order = self.buy()  
        else:  
            self.order = self.sell()  
        
        self.params.counter += 1        
        
        self.order = None
        
        #self.log('Test_Strategy next %s' % self.params.counter, doprint=True)
            
    #获得订单状态变化的通知    
    def notify_order(self, order):
        pass   
    
    #通知所有开仓/更新/平仓交易    
    def notify_trade(self,trade) : # ,historyon=True
        #trade.status_names:['Created', 'Open', 'Closed']
       
        # 触发一次notify_trade 的计数器
        self.params.notify_trade_trigger += 1
        print('self.params.notify_trade_trigger',self.params.notify_trade_trigger)
        
        # 平仓写日志
        if trade.isclosed:
            self.log("OPERATION isclosed PROFIT, GROSS %.2f, NET %.2f" % (trade.pnl, trade.pnlcomm))
        
        # 开仓写日志
        if trade.isopen:
            self.log("OPERATION isopen PROFIT, GROSS %.2f, NET %.2f" % (trade.pnl, trade.pnlcomm))
            
        #仓位显示
        position = self.getposition()  
        print('position:')  
        print(position)  
        
        # 交易信息
        print('trade information:')

        print('trade.Closed:'+str(trade.Closed))
        print('trade.Created:'+str(trade.Created))
        print('trade.Open:'+str(trade.Open))
        print('trade.barclose:'+str(trade.barclose))
        print('trade.barlen:'+str(trade.barlen))
        print('trade.baropen:'+str(trade.baropen))
        
        print('trade.commission:'+str(trade.commission))
        print('trade.data:'+str(trade.data[0]))
        
        if trade.dtclose == 0 :
            print('trade.dtclose:'+str(trade.dtclose))
        else:
            print('trade.dtclose:'+str(bt.num2date(trade.dtclose)))
        
        if trade.dtopen == 0 :
            print('trade.dtopen:'+str(trade.dtopen))
        else:        
            print('trade.dtopen:'+str(bt.num2date(trade.dtopen)))
        
        print('trade.isclosed:'+str(trade.isclosed))
        print('trade.isopen:'+str(trade.isopen))
        print('trade.status:'+str(trade.status))
        
        print('trade.long:'+str(trade.long))
        
        print('trade.pnl:'+str(trade.pnl))
        print('trade.pnlcomm:'+str(trade.pnlcomm))
        print('trade.price:'+str(trade.price))
        print('trade.value:'+str(trade.value))      
        
        print('trade.size:'+str(trade.size))
            
    def notify_fund(self,cash,value,fundvalue,shares):
        pass
    
    def notify_store(self ,msg):
        pass
    
    def notify_cashvalue(self,cash, value):
        #self.log('cash:', doprint=True)
        #self.log(dir(cash), doprint=True)
        
        #self.log('value:', doprint=True)
        #self.log(dir(value), doprint=True)
        pass
    
    def stop(self):
        self.params.counter += 1        
        print('self.params.counter:',self.params.counter)
        #self.log('Test_Strategy stop  %s' % self.params.counter, doprint=True)
        self.log('(MA Period %2d) Ending Value %.2f' %
                 (self.params.maperiod1, self.broker.getvalue()), doprint=True)
        
            
if __name__ == '__main__':
    
    # delete log file
    log_file = './Test_Strategy.log'
    delete_file(log_file)
    
    # 创建Cerebro引擎  导入2019-1-1 到 2019-3-31 三个月数据
    cerebro = declare_cerebar()
    
    # Set our desired cash start
    cerebro.broker.setcash(100000.0)

    # Set the commission - 0.1% ... divide by 100 to remove the %
    # 按万一的佣金 ,买卖操作都要扣除
    cerebro.broker.setcommission(commission=0.0001)
    
    # Add a FixedSize sizer according to the stake
    cerebro.addsizer(bt.sizers.FixedSize, stake=10)

    cerebro.addstrategy(Test_Strategy)  

    cerebro.run()
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    
    #cerebro.plot(iplot=False)

结果如下:

self.params.notify_trade_trigger 1
position:
--- Position Begin
- Size: -10
- Price: 119.5
- Price orig: 0.0
- Closed: 0
- Opened: -10
- Adjbase: 121.13
--- Position End
trade information:
trade.Closed:2
trade.Created:0
trade.Open:1
trade.barclose:0
trade.barlen:0
trade.baropen:22
trade.commission:0.11950000000000001
trade.data:121.13
trade.dtclose:0.0
trade.dtopen:2020-02-10 00:00:00
trade.isclosed:False
trade.isopen:True
trade.status:1
trade.long:False
trade.pnl:0.0
trade.pnlcomm:-0.11950000000000001
trade.price:119.5
trade.value:-1195.0
trade.size:-10
self.params.notify_trade_trigger 2
position:
--- Position Begin
- Size: 0
- Price: 0.0
- Price orig: 124.62939999999999
- Closed: 10
- Opened: 0
- Adjbase: 129.9
--- Position End
trade information:
trade.Closed:2
trade.Created:0
trade.Open:1
trade.barclose:45
trade.barlen:23
trade.baropen:22
trade.commission:3.0088500000000002
trade.data:126.04
trade.dtclose:2020-03-12 00:00:00
trade.dtopen:2020-02-10 00:00:00
trade.isclosed:True
trade.isopen:False
trade.status:2
trade.long:False
trade.pnl:-237.70000000000053
trade.pnlcomm:-240.70885000000052
trade.price:124.62939999999999
trade.value:0.0
trade.size:0
self.params.notify_trade_trigger 3
position:
--- Position Begin
- Size: 10
- Price: 120.0
- Price orig: 0.0
- Closed: 0
- Opened: 10
- Adjbase: 122.6
--- Position End
trade information:
trade.Closed:2
trade.Created:0
trade.Open:1
trade.barclose:0
trade.barlen:0
trade.baropen:46
trade.commission:0.12
trade.data:122.6
trade.dtclose:0.0
trade.dtopen:2020-03-13 00:00:00
trade.isclosed:False
trade.isopen:True
trade.status:1
trade.long:True
trade.pnl:0.0
trade.pnlcomm:-0.12
trade.price:120.0
trade.value:1200.0
trade.size:10
self.params.notify_trade_trigger 4
position:
--- Position Begin
- Size: 0
- Price: 0.0
- Price orig: 120.7
- Closed: -10
- Opened: 0
- Adjbase: 112.36
--- Position End
trade information:
trade.Closed:2
trade.Created:0
trade.Open:1
trade.barclose:49
trade.barlen:3
trade.baropen:46
trade.commission:0.4691
trade.data:107.59
trade.dtclose:2020-03-18 00:00:00
trade.dtopen:2020-03-13 00:00:00
trade.isclosed:True
trade.isopen:False
trade.status:2
trade.long:True
trade.pnl:-137.00000000000003
trade.pnlcomm:-137.46910000000003
trade.price:120.7
trade.value:0.0
trade.size:0
self.params.notify_trade_trigger 5
position:
--- Position Begin
- Size: -10
- Price: 105.8
- Price orig: 0.0
- Closed: 0
- Opened: -10
- Adjbase: 102.11
--- Position End
trade information:
trade.Closed:2
trade.Created:0
trade.Open:1
trade.barclose:0
trade.barlen:0
trade.baropen:50
trade.commission:0.1058
trade.data:102.11
trade.dtclose:0.0
trade.dtopen:2020-03-19 00:00:00
trade.isclosed:False
trade.isopen:True
trade.status:1
trade.long:False
trade.pnl:0.0
trade.pnlcomm:-0.1058
trade.price:105.8
trade.value:-1058.0
trade.size:-10
self.params.counter: 59
Final Portfolio Value: 99159.83
(3)barlen交易长度

barlen,在源码中定义计算:

#Update current trade length
self.barlen = len(self.data) - self.baropen

打印出结果:

trade.barclose:45
trade.barlen:23
trade.baropen:22

从开仓:22 bar,到平仓:45 bar ,整个交易的bar数量:45-22=23

(4)交易日期

dtclose /dtopen ,是需要用bt.num2date转换成正常日期。
执行过程中,可以从交易顺序中看出,dtopen一定有数值,但是dtclose不一定有数值,必须结束平仓,才有记录。

trade.dtclose:2020-03-18 00:00:00
trade.dtopen:2020-03-13 00:00:00
(5)交易数据trade.data

就是datafeed的数据,[0]是当前的close数值。

print(‘trade.data:’+str(trade.data[0]))

(6)仓位变化

交易过程中,随时观察仓位变化。

position:
--- Position Begin
- Size: -10
- Price: 105.8
- Price orig: 0.0
- Closed: 0
- Opened: -10
- Adjbase: 102.11
--- Position End
(7)输出日志

可以看到notify_trade被触发过5次,三次open,两次close 。
与self.params.notify_trade_trigger一致 。

self.params.notify_trade_trigger 5

# more Test_Strategy.log 
2020-02-10, OPERATION isopen PROFIT, GROSS 0.00, NET -0.12
2020-03-12, OPERATION isclosed PROFIT, GROSS -237.70, NET -240.71
2020-03-13, OPERATION isopen PROFIT, GROSS 0.00, NET -0.12
2020-03-18, OPERATION isclosed PROFIT, GROSS -137.00, NET -137.47
2020-03-19, OPERATION isopen PROFIT, GROSS 0.00, NET -0.11
2020-03-31, (MA Period  5) Ending Value 99159.83
(8)trade和order的区别

notice_order 和 notice_trade两个方法,写完两个使用操作,有一个疑问,实际中是如何区别两者用途呢?
其实,在两个方法中,都能看仓位,盈利,交易情况,区别是order记录更多一些,从20日周线后,每天都有order,最终的trade之有5条记录。两者之间的关联是什么?

仔细阅读输出order和trade的日志:
2020-02-10 ,order开始产生,开的空仓操作;trade也是在2020-02-10有开仓记录;
2020-03-12 trade第一次出现isclosed ,第二次触发。
此时的仓位是0 。

— Position Begin
-Size: 0

因此trade的周期就是从开仓开始,不论仓位是正数负数,都是开仓;结束是在仓位值为0的日期,为一个trade周期。

看order和trade的输出日志,就可以深刻理解trade和order的触发时间点。

实际上trade就是从开仓到平仓的一个周期,在第一次开仓触发open,仓位为0时触发closed。
order就是订单买卖交易的完整过程。

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值