iT邦幫忙

2021 iThome 鐵人賽

DAY 20
0
永豐金融APIs

永豐金融APIs - 從零開始到放棄!?系列 第 20

Backtrader - 策略收益

  • 分享至 

  • xImage
  •  

以下內容皆參考 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())

就可以進行我們的策略回測


上一篇
Backtrader - 新增策略
下一篇
Backtrader - 指標使用
系列文
永豐金融APIs - 從零開始到放棄!?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言