以下內容皆參考 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())
就可以進行我們的策略回測