股票量化交易进阶002_回测框架backtrader(二)

本文详细剖析了backtrader回测框架中的关键代码,包括`from future import`导入的特性,参数设置,打印函数,数据访问方式,以及订单和交易通知函数的用法。通过理解这些核心概念,有助于深入理解和应用backtrader进行股票量化交易策略的开发。


一、认识backtrader中的代码

1.from future import (absolute_import, division, print_function,unicode_literals)

**
from future import (absolute_import, division, print_function,unicode_literals)
**

absolute_import:顾名思义为:加入绝对引入这个新特性。
说到绝对引入,当然就会想到相对引入。那么什么是相对引入呢?
其实个个引入,可以理解成绝对路径和相对路径的区别,例如C/C++语言中的头文件包含一样,#include""与#include<>的区别就是头文件查找路径先后的问题而已,所以就不过多介绍了。

  • division:导入python未来支持的语言特征division(精确除法)。
    当我们没有在程序中导入该特征时,"/“操作符执行的是截断除法(Truncating
    Division),当我们导入精确除法之后,”/"执行的是精确除法,如下所示:
3/4
0

from future import division

3/4
0.75

当我们导入精确除法后,若要执行截断除法,可以使用"//"操作符:

3//4
0
  • print_function:导入python未来支持的print函数。
python2.7
print “Hello world”

python3
print(“Hello world”)
  • unicode_literals:

① Python的每个新版本都会增加一些新的功能,或者对原来的功能作一些改动。有些改动是不兼容旧版本的,也就是在当前版本运行正常的代码,到下一个版本运行就可能不正常了
② 从python 2.7到Python 3.x就有不兼容的一些改动,比如2.x里的字符串用’xxx’表示str,Unicode字符串用u’xxx’表示unicode,而在3.x中,所有字符串都被视为unicode,因此,写u’xxx’和’xxx’是完全一致的,而在2.x中以’xxx’表示的str就必须写成b’xxx’,以此表示“二进制字符串”。
要直接把代码升级到3.x是比较冒进的,因为有大量的改动需要测试。相反,可以在2.7版本中先在一部分代码中测试一些3.x的特性,如果没有问题,再移植到3.x不迟。
③ Python提供了__future__模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性。举例说明如下:
④ 为了适应Python 3.x的新的字符串的表示方法,在2.7版本的代码中,可以通过unicode_literals来使用Python 3.x的新的语法:在python3中默认的编码采用了unicode, 并取消了前缀u. 如果代码要兼容python2/3

2. params = ((‘x’,1))

# 参数定义
    params = (
        ('maPeriod', 20),
    )

这是一个标准的Python元组,它里面又包含一些元组,例如这个写法:

params = (
(‘myparam’, 27),
(‘exitbars’, 5),
)

3. print(‘%s, %s’ % (dt.isoformat(), txt))

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

print(‘%s, %s’ % (dt.isoformat(), txt))

就是把日期跟txt连接起来,如: print(‘Hello’,‘World!’)-> Hello World!
之后调用这个log函数,就会自动添加日期到txt前面

4. self.datas[0].close[0]

在这里插入图片描述
把上面代码拆分下:
‘1’ -> self.datas
‘2’ -> selft.datas[0]
‘3’ -> self.datas[0].close
‘4’ -> self.datas[0].close[0]

它们分别是什么意思呢?
我们在代码中依次添加,打印结果看看究竟

 def __init__(self):
        # 获得均线策略的均线值
        self.ma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maPeriod)
        # 保存对收盘价线最新数据的引用
        self.dataclose = self.datas[0].close

        print('data',self.datas)
        print('data[0]',self.datas[0])
        print('dataclose',self.datas[0].close)
        print('dataclose0',self.datas[0].close[0])
        print('dataclose-1',self.datas[0].close[-1])
        print('dataclose-2',self.datas[0].close[-2])
        exit()

打印结果:
data [<backtrader.feeds.pandafeed.PandasData object at 0x0000023A98E919C0>]
data[0] <backtrader.feeds.pandafeed.PandasData object at 0x0000023A98E919C0>
dataclose <backtrader.linebuffer.LineBuffer object at 0x0000023A98E91120>
dataclose0 1183.0
dataclose-1 1185.8
dataclose-2 1163.0

从log中可以看出 1和2属于 pandafeed, 3属于LineBuffer,4属于具体的值

  • Data Feed 数据馈送对象,pandafeed也是一种Data Feed

Backtrader 中有一个“Data Feed” 或 “Data Feeds” 概念(可将其称为“数据馈送对象” ),其实这个“Data Feed” 或 “Data Feeds”就是我们熟悉的数据表格或数据表格集合 。Data Feed 在 Backtrader 中扮演一个“数据传递者”的角色,给策略有序的提供数据以及数据的索引位置

  • self.datas

大家在策略函数中经常用到的 self.datas 属性就是一个 Data Feeds,对应通过 Cerebro 导入的行情数据表格的集合(可能只导入了一只证券的行情数据,也可能导入了 N 只证券的行情数据)。在这个集合中,数据表格是按照导入的顺序依次确定索引位置,第一个导入的数据表格的索引位置为 0 ,之后的依次递增,
在这里插入图片描述
从上面图可以很清楚看到self.datas就是传入backtrader框架中针对当前策略的所有行情数据,比如策略中需要传了3只股票的数据,self.datas就是这3只股票的数据集,self.data[0],则就对应着传入的第一只股票的行情数据表格。

  • self.datas[0].close
    列是“lines”,Backtrader 将数据表格的列拆成了一个个 line 线对象,一列,一个指标,’该指标的时间序列都可以作为一条线 line。Backtrader 默认情况下要求导入的数据表格要包含 7 个字段:‘datetime’、 ‘open’、 ‘high’、 ‘low’、 ‘close’、 ‘volume’、 ‘openinterest’ ,这 7 个字段序列就对应了 7 条 line 。其实给列赋予“线”的概念也很好理解,回测过程中用到的时间序列行情数据可视化后就是一条条曲线:close 曲线、 open 曲线、high 曲线 …
    在这里插入图片描述
  • self.datas[0].close[0]
    self.datas[0].close[0]并不是代表line中的最后一条数据。指backtrader next方法运行到当前bar(行是“Bars”,当前bar可以看成当前行或者当前k线/当前日期)的收盘价。
    所以索引 0 号位置永远指向当前时间点的数据,-1 号位置指向前一个时间点的数据
    在这里插入图片描述

5. def notify_order(self, order)

    def notify_order(self, order):
        # 如果订单在提交或者允许的情况下,直接返回
        if order.status in [order.Submitted, order.Accepted]:
            # 做多/做空 订单 已提交/已执行 到/被代理 - 无事可做
            return

        # 检查订单是否已经完成
        # 注意:如果没有足够资金,代理可能拒绝订单

        # 如果下单完成了
        if order.status in [order.Completed]:
            if order.isbuy():
                # 第一个前复权买入价,第二个订单执行的总价格,第三个手续费
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))
            elif order.issell():
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))
        # 如果下单的情况在另外状态:订单取消/保证金不足/拒绝
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')
            # Write down: no pending order
        # 将订单状态重置为None
        self.order = None

在订单状态发生变化时,触发策略的 notify_order方法,在方法中可以查询订单状态相关信息,或执行一些动作。该方法在next方法前触发

def notify_order(self, order): 方法中,通过参数order 访问 order信息
order.status 订单状态(int)
order.getstatusname() 订单状态名称
order.getstatusname(order.status) 通过订单状态int,获取订单状态名称

订单状态 0~8

[Created、Submitted、Accepted、Partial、Completed、Canceled、Expired、Margin、Rejected]

在订单通知事件中,可以通过 order.executed.price、order.executed.size 访问订单价格和执行数量。

order.isbuy() 、order.issell() 判断是买卖单。

order.alive() 判断是否在状态 Partial 或 Accepted 中。

order.data 可以访问该订单的行情数据对象。

6. def notify_trade(self, trade):

    # 通知每笔交易收益情况(可省略)
    def notify_trade(self, trade):
        # 没有交易的话,直接return
        if not trade.isclosed:
            return
        # 交易结束,输出信息,第一个为毛利润,第二个为净利润(扣除手续费)
        self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                 (trade.pnl, trade.pnlcomm))

每笔交易完会通知收益情况,比如这笔交易是盈利多少(trade.pnl),扣除手续费盈利多少(trade.pnlcomm)。

如下面log情况
在这里插入图片描述

7.def next(self):

# 每个bar都会执行一次,即回测的每个日期都会执行一次
    def next(self):
        #   检查订单是否挂起。。。如果是,我们无法发送第二个
        if self.order:
            print('===有正在执行的订单', self.order)
            return
        # 空仓
        if not self.position.size:
            if self.datas[0].close[0] >= self.ma[0]:
                self.order = self.buy(size=200)
                print(f"====买入>>>买入价{self.datas[0].close[0]} > 均线价{self.ma[0]}  买入后仓位={self.getposition().size}")
        else:
            if self.datas[0].close[0] < self.ma[0]:
                self.order = self.sell(size=200)
                print(f"====卖出>>>买出价{self.datas[0].close[0]} < 均线价{self.ma[0]}  买入后仓位={self.getposition().size}")

主逻辑函数,根据何种逻辑进行买卖,还有什么时候买卖都是写到这个函数中

总结

`要真正掌握一个东西,还真得细嚼。
通过理解每句代码的含义,可以让你更加深刻的理解backtrader框架,包括它是如何设计的。

<think>我们参考了用户提供的引用内容,这些引用都指向了使用backtrader进行量化交易策略开发的主题。用户明确询问关于“backtrader量化交易策略开发”,因此我们将围绕这一主题展开答。 首先,我们需要明确backtrader是一个Python框架,用于开发交易策略。它提供了丰富的功能,包括数据加载、策略编写、执行以及结果分析等。根据引用[2]和[4],backtrader的基本使用流程包括安装backtrader、准备数据、创建策略类、设置参数、实现策略逻辑、运行和分析结果。 接下来,我们将按照典型的使用步骤进行说明: 1. **安装backtrader**:可以通过pip安装。 2. **准备交易数据**:backtrader支持多种格式的数据,如Pandas DataFrame、CSV文件等。数据通常包含时间序列的开盘价、最高价、最低价、收盘价和成交量(OHLCV)。 3. **创建策略类**:需要继承`backtrader.Strategy`类,并实现`__init__`和`next`方法。在`__init__`中定义需要的指标(如移动平均线),在`next`中编写交易逻辑。 4. **设置策略参数**:通过类属性`params`来定义可调整的参数,例如移动平均线的周期。 5. **实现策略逻辑**:在`next`方法中,根据指标和当前数据判断买卖信号。 6. **运行**:创建Cerebro引擎(backtrader的核心控制类),加载数据,添加策略,设置初始资金和交易手续费等,然后运行。 7. **分析结果**:backtrader提供了多种分析工具和绘图功能,可以输出结果和图表。 下面,我们将通过一个简单的例子来展示如何使用backtrader实现一个双移动平均线交叉策略(金叉买入,死叉卖出)。 ### 示例:双移动平均线策略开发 #### 步骤1:安装backtrader ```bash pip install backtrader ``` #### 步骤2:准备数据 假设我们有一个名为`data.csv`的股票历史数据文件,包含日期(Date)、开盘价(Open)、最高价(High)、最低价(Low)、收盘价(Close)和成交量(Volume)。数据格式如下: ``` Date,Open,High,Low,Close,Volume 2020-01-01,100,102,99,101,100000 2020-01-02,101,103,100,102,120000 ... ``` #### 步骤3:创建策略类 ```python import backtrader as bt class DoubleSMAStrategy(bt.Strategy): # 定义策略参数,这里定义两个移动平均线的周期 params = (('fast_period', 10), ('slow_period', 30)) def __init__(self): # 计算快速移动平均线(如10日) self.fast_sma = bt.indicators.SimpleMovingAverage( self.data.close, period=self.params.fast_period) # 计算慢速移动平均线(如30日) self.slow_sma = bt.indicators.SimpleMovingAverage( self.data.close, period=self.params.slow_period) def next(self): # 如果快速均线上穿慢速均线(金叉),买入 if not self.position: # 如果没有持仓 if self.fast_sma[0] > self.slow_sma[0] and self.fast_sma[-1] <= self.slow_sma[-1]: self.buy() # 全仓买入 # 如果快速均线下穿慢速均线(死叉),卖出 elif self.fast_sma[0] < self.slow_sma[0] and self.fast_sma[-1] >= self.slow_sma[-1]: self.sell() # 卖出全部持仓 ``` #### 步骤4:运行 ```python # 创建引擎 cerebro = bt.Cerebro() # 加载数据 data = bt.feeds.GenericCSVData( dataname='data.csv', dtformat='%Y-%m-%d', datetime=0, open=1, high=2, low=3, close=4, volume=5, openinterest=-1 ) cerebro.adddata(data) # 添加策略 cerebro.addstrategy(DoubleSMAStrategy) # 设置初始资金 cerebro.broker.set_cash(100000.0) # 设置交易手续费(此处为万分之三) cerebro.broker.setcommission(commission=0.0003) # 运行 print('初始资金: %.2f' % cerebro.broker.getvalue()) cerebro.run() print('最终资金: %.2f' % cerebro.broker.getvalue()) # 绘制结果 cerebro.plot() ``` #### 步骤5:分析结果 运行上述代码后,我们可以得到的最终资金情况,并通过图表观察策略的执行情况,包括资金曲线、买卖点、移动平均线等。 ### 注意事项 - 在策略中,我们使用`self.position`来判断是否有持仓,避免重复买入。 - 在`next`方法中,我们通过比较当前时刻(索引0)和上一时刻(索引-1)的均线值来判断交叉。 - 中需要考虑交易成本(手续费),我们在`setcommission`中设置了手续费率。 以上是一个简单的双均线策略的实现。实际策略开发中,我们还需要考虑更多因素,如滑点、止损止盈、仓位控制等。backtrader提供了丰富的功能来支持这些需求[^4][^5]。 --- §§相关问题§§ 1. 如何优化backtrader策略参数? 2. backtrader中如何实现止损和止盈? 3. backtrader支持哪些数据源? 4. 如何评估backtrader结果? 5. backtrader中如何实现多策略组合?</think>### Backtrader量化交易策略开发指南 Backtrader是一个功能强大的Python框架,用于量化交易策略的开发试和优化。以下是核心开发流程及关键要点: --- #### 一、基础开发流程 1. **安装框架** ```bash pip install backtrader ``` 2. **数据准备** 支持多种数据格式(CSV/Pandas DataFrame等),需包含时间序列的OHLCV数据(开盘价、最高价、最低价、收盘价、成交量)[^4]: ```python data = bt.feeds.YahooFinanceData(dataname='AAPL', fromdate=datetime(2020,1,1)) ``` 3. **创建策略类** 继承`bt.Strategy`并实现核心方法: ```python class MyStrategy(bt.Strategy): params = (('fast_ma', 10), ('slow_ma', 30)) # 可调参数 def __init__(self): self.fast_ma = bt.indicators.SMA(self.data.close, period=self.params.fast_ma) self.slow_ma = bt.indicators.SMA(self.data.close, period=self.params.slow_ma) def next(self): if self.fast_ma > self.slow_ma and not self.position: # 金叉买入 self.buy(size=100) elif self.fast_ma < self.slow_ma and self.position: # 死叉卖出 self.sell(size=100) ``` 4. **执行** ```python cerebro = bt.Cerebro() cerebro.adddata(data) cerebro.addstrategy(MyStrategy) cerebro.broker.set_cash(100000) # 初始资金 cerebro.broker.setcommission(0.001) # 手续费率0.1% results = cerebro.run() cerebro.plot() # 可视化结果 ``` --- #### 、核心功能详解 1. **指标计算** 内置100+技术指标(SMA, RSI, MACD等),支持自定义: ```python self.rsi = bt.indicators.RSI(self.data.close, period=14) ``` 2. **仓位管理** - `self.buy()` / `self.sell()`:开平仓操作 - `self.position.size`:当前持仓数量 - `self.close()`:平掉全部仓位 3. **参数优化** 使用`cerebro.optstrategy()`进行参数网格搜索: ```python cerebro.optstrategy(MyStrategy, fast_ma=range(5,20), slow_ma=range(20,50)) ``` 4. **绩效分析** 自动生成关键指标: ```python print('夏普比率:', bt.analyzers.SharpeRatio.get_analysis()) print('最大撤:', bt.analyzers.DrawDown.get_analysis()) ``` --- #### 三、进阶技巧 1. **多资产** ```python data1 = bt.feeds.PandasData(dataname=df_stock) data2 = bt.feeds.PandasData(dataname=df_etf) cerebro.adddata(data1) cerebro.adddata(data2) ``` 2. **事件驱动逻辑** 在`notify_order()`中处理订单状态: ```python def notify_order(self, order): if order.status in [order.Completed]: print(f"订单成交: {order.executed.price}") ``` 3. **自定义分析器** 继承`bt.Analyzer`实现个性化绩效统计[^3]: ```python class WinRateAnalyzer(bt.Analyzer): def __init__(self): self.win_count = 0 self.total_count = 0 def notify_trade(self, trade): if trade.status == trade.Closed: self.total_count += 1 if trade.pnl > 0: self.win_count += 1 def get_analysis(self): return {'win_rate': self.win_count / self.total_count} ``` --- #### 四、最佳实践 1. **避免未来函数**:仅使用`self.data.close[0]`等当前时点数据 2. **考虑滑点**:设置`cerebro.broker.set_slippage_perc(0.005)` 3. **验证过拟合**:通过`Walk-Forward Analysis`分阶段验证策略鲁棒性 4. **结果可视化**:使用`cerebro.plot(style='candlestick')`生成K线图+信号标记 > 提示:完整案例可参考Backtrader官方文档和GitHub示例库[^2][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Johnny2004

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

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

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

打赏作者

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

抵扣说明:

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

余额充值