backtrader 的誕生是出於必要。我自己的...有一種感覺,我控制著自己的回溯測試平臺,可以嘗試新的想法。但是,在這樣做並且從一開始就完全 open 採購它時,很明顯它必須有一種方法來滿足他人的需求和願望。
作為未來的交易者,我本可以選擇基於點的計算和每輪傭金的固定價格,但這將是一個錯誤。
注意
7月31, 2015
使用新添加的操作/交易通知跟進帖子,修復交易損益數位的繪圖,並避免手動計算,如下面的示例所示
提高傭金:股票與期貨
相反,backtrader
它提供了使用常規的基於%大小/價格的方案和固定價格/點方案的可能性。選擇權在您手中。
不可知論
在繼續之前,讓我們記住,backtrader
嘗試對數據所代表的內容保持不可知論。不同的傭金方案可以應用於同一數據集。
讓我們看看如何做到這一點。
使用代理快捷方式
這使最終使用者遠離CommissionInfo
對象,因為可以使用單個函數調用創建/設置傭金方案。在常規 cerebro
創建/設置過程中,只需在成員變數上添加對 setcomission
成員變數的 broker
調用即可。以下電話會議為 Eurostoxx50 期貨在與互動經紀商合作時設定了通常的傭金方案:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)
由於大多數使用者通常只測試一台儀器,因此僅此而已。如果您已經對data feed給出了 aname
,因為圖表上同時考慮了多個工具,則可以稍微擴展此調用,如下所示:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0, name='Eurostoxxx50')
在這種情況下,此即時傭金計劃僅適用於名稱匹配的工具Eurostoxx50
。
設置委託參數的含義
-
commission
(預設值:0.0)以絕對或百分比形式表示每個操作 成本的貨幣單位。
在上面的例子中,a
buy
的每張合約為2.0歐元,每張合約為sell
2.0歐元。這裡的重要問題是何時使用絕對值或百分比值。
-
如果
margin
計算結果為False
(例如,它是 False,0或None),則將考慮commission
表示操作值的price
百size
分比 -
如果
margin
是其他東西,則認為操作發生在類似儀器上futures
,並且是commission
每size
張合約的固定價格
-
-
margin
(預設值:無)使用
futures
類似工具操作時所需的保證金。如上文所述-
如果設置了no
margin
,則將commission
理解為以百分比表示,並應用於price \* size
或buy
sell
操作的元件 -
如果設置了 a
margin
,則commission
將理解為一個固定值,該值乘以 或sell
操作的size
buy
分量
-
-
mult
(預設值:1.0)對於
future
類似的工具,這決定了乘法器應用於損益計算。這就是期貨同時具有吸引力和風險的原因。
-
name
(預設值:無)將傭金方案的應用限制在工具匹配
name
這可以在創建 data feed期間設置。
如果未設置,該方案將適用於系統中存在的任何數據。
現在有兩個例子:股票與期貨
上面的期貨例子:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)
股票示例:
cerebro.broker.setcommission(commission=0.005) # 0.5% of the operation value
創建永久金計劃
可以通過直接使用類來創建更永久的CommissionInfo
傭金計劃。使用者可以選擇在以下位置使用此定義:
from bt import CommissionInfo commEurostoxx50 = CommissionInfo(commission=2.0, margin=2000.0, mult=10.0)
稍後在另一個Python模組addcommissioninfo
中應用它:
from mycomm import commEurostoxx50 ... cerebro.broker.addcomissioninfo(commEuroStoxx50, name='Eurostoxxx50')
CommissionInfo
是一個物件,它像環境中backtrader
的其他物件一params
樣使用聲明。因此,上述內容也可以表示為:
from bt import CommissionInfo class CommEurostoxx50(CommissionInfo): params = dict(commission=2.0, margin=2000.0, mult=10.0)
稍後:
from mycomm import CommEurostoxx50 ... cerebro.broker.addcomissioninfoCommEuroStoxx50(), name='Eurostoxxx50')
現在是與SMA分頻器的「真實」比較
使用SimpleMovingAverage交叉作為進入/退出信號,相同的數據集將使用futures
類似的傭金方案進行測試,然後使用類似的傭金方案進行測試 stocks
。
注意
期貨頭寸不僅可以給出進入/退出行為,還可以在每次情況下給出反轉行為。但這個例子是關於比較傭金方案的。
代碼(完整策略請參見底部)是相同的,可以在定義策略之前選擇方案。
futures_like = True if futures_like: commission, margin, mult = 2.0, 2000.0, 10.0 else: commission, margin, mult = 0.005, None, 1
只需設置為futures_like
false以使用stocks
類似方案運行。
添加了一些 logging 代碼來評估不同傭金計劃的影響。讓我們只關注前 2 個操作。
對於期貨:
2006-03-09, BUY CREATE, 3757.59 2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 2000.00, Comm 2.00 2006-04-11, SELL CREATE, 3788.81 2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 2000.00, Comm 2.00 2006-04-12, OPERATION PROFIT, GROSS 328.00, NET 324.00 2006-04-20, BUY CREATE, 3860.00 2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 2000.00, Comm 2.00 2006-04-28, SELL CREATE, 3839.90 2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 2000.00, Comm 2.00 2006-05-02, OPERATION PROFIT, GROSS -243.30, NET -247.30
對於股票:
2006-03-09, BUY CREATE, 3757.59 2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 3754.13, Comm 18.77 2006-04-11, SELL CREATE, 3788.81 2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 3786.93, Comm 18.93 2006-04-12, OPERATION PROFIT, GROSS 32.80, NET -4.91 2006-04-20, BUY CREATE, 3860.00 2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 3863.57, Comm 19.32 2006-04-28, SELL CREATE, 3839.90 2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 3839.24, Comm 19.20 2006-05-02, OPERATION PROFIT, GROSS -24.33, NET -62.84
第 1次 操作具有以下價格:
-
買入(執行)-> 3754.13 / 賣出(執行)-> 3786.93
-
期貨損益(帶傭金):324.0
-
股票盈虧(含傭金):-4.91
嘿!!傭金已經完全吞噬了運營的任何利潤,
stocks
但只意味著對運營的futures
一個小小的影響。 -
第 2個 操作:
-
買入(執行)-> 3863.57 / 賣出(執行)-> 3389.24
-
期貨損益(含傭金):-247.30
-
股票損益(含傭金):-62.84
對於這種負操作,咬合已經明智地更大了
futures
-
但:
-
期貨累計凈損益:324.00 + (-247.30) = 76.70
-
股票累計凈損益: (-4.91) + (-62.84) = -67.75
累積效應可以在下面的圖表上看到,在那裡也可以看出,在全年結束時,期貨產生了更大的利潤,但也遭受了更大的回撤(在水下更深)
但重要的是:是否futures
stocks
... 它可以進行回溯測試。
期貨傭金
股票傭金
代碼
from __future__ import (absolute_import, division, print_function, unicode_literals) import backtrader as bt import backtrader.feeds as btfeeds import backtrader.indicators as btind futures_like = True if futures_like: commission, margin, mult = 2.0, 2000.0, 10.0 else: commission, margin, mult = 0.005, None, 1 class SMACrossOver(bt.Strategy): def log(self, txt, dt=None): ''' Logging function fot this strategy''' dt = dt or self.datas[0].datetime.date(0) print('%s, %s' % (dt.isoformat(), txt)) def notify(self, order): if order.status in [order.Submitted, order.Accepted]: # Buy/Sell order submitted/accepted to/by broker - Nothing to do return # Check if an order has been completed # Attention: broker could reject order if not enougth cash if order.status in [order.Completed, order.Canceled, order.Margin]: if order.isbuy(): self.log( 'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.buyprice = order.executed.price self.buycomm = order.executed.comm self.opsize = order.executed.size else: # Sell self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) gross_pnl = (order.executed.price - self.buyprice) * \ self.opsize if margin: gross_pnl *= mult net_pnl = gross_pnl - self.buycomm - order.executed.comm self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (gross_pnl, net_pnl)) def __init__(self): sma = btind.SMA(self.data) # > 0 crossing up / < 0 crossing down self.buysell_sig = btind.CrossOver(self.data, sma) def next(self): if self.buysell_sig > 0: self.log('BUY CREATE, %.2f' % self.data.close[0]) self.buy() # keep order ref to avoid 2nd orders elif self.position and self.buysell_sig < 0: self.log('SELL CREATE, %.2f' % self.data.close[0]) self.sell() if __name__ == '__main__': # Create a cerebro entity cerebro = bt.Cerebro() # Add a strategy cerebro.addstrategy(SMACrossOver) # Create a Data Feed datapath = ('../datas/2006-day-001.txt') data = bt.feeds.BacktraderCSVData(dataname=datapath) # Add the Data Feed to Cerebro cerebro.adddata(data) # set commission scheme -- CHANGE HERE TO PLAY cerebro.broker.setcommission( commission=commission, margin=margin, mult=mult) # Run over everything cerebro.run() # Plot the result cerebro.plot()