第三篇 Strategy(1) 生命周期函数

在整个框架中,策略是重中之重。策略是你交易的逻辑的展现,是你思维的展示。量化交易者要认真对待。

策略模块相比其他模块需要更多的代码编写,交易逻辑是自己的,无法代替。其他模块有既定的、成熟的方案,拿来主义就可,当然也可以根据自己的需求重新编写。

框架中已经有了Strategy类,编写新的策略继承这个类。然后根据自己的需要,重写某些函数。

backtrader模块的编写一般都在继承的继承上编写,backtrader作者为大家无私奉献,劳心劳力。

这一片有三个重点:一是从整体上了解整个Strategy,掌握内部的函数结构。二是交易思维如何落实到代码中,三是了解每个函数具体作用。

1. 策略类中内部函数

import backtrader as bt

class TestStrategy(bt.Strategy):

    def __init__(self):   # 必选项
        pass 

    def start(self):
        pass

    def prenext(self):
        pass

    def nextstart(self):
        pass

    def next(self):    # 必选项
        pass

    def stop(self):
        pass

    def notify_cashvalue(cash, value):
        # 监听函数,监听cash,value的变化。
        pass

    def notify_trade(trade):
        # 监听函数, 监听trade的变化
        pass

2.生命周期函数讲解

2.1__init__函数

_init_ 初始化,和python中的类一样用法。在实例中调用,可以创建需要的变量。

在策略中,init有个重要的作用,那就是访问line对象。此对象是完整的对象。

在操作中经常对线对象或者含线对象进行操作,从而创造出新的线对象。

当然可以直接引用指标的含线对象

def __init__(self):
  
  self.sma = bt.indicators.MovingAverageSimple(self.datas[0].lines.close, period=15)
  
  self.dif = self.datas[0].close - self.sma

需要注意的是init方法是不能访问线的点。线的点在其他函数中则会用到。

在平均线中参数中,close线是默认的。self.datas[0].close 可以改写成 self.datas[0]

在前面 一些概念中介绍,含线对象需要是属于哪个数据时,第一个数据是默认值。那么self.sma可以简化成如下:

  self.sma = bt.indicators.MovingAverageSimple(period=15)

2.2 next函数

2.2.1运行方式

line线对象或者含线对象是经过特殊处理的列表。是可以循环迭代的,next方法可以访问线中的点。self.datas[0].close[0]是今日收盘价,self.datas[0].close[-1]是昨日收盘价…

next是不能访问线的整体。

在最小周期为1的情况,next方法调用的次数跟数据量是一样,next方法不停的循环迭代。
在这里插入图片描述

如上图,假设这就是全部的交易数据,总共四行。next首先访问20050104日这一行bar,然后一次访问,直到最后20050107日bar结束。

注意索引[0]是当前next正在处理的bar行,代表当前。例如next循环到第二行,self.datas[0].close[0]指的是第二行的收盘价。这个索引是动态的。

不要搞混。这一行数据是一个bar,是概念上的。next还是要通过线对象来访问点的数据信息。

next函数迭代是在满足最小周期交易数据时,才能运行。

2.2.2时间访问

在前面介绍过,在使用多个数据对象时,第一个数据的时间将作为标准,所以第一个数据的时间是长度要足够长,要有连续性。在投资组合中,最好第一个数据使用指数数据,保证了时间线的稳定性,连续性。

存在两个时间对象,都可以进行访问:

  • 策略时间对象 即self.datatime。当前时间点:self.datetime[0]
  • 行情数据时间对象 即self.datas[0].datetime。 当前时间点:self.datas[0].datetime[0]

然而时间是float类型,使用时需要转化成python数据格式。

可以使用datetime包进行转换,backtrader已经内置其方法。对当前时间进行格式转变:

  • 策略时间对象进行格式转化 self.datetime.datetime(0)
  • 行情数据进行时间格式转化 self.datas[0].datetime.datetime(0)

注意这里是()而不是[]

在进行时间格式转化时,数据多的话是很消耗算力资源的,backtrader编写了一个函数num2date,来提高运算效率

用法如下:

  • 策略时间对象 bt.num2date(self.datetime[0])
  • 数据行情时间对象 bt.num2date(self.datas[0].datetime[0])

数据行情时间线可以访问下个bar的时间点,如self.datas[0].datetime.datetime(1)。用策略时间self.datetime.datetime(1)则会出错。

在使用中,更多的使用的是数据行情的时间对象。

2.3其他声明周期函数

  • start 函数是提示策略可以开始了,默认是空函数
  • prenext 预告函数 调用了15日的移动平均线,刚开始处理的条数不足15,也就是前14条都会调用prenext函数。这个15日就是最小周期,在满足最小周期之前调用。
  • nextstart函数,满足最小周期时调用,只调用一次。默认行为是调用next
  • stop函数 停止函数在回测执结束后调用。默认的是空方法。

作者把几个周期函数比作人生命的某个阶段。

init怀孕------->start出生------>prenext童年------>next成年------->stop死亡

3.声明周期函数代码示例:

Strcycle.py

import pandas as pd
import backtrader as bt
from datetime import datetime
import sys, os

# 编写策略
class TestStrategy(bt.Strategy):

    def log(self, txt, dt=None):
        '''定义打印功能,被next函数调用'''
        dt = dt or self.datas[0].datetime.date(0)  
        print(f'{dt.isoformat()} {txt}')

    def start(self):
        print('start是开始')

    def __init__(self):
      
        self.num_prenext = 0  # prenext的计数器
        self.num_next = 0    # next的计数器
        self.sma = bt.indicators.MovingAverageSimple(period=15)
        print('init是第一个')

    def prenext(self):
        self.num_prenext += 1
        self.log(f'{self.num_prenext}次运行prenext方法')

    def nextstart(self):
        self.log(f'在第{len(self.data.close)}个bar运行nextstart方法')

    def next(self):
        self.num_next += 1
        self.log(f'这是第{self.num_next}次运行next方法')
    
    def stop(self):
        print('stop是结束')
				print(f'一共有{round(len(self.data.close))}个交易数据')  # len()已处理的数据量。

if __name__ == "__main__":
    # 1.大脑实体化
    cerebro = bt.Cerebro()
    # 2.添加策略
    cerebro.addstrategy(TestStrategy)

    # 3.获取数据,添加数据
    abspath = os.path.dirname(sys.argv[0])  # 获取当前程序所在的目录
    pathfile = os.path.join(abspath,'./601318.csv')   #获取数据文件

    stock_name = pd.read_csv(pathfile, index_col='date', parse_dates=True)
    start_date = datetime(2022, 6, 1)
    end_date = datetime(2022, 6, 30)
    data = bt.feeds.PandasData(dataname=stock_name, fromdate=start_date, todate=end_date)
    cerebro.adddata(data)  

    cerebro.run()

这个代码的结构:

  1. 策略TestStrategy
  2. 数据 Data Feeds
  3. 将策略和数据加入到大脑实例Cerebro中
  4. 运行回测。

(有些知识还未学习,重点在于了解策略内部的运行机制)

程序运行结果如下:

init第一个
start是开始
2022-06-01 1次运行prenext方法
2022-06-02 2次运行prenext方法
2022-06-06 3次运行prenext方法
2022-06-07 4次运行prenext方法
2022-06-13 8次运行prenext方法
2022-06-14 9次运行prenext方法
2022-06-15 10次运行prenext方法
2022-06-16 11次运行prenext方法
2022-06-17 12次运行prenext方法
2022-06-20 13次运行prenext方法
2022-06-21 14次运行prenext方法
2022-06-22 在第15个bar运行nextstart方法
2022-06-23 ------------这是第1次运行next方法
2022-06-24 ------------这是第2次运行next方法
2022-06-27 ------------这是第3次运行next方法
2022-06-28 ------------这是第4次运行next方法
2022-06-29 ------------这是第5次运行next方法
2022-06-30 ------------这是第6次运行next方法
stop是结束
一共有21个交易数据

选取了一个06月的交易日。从运行结果来看,先是init,start方法,先14次运行prenext方法,然后在第15个bar时运行nextstart方法,紧接着都是运行next方法,最后运行stop方法。

可见策略生命周期与最小周期是高度相关。设置15日的最小周期,在满足15个bar的情况下,next方法才能被调用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南万寿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值