Click here to get the backtrader technical tutorial
==================
Many students asked how to use backtrader for futures backtesting. The following is an example. The difference from ordinary stock backtesting is the way commissions are set.
1 Commission settings
import datetime # For datetime objects
import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind
import pandas as pd
import numpy as np
from datetime import datetime
class Par_SAR(bt.Strategy):
params = (('period', 2), ('af', 0.02), ('afmax', 0.2))
def __init__(self):
pass
if __name__ == '__main__':
# rb 15 min data
brf_min_bar = bt.feeds.GenericCSVData(
dataname='E:/Quant/Backtrader/backtrader-master/China_Market/data/rb_15m.csv',
timeframe=bt.TimeFrame.Minutes,
#fromdate=datetime.datetime(2019, 5, 1),
#todate=datetime.datetime(2020, 6, 1),
nullvalue=0.0,
dtformat=('%Y-%m-%d %H:%M:%S'),
datetime=1,
time=-1,
high=4,
low=5,
open=3,
close=6,
volume=-1,
# openinterest=-1
)
# Add the Data Feed to Cerebro
cerebro.adddata(brf_min_bar)
#cerebro.resampledata(brf_min_bar, timeframe=bt.TimeFrame.Days)
# Add strategy
cerebro.addstrategy(Par_SAR)
# 设置初始资本为100,000
cerebro.broker.setcash(1000000.0)
# 设置期货佣金模式
cerebro.broker.setcommission(commission=0.0001, margin=2800.0, mult=10.0)
cerebro.run()
The above commission settings actually use the built-in commission class CommInfoBase of backtrader, and the parameters that can be set in this class are as follows. Of course, most of the parameters can use the default values, and there is no need to set them. In the above case, only 3 parameters are set, and the others are default values. The leverage in the following parameters may also be related to futures. In addition, this class can also consider borrowing interest.
Params:
commission
(def:0.0
): base commission value in percentage or monetary unitsmult
(def1.0
): multiplier applied to the asset for value/profitmargin
(def:None
): amount of monetary units needed to open/hold an operation. It only applies if the final_stocklike
attribute in the class is set toFalse
automargin
(def:False
): Used by the methodget_margin
to automatically calculate the margin/guarantees needed with the following policy- Use param
margin
if paramautomargin
evaluates toFalse
- Use param
mult
and usemult * price
ifautomargin < 0
- Use param
automargin
and useautomargin * price
ifautomargin > 0
- Use param
commtype
(def:None
): Supported values areCommInfoBase.COMM_PERC
(commission to be understood as %) andCommInfoBase.COMM_FIXED
(commission to be understood as monetary units)
The default value ofNone
is a supported value to retain compatibility with the legacyCommissionInfo
object. Ifcommtype
is set to None, then the following applies:margin
isNone
: Internal_commtype
is set toCOMM_PERC
and_stocklike
is set toTrue
(Operating %-wise with Stocks)margin
is notNone
:_commtype
set toCOMM_FIXED
and_stocklike
set toFalse
(Operating with fixed rount-trip commission with Futures)
If this param is set to something else than None
, then it will be passed to the internal _commtype
attribute and the same will be done with the param stocklike
and the internal attribute _stocklike
stocklike
(def:False
): Indicates if the instrument is Stock-like or Futures-like (see thecommtype
discussion above)percabs
(def:False
): whencommtype
is set to COMM_PERC, whether the parametercommission
has to be understood as XX% or 0.XX
If this param isTrue
: 0.XX If this param isFalse
: XX%interest
(def:0.0
)
If this is non-zero, this is the yearly interest charged for holding a short selling position. This is mostly meant for stock short-selling
The formula:days * price * abs(size) * (interest / 365)
It must be specified in absolute terms: 0.05 -> 5%
Note
the behavior can be changed by overriding the method:_get_credit_interest
interest_long
(def:False
)
Some products like ETFs get charged on interest for short and long positions. If ths isTrue
andinterest
is non-zero the interest will be charged on both directionsleverage
(def:1.0
)
Amount of leverage for the asset with regards to the needed cash
You can derive your own commission class from the commission base class to meet your own requirements. For example, if someone expects that the commission rate depends not only on the transaction amount, but also on the multiplier mult, you can define a new commission class as follows
import backtrader as bt
class CommInfo_Fut_Perc_Mult(bt.CommInfoBase):
params = (
('stocklike', False), # Futures
('commtype', bt.CommInfoBase.COMM_PERC), # Apply % Commission
# ('percabs', False), # pass perc as xx% which is the default
)
def _getcommission(self, size, price, pseudoexec):
return size * price * self.p.commission * self.p.mult
Then, use it like this
comminfo = CommInfo_Fut_Perc_Mult(
commission=0.1, # 0.1%
mult=10,
margin=2000 # Margin is needed for futures-like instruments
)
cerebro.addcommissioninfo(comminfo)
2 Stitched Rolling Contracts
In addition, backtrader also allows rolling splicing of futures contracts with different expiration dates to form continuous futures (The RollOver Data Feed), examples are as follows
import backtrader as bt
cerebro = bt.Cerebro()
data0 = bt.feeds.MyFeed(dataname='Expiry0')
data1 = bt.feeds.MyFeed(dataname='Expiry1')
...
dataN = bt.feeds.MyFeed(dataname='ExpiryN')
drollover = bt.feeds.RollOver(data0, data1, ..., dataN, dataname='MyRoll', **kwargs)
cerebro.adddata(drollover)
cerebro.run()
The RollOver class has two parameters that control how contracts are stitched together, checkdate
and checkcondition
. Detailed description refer to Data Feeds - Rollover - Backtrader