Backtrader - 策略收益

以下内容皆参考 Backtrader 官网

昨天介绍了 backtrader 如何去执行一个策略,今天就介绍一下,策略的收益的评估。

def notify_trade(self, trade):
    if not trade.isclosed:
        return

    self.log("交易收益:毛利 %.2f 净利:%.2f" % (trade.pnl, trade.pnlcomm))

主要就是在我们的策略里,加上以上程序码,notify_trade 是在交易完成的时候会触发,传入的 trade 参数就有我们需要的毛利和净利。整段程序码完成後,如下:

import backtrader as bt

class TestStragety(bt.Strtegy):
    def log(self, txt, dt = None):
        dt = dt or self.datas[0].datetime.date(0)
        print("%s %s" % (dt.isoformat(), txt))

    def __init__(self):
        self.dataclose = self.datas[0].close

        self.order = None
        
    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("买入 价格: %.2f 成本: %.2f 手续费: %.2f" % (order.executed.price, order.executed.value, order.executed.comm))
            elif order.issell():
                self.log("卖出 价格: %.2f 成本: %.2f 手续费: %.2f" % (order.executed.price, order.executed.value, order.executed.comm))
                
            self.bar_executed = len(self)
                
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log("订单取消/余额不足/拒绝交易")

        self.order = None
    
    def notify_trade(self, trade):
        if not trade.isclose:
            return

        self.log("交易收益:毛利 %.2f 净利:%.2f" % (trade.pnl, trade.pnlcomm))
        
    def next(self):
        self.log("收盘价:%.2f" % self.dataclose[0])

        if self.order:
            return

        if not self.position:
            if self.dataclose[0] < self.dataclose[-1]:
                if self.dataclose[-1] < self.dataclose[-2]:
                    self.log("买入 %.2f" % self.dataclose[0])
                    self.order = self.buy(size=1000)

        else:
            if len(self) >= (self.bar_executed + 5):
                self.log("卖出 %.2f" % self.dataclose[0]):
                self.order = self.sell(size=1000)

再来,如果我们想要测试可以参数化,譬如原本我们固定持有 5 天後卖出,希望这个 5 天可以在执行的时候指定,就可以在交易策略的 class 里,新增 params 属性,来控制参数

class TestStrategy(bt.Strategy):

    params = (
        ("keepdays", 5)
    )

    #...略

    def next(self):
        # ...略

        if not self.position:
            # ...略
        else:

            if len(self) >= (self.bar_excuted + self.params.keepdays):
                self.log("卖出 %.2f" % self.dataclose[0])
                self.order = self.sell()

在执行的时候,就可以使用以下代码来变更参数

cerebro.addstrategy(TestStrategy, keepdays = 10)

我们顺便把是否输出结果,和要买多少股都给参数化

params = (
    ("keepdays", 5),
    ("printlog", False),
    ("defultSize", 1000),
)

除了这种设法,也可以使用 dict 来做设定,两个是有同样效果的

params = dict(
    keepdays = 5,
    printlog = False,
    defaultSize = 1000
)

简化写法

工程序就是懒,所以能少打一个字就少打一个字,所以 backtrader 也有一些简化的写法
self.parameter - self.p
self.datas[0] - self.data0 - self.data
self.datas[x] - self.datax
如果有载入多组资料的话,可以这麽用,x 是数字

所以整段程序码改完如下:

import backtrader as bt

class TestStragety(bt.Strtegy):
    params = (
        ("keepdays", 5),
        ("printLog", False),
        ("defaultSize", 1000),
    )

    def log(self, txt, dt = None, doPrint = False):
        if self.p.printLog or doPrint:
            dt = dt or self.data.datetime.date(0)
            print("%s %s" % (dt.isoformat(), txt))

    def __init__(self):
        self.sizer.setsizing(self.p.defultSizing)
        self.dataclose = self.data.close

        self.order = None
        
    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("买入 价格: %.2f 成本: %.2f 手续费: %.2f" % (order.executed.price, order.executed.value, order.executed.comm))
            elif order.issell():
                self.log("卖出 价格: %.2f 成本: %.2f 手续费: %.2f" % (order.executed.price, order.executed.value, order.executed.comm))
                
            self.bar_executed = len(self)
                
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log("订单取消/余额不足/拒绝交易")

        self.order = None
    
    def notify_trade(self, trade):
        if not trade.isclose:
            return

        self.log("交易收益:毛利 %.2f 净利:%.2f" % (trade.pnl, trade.pnlcomm))
        
    def next(self):
        self.log("收盘价:%.2f" % self.dataclose[0])

        if self.order:
            return

        if not self.position:
            if self.dataclose[0] < self.dataclose[-1]:
                if self.dataclose[-1] < self.dataclose[-2]:
                    self.log("买入 %.2f" % self.dataclose[0])
                    self.order = self.buy()

        else:
            if len(self) >= (self.bar_executed + self.p.keepdays):
                self.log("卖出 %.2f" % self.dataclose[0]):
                self.order = self.sell()

再配合执行的程序码如下

cerebro = bt.Cerebro()

cerebro.addstrategy(TestStrategy, printLog = True, keepdays = 10)

cerebro.setcommission(commission = 0.1425 / 100)

data = bt.feeds.PandasData(dataname = df, timeframe = bt.TimeFrame.Minutes)

cerebro.resampledata(data, timeframe = bt.TimeFrame.Days)

print("开始资金: %.2f" % cerebro.broker.getvalue())

cerebro.run()

print("结束资金: %.2f" % cerebro.broker.getvalue())

就可以进行我们的策略回测


<<:  [Day 20] 资料标注 (1/2) — Forget about the price tag ♫

>>:  [Day 20] Edge Impulse + BLE Sense实现唤醒词辨识(上)

Python 多赋值问题,推论过程与结果

题目来源:邦友问答,因觉得有趣就尝试推论看看 python 多赋值是如何运作的 以下是我推论出来的,...

[Day - 13] - Spring 依赖性注入元件管理运作与方法

Abstract 无论何种时候,每种系统的开发元件势必都有先後启动顺序,如何有效管控每项元件的启动流...

从 JavaScript 角度学 Python(4) - 型别与变数

前言 oh!终於要开始学习写 Python 了呢!(被揍),都混了三天,我如果再不开始写 Pytho...

Day13 Function Component的生命周期 - UseEffect

在Hook尚未出现之前,只有Class Component能够有生命周期可以使用。 什麽是生命周期?...

D32 - 完赛心得

30 天的填坑之旅终於结束了 ...(›´ω`‹ ) 不知道大家觉得如何呢? 第一次挑战将主题分成 ...