Reference: traders using Python
【Preface: Starting with this tutorial, all development will take place in a Python environment (thank goodbye to C++).
Introduction of the underlying interface
1、Usually, the structure of a trading process consists of the following three parts:
- The underlying interface: responsible for docking quotations and trading APIs, pushing data to the system core, and sending instructions (order, data request, etc.)
- Middle-level engine: Used to integrate components (including underlying interfaces, database interfaces, etc.) into an object for top-level UI calls
- Top level GUI: the active function used to display data and invoke the middle level engine to realize the specific functions.
2、The above picture shows the architecture of an open source trading platform AlgoTrader abroad:
- The Adapters on both sides represents the underlying interface (left market data, right trading).
- The red cylinder includes the mid-level engine architecture, the event-driven side uses the Esper Complex Event Processing (CEP) engine, while built-in some commonly used functional engines, such as option pricing engine, foreign exchange hedging module, portfolio management module, and so on.
- Trategy 1 and 2 above represent top-level applications (algorithmic strategies, GUI interfaces, etc.) that call the functionality of the middle-level engine to achieve the business users need
3、vn.pyComparison with AlgoTrader:
Here is a simple comparison of two projects.
vn.pyAdvantage:
- Language is easy to use: Java language is more verbose than Python.
- The architecture is simple: Java’s programming philosophy (pure object-oriented, heavily framed, etc.) is more cumbersome than Python’s (life is short, our goal is to solve problems)
- Event Driven Engine: AlgoTrader’s Esper engine, though powerful and complex to use, is completely unnecessary for most of the domestic quantization business
- Localization: vn.py is designed entirely for the Chinese market, and its functional design is more in line with Chinese usage, while AlgoTrader is designed for the European and American markets.
AlgoTraderAdvantage:
- Static Language: Java can be static checked at development time, while its relatively low flexibility makes it more suitable for large teams (i.e. each member does not necessarily have a good understanding of the project as a whole)
- Foreign Interface: There are a large number of foreign brokers and market providers interface, if the user mainly do the European and American markets can be used out of the box.
- Maturity: AlgoTrader has been around for six years since its release in 2009, with a significant number of institutional customers
Similarities between the two projects:
- At first, the author developed the project in order to trade some option strategy.
- The overall frame design is similar (bottom interface, middle engine, top level GUI).
- It is very convenient to develop automatic trading strategy.
- Both are open source projects, currently hosted on Github, and users can customize the functionality to suit their needs
- FineUsed in high frequency trading (millisecond delay),Not applicable to UHF Trading (microsecond delay).
4、Tutorials
This tutorial will cover the development of the underlying interface, followed by several articles on the middle engine and various top-level GUI components. The relevant examples are based on the LTS interface DEMO in vn.demo, which is published in: https://github.com/vnpy/vnpY/tree/master/vn.demo/ltsdemo
Two, bottom interface docking
Through the previous tutorial, we have acquired the same Python package API as the native C++ API. Usually, in order to get a API pair into our program, we need the following two steps:
- APIData received by callback functionPush to the middle engine of the program and wait for processing.
- Simplify the encapsulation of API’s active function to facilitate the middle level engine call.
vn.ltsAPI interface in the use of the user inheritance needs to implement the callback function corresponding to the specific functions, the following content to quote interface as an example.
######################################################################## class DemoMdApi(MdApi): """ DemoMarket API packageAfter encapsulation, all data is automatically pushed to the event-driven engine, which is responsible for pushing to the callback functions that monitor the event.The active functions of user exposure include:Landing loginSubscription contract subsCribe""" #---------------------------------------------------------------------- def __init__(self, eventEngine): """ APIInitialization function for objects""" super(DemoMdApi, self).__init__() # Event engine, all data is pushed to it, and then distributed by event engine.Self.__eventEngine= eventEngine # The request number is managed by API.Self.__reqid= 0 # The following variables are used to achieve automatic login after connection and reconnection.Self.__userid= '' self.__password = '' self.__brokerid = '' # The following collection is used for contracts that have been subscribed before rejoining automatic subscription. Use collections to prevent duplication.Self.__setSubscribed= set() # Initialize the. con file to save the directory mdconnection, note that this directory must already exist, otherwise it will report an errorSelf.createFtdcMdApi (os.getcwd ())+ '\\mdconnection\\')
- DemoMdApiThe class inherits from the MdApi class and implements the specific function of the callback function.
- When creating an object of DemoMdApi, the user needs the parameter to be imported is the event driven engine object eventEngine.
- Each time the API’s active function is called, a reqid parameter is passed in as the unique identifier of the request. In most cases, we don’t care about the identity of each request, so we choose to give this parameter to the DemoMdApi object to maintain, and each time the active function is called, it is self-contained.Add 1.
- We save the username, password, and broker number in the object of DemoMdApi for the automatic login function after the pre-machine connection is completed, and the operation related to disconnection and reconnection.
- __setSubscribedThe corresponding is a Python collection that holds the contracts we subscribed to through the subscription function and automatically subscribes after disconnection and reconnection. Set instead of list is chosen to ensure contract uniqueness and avoid duplicate subscriptions (although duplicate subscriptions have no effect).
- While creating the object DemoMdApi object, the createFtdcMdApi is automatically called to initialize the connection interface, and the. con communication file is saved using the mdconnection folder in the current directory.
1、callback
#---------------------------------------------------------------------- def onFrontConnected(self): """Server connection""" event = Event(type_=EVENT_LOG) event.dict_['log'] = u'Market server connection successfully' self.__eventEngine.put(event) # If the user has entered the user name and so on, then try to connect automatically.if self.__userid: req = {} req['UserID'] = self.__userid req['Password'] = self.__password req['BrokerID'] = self.__brokerid self.__reqid = self.__reqid + 1 self.reqUserLogin(req, self.__reqid) ... #---------------------------------------------------------------------- def onRspUserLogin(self, data, error, n, last): """Return on landing""" event = Event(type_=EVENT_LOG) if error['ErrorID'] == 0: log = u'Market server login successfully' else: log = u'Landing returns, error code:' + unicode(error['ErrorID']) + u',' + u'Error message:' + error['ErrorMsg'].decode('gbk') event.dict_['log'] = log self.__eventEngine.put(event) # Automatically subscribe to a previously subscribed contract after reconnection.if self.__setSubscribed: for instrument in self.__setSubscribed: self.subscribe(instrument[0], instrument[1]) ... #---------------------------------------------------------------------- def onRtnDepthMarketData(self, data): """Market push""" # When a market push is received, it triggers both regular market events and specific contract market events for different types of monitoringOrdinary market eventsEvent1= Event(type_=EVENT_MARKETDATA) event1.dict_['data'] = data self.__eventEngine.put(event1) # Specific contract market eventsEvent2= Event(type_=(EVENT_MARKETDATA_CONTRACT+data['InstrumentID'])) event2.dict_['data'] = data self.__eventEngine.put(event2) ...
- After receiving the data push from the API through the callback function, different types of event Event objects (from the event-driven engine module) are created, and the data to be pushed is stored in the event object’s data dictionary dict_and pushed to the event-driven engine for processing.
- In the data received by the callback function, data and error correspond to a dictionary that holds the main data (such as quotations) and error information, n is the request number corresponding to the callback function (that is, reqid when the active function is called), last is a Boolean value, representing whether or not it is the last of the callReturn information.
- We are interested in the data dictionary, so we choose to push the whole thing in the event.
- The error dictionary should check immediately after each receipt to see if it contains an error message (because it will be pushed even if no error occurs) and automatically save it as a log event (displayed through the log monitoring control).
- When the server connection is complete (onFrontConnected), check to see if login information such as user name has been filled in, and if so, log in automatically (see the example in the next active function).
- Once the login is complete (onRspUserLogin), automatically subscribe to a contract previously subscribed to in u setSubscribed.
- On receipt of the onRtn Depth Market Data, we chose to create two kinds of events, one is a regular quotation event (usually applicable to components such as the market monitoring GUI that focus on all market pushes), and the other is a specific contract quotation event (usually applicable to calculations).Law only focuses on the components of specific contract quotes.
- When we call an active function that will return information, we need to pass in the number of this request. At this point, we first add u reqid by 1, and then pass it into the active function as a parameter.
2、Active function
#---------------------------------------------------------------------- def login(self, address, userid, password, brokerid): """Connect server""" self.__userid = userid self.__password = password self.__brokerid = brokerid # Register server addressSelf.registerFront (address)The initialization connection will succeed in calling onFrontConnected.Self.init()Be---------------------------------------------------------------------- def subscribe(self, instrumentid, exchangeid): """Subscribe contract""" req = {} req['InstrumentID'] = instrumentid req['ExchangeID'] = exchangeid self.subscribeMarketData(req) instrument = (instrumentid, exchangeid) self.__setSubscribed.add(instrument)
- The active function encapsulates only two functions.,Login login and subscription contract subscribe。Let’s assume that we don’t normally do things like logout (killing the process directly) and withdrawal (not necessarily) and that we can encapsulate the corresponding functions if needed.
- For the login function, the incoming four parameters include the server’s prefix address, userid, password, and brokerage code brokerid. After the function is called, we first userid, password andBrokerid is saved, then register the server address registerFront and initialize the connection init.When the connection is complete, the onFrontConnected callback function is called automatically, and what happens then refers to the callback function workflow in the previous paragraph。
- LTSWhen the API subscribes to the quotation, it needs to pass in the contract code and the exchange where the contract is located (because there are two stock exchanges with the same code), while the CTP API does not have this problem with futures, just passing in the contract code. After sending the subscription request, the subscription request is saved in theIn the setSubscribed collection, you can automatically resubscribe when a broken line is reconnected.
Three. Summary
In the development of trading procedures, all the API docking principles are very similar, in addition to the CTP-like API, the domestic Hang Seng interface, FIX engine interface and so on can also follow the above principles for docking design.
The example in this article is the quotation interface. Because the transaction interface contains more callback functions and active functions, it is more complicated in design. Interested readers suggest to read the source code of demo directly. Related questions can be asked in the vn.py framework exchange group (group number: 262656087)..