其他分享
首页 > 其他分享> > Backtrader快速入门——1. QuickStart

Backtrader快速入门——1. QuickStart

作者:互联网

1. backtrader介绍

1.1 基本情况

参考文档

backtrader框架介绍

官方定义:
backtrader是一个用于回测和交易的功能丰富的框架,可以让你专注于写可用的交易策略,指标和分析器,而不会花费时间在构建基础架构上

1.2 安装

安装的话还是直接去github上面看最新的吧。(截止2021.1.29我写这个博客的时候,github主页最新一次更新是在2020年6月,7个月之前。。。)

"""不需要画东西 """
pip install backtrader  

"""需要画东西 """
pip install backtrader[plotting] -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
"""加一个镜像,下载的快点"""

1.3 用到的一些金融词语

portfolio:投资组合,有价证券


2. QuickStart

主要就是参考两个文档,加了一些个人理解的修正。
(建议直接看英文,看不懂再去看中文翻译文档)

注意:由于这个QuickStart教程所使用的的数据文件会一直更新,所以最后得出的类似收盘价这些内容都会有所改变,所以文档中展示的一些结果可能和你实际操作之后得到的不同。

2.1 使用这个平台/框架

让我们从零开始来跑一些完整的例子,在此之前,先来了解两个基本概念。

折线(Line)

索引从0开始


2.2 从0到100 ,样本

1. 基础设置

import backtrader as bt 
'''首先引入了backtrader这个包'''
cerebro = bt.Cerebro()
'''实例化Cerebro (大脑)引擎'''
# Cerebro引擎在后台创建了broker(经纪人)实例,系统默认每个broker的初始资金量为10000
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())

2. 设置现金(cash)

很少有人会拿着1w块钱去炒股,所以要改改这个初始设置(可以接着上面的代码继续跑)

cerebro.broker.setcash(100000.0)
"""设置cash用broker.setcash这个函数"""
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())

3. 添加现有数据

import datetime 
import os.path 
import sys  
import backtrader as bt
cerebro = bt.Cerebro()

"""设定一个根目录(数据文件的上级目录)"""
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))

"""获取数据文件的完整路径"""
datapath = os.path.join(modpath, '../../datas/orcl-1995-2014.txt')

"""
创建数据交易集
注意: Yahoo的价格数据有点非主流,它是以时间倒序排列的。
datetime.datetime()中的reversed=True参数是将顺序反转一次,
这样就得到了我们想要的正序数据。
"""
data = bt.feeds.YahooFinanceCSVData(
    dataname=datapath,
    fromdate=datetime.datetime(2000, 1, 1), """数据起始日期"""
    todate=datetime.datetime(2000, 12, 31),"""数据截止日期"""
    reverse=False)
    
"""把数据加入Cerebro中"""
cerebro.adddata(data)

cerebro.broker.setcash(100000.0)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())

4. 第一个策略

现在有了交易数据,也有了本金(经纪人管理本金,类似RL中的agent,负责执行算法动作的代理),可以开始进入股市/证券市场去搏一搏,单车变摩托了。
可以考虑用代码实现一个策略,然后应用这个策略,来看看每天的收盘价。

DataSeries(交易数据的基类)对象,可以直接访问到 OHLC (开盘价、最高价、最低价、收盘价)等数据。这使我们打印数据很方便。

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import datetime  
import os.path  
import sys  
import backtrader as bt

class TestStrategy(bt.Strategy):
"""创建策略(继承bt.Strategy)"""
    def log(self, txt, dt=None):
        ''' 这个策略的日志函数(输出信息)'''
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))
    def __init__(self):
    """保存一个原数据中close的副本(便于比较)"""
        self.dataclose = self.datas[0].close
    def next(self):
        # Simply log the closing price of the series from the reference
        self.log('Close, %.2f' % self.dataclose[0])
if __name__ == '__main__':
    cerebro = bt.Cerebro()
    """添加策略 虽然那个策略就是打印信息,剩下啥都没干"""
    cerebro.addstrategy(TestStrategy)
    """创建数据"""
    modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
    datapath = os.path.join(modpath, '../../datas/orcl-1995-2014.txt')
    data = bt.feeds.YahooFinanceCSVData(
        dataname=datapath,
        fromdate=datetime.datetime(2000, 1, 1),
        todate=datetime.datetime(2000, 12, 31),
        reverse=False)

  """向cerebro中添加数据"""
    cerebro.adddata(data)
    cerebro.broker.setcash(100000.0) # 设置初始资金
    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    # 打印一开始的情况
    cerebro.run()# 运行一下(运行时的cerebro以及添加过策略了)
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    # 打印最后的结果

这里的这个策略只是个示例,这个TestStrategy并没有进行任何交易,只是打印出日期和对应的收盘价,所以可以看到最初和最后打印出的资金没有发生变化。

这里只是给出一个示例,cerebro设置初始资金,添加数据之后,再添加策略,就可以run得到一个结果。

额外说明:

框架在调用init时,该策略已经具有一个数据列表datas,这是标准的Python列表,可以按插入顺序访问数据。

这个datas应该是cerebro添加adddata添加的那个数据,也就是策略会作用于那个data上,策略和数据通过cerebro连接在一起。

列表中的第一个数据self.datas[0]用于交易操作,并且策略中的所有元素都是由框架的系统时钟进行同步的。

由于只需访问收盘价数据,于是使用 self.dataclose = self.datas[0].close将第一条价格数据的收盘价赋值给新变量。

系统时钟当经过一个K线柱的时候,策略的next()方法就会被调用一次。这一过程将一直循环,直到其他指标信号出现为止。此时,便会输出最终结果。关于这些,后继内容会讲到。

5. 给策略加点逻辑

上面的第一个策略只是打印了一些信息,没什么用,这里可以考虑加入一些逻辑,比如:如果价格连续三个交易日下跌,就买(高抛低吸中的低吸)

只需要修改上面策略类中的next()函数即可

def next(self):
	self.log('Close, %.2f' % self.dataclose[0])	
	if self.dataclose[0] < self.dataclose[-1]:
	    if self.dataclose[-1] < self.dataclose[-2]:
	    """如果连续三天跌,就买"""
	        self.log('BUY CREATE, %.2f' % self.dataclose[0])
	        self.buy()
     """其实两个if可以写成一个"""
     if (self.dataclose[0] < self.dataclose[-1]) and (self.dataclose[-1] < self.dataclose[-2]) :

6. 不仅有买入 还有卖出

在知道如何买入(做多)之后,需要知道如何卖出,并且还需要了解该策略是否在市场中。 Strategy类有一个变量position保存当前持有的资产数量(可以理解为金融术语中的头寸),buy()sell()会返回被创建的订单(尚未执行的), 订单状态的更改将通过notify方法通知给策略Strategy

卖出逻辑也很简单: 5个K线柱后(第6个K线柱)不管涨跌都卖。 请注意,这里没有指定具体时间,而是指定的柱的数量。一个柱可能代表1分钟、1小时、1天、1星期等等,这取决于你价格数据文件里一条数据代表的周期。 虽然我们知道每个柱代表一天,但策略并不知道。(一般都是一天)

因为买入的时候,用的是交易日,所以这里应翻译为:5个交易日(第6个交易日)不管涨跌都卖。

只在空仓的时候才进行买入操作(空仓表示不在市场里,没有可以交易的证券)

注意: 没有将柱的下标传给next()方法,怎么知道已经经过了5个柱了呢? 这里用了Python的len()方法获取它Line数据的长度。 交易发生时记下它的长度,后边比较大小,看是否经过了5个柱。

代码部分还是只用更新策略类,其他的不变(设置数据feed和本金不用变)

class TestStrategy(bt.Strategy):

    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    def __init__(self):
        """从后面的使用不难看出,这个order也是一个类,有status这个属性"""
        self.dataclose = self.datas[0].close
        self.order = None # 追踪挂单(挂单 创建但是还没有执行的)

    def notify_order(self, order):
        """订单状态的更改将通过notify方法通知给策略Strategy"""
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            return

        """检查一个订单order是否完成completed,当cash本金不够时,broker经纪人可以拒绝订单"""
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log('BUY EXECUTED, %.2f' % order.executed.price)
            elif order.issell():
                self.log('SELL EXECUTED, %.2f' % order.executed.price)

            self.bar_executed = len(self)# 衡量已经执行过几个k线柱/ticks(系统时钟周期)
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')
        """执行完之后,将order重新标记为none,标明没有待执行订单"""
        self.order = None

    def next(self):
        self.log('Close, %.2f' % self.dataclose[0]) # 打印出原始的收盘价
        
        # 检查订单是否待执行,如果待执行,不能发送第二个订单
        if self.order:
            return

        # Check if we are in the market
        # 如果仓位为空(有本金 但是还没有买证券,没有进行市场进行交易的权利,只能买)
        if not self.position:
            if self.dataclose[0] < self.dataclose[-1]:
                    if self.dataclose[-1] < self.dataclose[-2]:      
                        # 连续三天收盘价都在下降,低吸(按照默认的参数买入)
                        self.log('BUY CREATE, %.2f' % self.dataclose[0])
                        # 追踪创建的订单避免第二个出现
                        self.order = self.buy()
        else:
            # 已经在市场中(有可以交易的证券了)才可以卖
            if len(self) >= (self.bar_executed + 5):
                # 经过5个k线柱之后,不管涨跌都卖
                self.log('SELL CREATE, %.2f' % self.dataclose[0])
                # 继续追踪避免产生第二个订单
                self.order = self.sell()

这里的position指的是仓位,是否持有股票/证券进场(进场资格)

7. 经纪人的手续费

给代理人/经纪人的手续费一般称之为佣金commission。设置一个常见的比率0.1%(不管买还是卖都要付,代理人就是挺贪心的),一行代码就可以搞定

cerebro.broker.setcommission(commission=0.001)

其余代码参考中文文档里的(注释也很详细)backtrader中文文档(非官方)中的4.9 经纪人说,手续费呢?

8.自定义策略:技术指标参数

Parameters(指标参数)

在实战中,一般不将参数写死到策略中。Parameters(参数)就是用来处理这个的。 参数的定义像这样:

# 参数的定义像这样: 
params = (('myparam', 27), ('exitbars', 5),)

就去看中文文档吧。。。基本概念了解清楚就差不多可以直接上手代码了。

额外知识

T+0

参考百度百科视频blob(是一种交易制度):
https://baike.baidu.com/058bdf84-dd05-4e1c-8aa1-eaef8053ed7e

头寸

但是在股票市场,似乎用来代表仓位更合适,position你可以理解成“开仓位置”或者“持仓位置”
参考知乎回答:
关于《股票作手回忆录》中position,应该怎么理解的问题,部位?头寸?仓位?

关于K线

这里插一部分,关于k线的知识。
在这里插入图片描述
这是去东方财富网随便截的一张图,可以看到有一些框框,参考知乎专栏文章:股票基础知识—K线图基础知识(一)

挂单

参考
福步外贸论坛(FOB Business Forum) » 外贸英语 » pending order什么意思啊?

挂单(Pending order)- 用户下达给经纪商的、在市场报价达到某个水平才能执行的订单。 待执行订单。
有四种挂单形式:
Buy Limit - 在市场实时报价中的买价达到或低于挂单价位时建立长仓(买进)。该挂单价位应低于下单时的市场报价;
Buy Stop - 在市场实时报价中的买价达到或高于挂单价位时建立长仓(买进)。该挂单价位应高于下单时的市场报价;
Sell Limit - 在市场实时报价中的卖价达到或高于挂单价位时建立短仓(卖出)。该挂单价位应高于下单时的市场报价;
Sell Stop - 在市场实时报价中的卖价达到或低于挂单价位时建立短仓(卖出)。该挂单价位应低于下单时的市场报价。

标签:broker,%.,入门,Backtrader,self,cerebro,dataclose,order,QuickStart
来源: https://blog.csdn.net/Castlehe/article/details/113378863