Introduction: the multi contract operation of some quantitative software in the market is complex and even not supported, while the backtrader real offer multi contract operation is very simple. This article introduces this
Backtrader has won the favor of quantitative traders because of its simple and powerful features. However, it is a pity that there has been no production level interface for China ctp futures firm trading. The good news is that today, we officially released backtrader's real offer trading interface for China's futures market, enabling users to conduct real offer trading.
We also released the sweeper monk Backtrader awesome Tutorial Series II: Advanced chapter on quantitative backtesting and real offer trading, which will explain in detail the use of this interface.
This interface supports the separation of long and short positions in the same contract of China Futures and the automatic closing of today and yesterday. Support multi contract operation and multi cycle mixing, including resample and replay dynamic synthesis bar functions scientific and accurate resample synthesis bar
The core policy logic in next is almost the same as the back test code.
Next, let's see how to write the multi contract policy code based on this interface. The following code obtains the tick from the remote ctp quotation server, and then the resample is synthesized into a 10 second bar. In the strategy, when the target price crosses the moving average, the target order is issued to make the target position reach the set long position target value, and when the target price crosses the moving average, the target order is issued to make the target position reach the set short position target value. self.live_data is the flag of whether to enter the real offer.
It can be seen that compared with other open source products on the market, backtrader's real offer trading code is concise and clear.
# Disclaimer: the author does not guarantee the correctness of this procedure, nor has it been strictly tested. Do not make a firm offer on this basis, and bear all the consequences import backtrader as bt from backtrader_ctpcn_api.ctpstore import CTPStore from backtrader_ctpcn_api.ctputil import CtpStrategy from time import sleep import pandas as pd import datetime import pytz ####################################################################### # Policy class ######################## class SmaCross(CtpStrategy): params = dict( smaperiod=2, store=None, ) # Event method automatically recalled by ctpbee def on_order(self, order)-> None: """ Declaration return """ # print('in strategy return on_order') def on_trade(self, trade) -> None: """ Transaction return """ # print('transaction return on\u trade', trade, "\n") def on_position(self, position) -> None: """ Handle position returns """ # print('return on_position',position) def on_account(self, account) -> None: """ Process account information return """ # print('account information return on_account',account) def on_contract(self, contract)-> None: """ Processing pushed contract information """ # print('Contract information return on_account',contract) def on_tick(self, tick): pass def on_realtime(self): """ 1s Interface triggered once """ # The following are backtrader's own event methods def __init__(self): # ################ # Hook up with ctpbee ################### # Get the reference of ctpbeeapi self.beeapi = self.p.store.main_ctpbee_api # Pass the policy to the attribute strategy of beeapi object self.beeapi.set_strategy(self) self.beecenter = self.beeapi.app.center # d. whether the firm offer has been entered self.live_data = {d: False for d in self.datas} # Moving Average move_average = {d: bt.ind.MovingAverageSimple(d,period=self.params.smaperiod) for d in self.datas} # Moving average cross signal index of target d self.crossover={d: bt.ind.CrossOver(d, move_average[d]) for d in self.datas} def next(self): print('=========================================') print('in next') for d in self.datas: if not self.live_data[d]: return # It is not real-time data (still in the process of historical data filling) and does not enter the order logic self.cancel_all() # Cancel all outstanding orders for d in self.datas: print(d._name, d.datetime.datetime(0), 'o h l c ', d.open[0],d.high[0],d.low[0], d.close[0], ' vol ', d.volume[0], 'openinterest', d.openinterest[0]) # Subject position information pos = self.beecenter.get_position(d._dataname) # Long warehouse quantity long_pos = pos.long_volume if pos else 0 # Short warehouse quantity, positive short_pos = pos.short_volume if pos else 0 print(d._name, 'long pos', long_pos, 'short pos', short_pos) targetsize = 1 # Target quantity print(d._name, 'crossover',self.crossover[d][0]) if self.crossover[d][0]==1: # kdj print(d._name, 'Golden fork, to make the net long position reach the target quantity', targetsize) olist = self.target_size(d.close[0], targetsize, d,buy_delta=5, sell_delta=5) print('olist',olist) elif self.crossover[d][0]== -1: # Dead fork print(d._name, 'Dead cross, to make the net short position reach the target quantity', targetsize) # Note that the target size parameter is set to a negative number olist = self.target_size(d.close[0], - targetsize, d, buy_delta=5, sell_delta=5) print('olist',olist) def notify_data(self, data, status, *args, **kwargs): msg = f'Data status: {data._getstatusname(status)}' print(data._name, msg) # Set the sign of entering the firm offer if data._getstatusname(status) == 'LIVE': self.live_data[data] = True print() print('**********************************************') print(data._name,'Enter the firm offer') print('**********************************************') else: self.live_data[data] = False #####End of policy############################################## ########################################################### # Main program start ################## if __name__ == '__main__': # http://122.51.136.165:50080/detail.html View the status of quotation server ctp_setting = { "CONNECT_INFO": { "userid": "????", # investorId in your account registered with simnow "password": "????", # Your account password registered in simnow "brokerid": "9999", "md_address": "tcp://180.168.146.187:10211 ", \ "td_address": "tcp://180.168.146.187:10201 ", \transaction front, transaction server address "product_info": "", "appid": "simnow_client_test", "auth_code": "0000000000000000" }, "INTERFACE": "ctp", # ctp/ctp_se loads the interface name. Currently, CTP production and CTP are supported_ Se penetrating verification interface "TD_FUNC": True, # Enable transaction function "MD_FUNC": True, # Open quotation reception "XMIN": [], # ctpbee generates bar for several minutes, for example [1] "TODAY_EXCHANGE":["SHFE","INE"], # Need to support Pingjin exchange code list "CLOSE_PATTERN": "today", # For the exchanges that support Pingjin, the priority of Pingjin or pingyesterday shall be designated } tz = pytz.timezone('Asia/Shanghai') cerebro = bt.Cerebro() # Create ctp store store = CTPStore(ctp_setting) # Define data # ag2206.SHFE is silver futures ap2210 ZCE Zhengshang Institute a2207 DCE China Financial Futures Exchange (IF2205.CFFEX) # vnpy supports five of China's eight major compliance exchanges, including Shanghai Futures Exchange, Dalian futures exchange, Zhengzhou Futures Exchange, CICC and Energy Research Institute. data0_name = 'ag2206.SHFE' # ag2206 is the silver futures of the previous exchange data1_name = 'ag2207.SHFE' # Path of backfill data file csvpath = 'E:/myquant/backtrader_ctpcn/' data0 = store.getdata(dataname=data0_name, timeframe=bt.TimeFrame.Ticks, # Where does the backfill data come from? If you do not want to backfill, set backfill_from=None backfill_from = load_hist_ticks(csvpath+'tickhistory.csv'), qcheck = 5, # Timeout waiting for remote tick sessionstart = datetime.time(21,00,00), # Opening time sessionend = datetime.time(15,00,00), # Closing time tzinput = tz, tz=tz ) # cerebro.adddata(data0) # Resample the bar required for tick synthesis. Note the setting of name cerebro.resampledata(data0, timeframe=bt.TimeFrame.Seconds, compression=10, name=data0_name+'10s') # Multi contract data1 = store.getdata(dataname=data1_name, timeframe=bt.TimeFrame.Ticks, # Where does the backfill data come from? If you do not want to backfill, set backfill_from=None backfill_from = load_hist_ticks(csvpath+'tickhistory1.csv'), qcheck = 5, # Timeout waiting for remote tick sessionstart = datetime.time(21,00,00), # Opening time sessionend = datetime.time(15,00,00), # Closing time tzinput = tz, tz=tz ) # cerebro.adddata(data1) # To playback the bar required for tick synthesis, pay attention to setting the name cerebro.resampledata(data1, timeframe=bt.TimeFrame.Seconds, compression=10, name=data1_name+'10s') # Injection policy, pay attention to the store parameter settings cerebro.addstrategy(SmaCross, store=store) # When the simulated market is synchronized with the actual offer, is_realtime set to True, otherwise set to False cerebro.run(is_realtime=True, tz=tz)
The following is a video demonstration of backtrader trading through the China Futures real order interface developed by us:
backtrader China Futures real offer interface transaction demonstration