交易网关Gateway

Gateway封装思路

CTP,全称为中国期货市场监控中心交易系统(China Financial Futures Exchange Trading System),是由中国金融期货交易所开发的一套综合性交易系统。该系统不仅支持期货交易,还涵盖了期权、债券等多种金融产品的交易。通过期货公司提供的CTP接口,可实现期货交易。

中金所提供的CTP接口是用C++写成的,接下来看看VNPY是如何将C++接口转化为python接口,又进一步实现系统交互。这些内容封装在了vnpy_ctp包里。

以下是vnpy_ctp的文件目录

初始材料

初始材料为交易所提供的.dll动态链接库和.h头文件,最重要的如下两个:

  • ThostFtdcMdApi.h:C++头文件,包含获取行情相关的指令
  • ThostFtdcTraderApi.h:C++头文件,包含交易相关指令,如报单、撤单

在头文件中,一般只声明函数,没有函数的具体实现,而这些函数的具体实现在动态和静态链接库中。

ThostFtdcMdApi.h中,定义了两个类:

  • CThostFtdcMdApi:完成行情接口的初始化、登录、订阅、接收行情等业务
  • CThostFtdcMdSpi:获取相应业务操作的返回信息
class CThostFtdcMdSpi
{
public:

	virtual void OnFrontConnected(){};

	///@param nReason 
	///        0x1001 
	///        0x1002 
	///        0x2001 
	///        0x2002 
	///        0x2003 
	virtual void OnFrontDisconnected(int nReason){};
		
	@param nTimeLapse 
	virtual void OnHeartBeatWarning(int nTimeLapse){};
	
	virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};

	virtual void OnRspUserLogout(CThostFtdcUserLogoutField *pUserLogout, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};

	virtual void OnRspQryMulticastInstrument(CThostFtdcMulticastInstrumentField *pMulticastInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};

	virtual void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};

	virtual void OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};

	virtual void OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};

	virtual void OnRspSubForQuoteRsp(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};

	virtual void OnRspUnSubForQuoteRsp(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};

	virtual void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData) {};

	virtual void OnRtnForQuoteRsp(CThostFtdcForQuoteRspField *pForQuoteRsp) {};
};

ThostFtdcTraderApi.h中,同样定义了两个类:

  • CThostFtdcTraderApi:完成交易接口的初始化、登录、确认计算结果,查询合约,查询资金,报单,撤单等业务操作
  • CThostFtdcTraderSpi:获取相应操作的返回信息

VNPY的Python封装

为了实现C++到python的接口转换,VNPY分行情接口和交易接口,重写了两个类:

  • MdApi
    • 定义在vnctpmd
    • 继承自CThostFtdcMdSpi,但成员变量包含CThostFtdcMdApi,以及一个负责处理回调数据的任务队列task_queue
  • TdApi
    • 定义在vnctptd
    • 类似MdApi

然后,上述C++对象通过pybind11模块编译为python的MdApi类和TdApi,见文件vnctpmd.cp310-win_amd64.pydvnctptd.cp310-win_amd64.pyd。到此,就完成了C++下CTP的python封装。

剩下还需要实现交易接口每次触发回调事件的时候,通知本地的事件引擎,与本地vntrader实现交互。这是交易网关BaseGateway的主要作用。

参考https://blog.51cto.com/u_15067246/4004491

交易网关模板BaseGateway

BaseGateway提供了一个交易网关交互的模板,有两块内容:

  • 一是实现了与VNPY事件引擎交互的函数
  • 二是声明了与网关交互的函数,具体的实现根据交易接口的不同,让用户自定义

所以,BaseGateway是一个高度抽象、高度可定制的模板,几乎所有其他的交易网关使用都可以继承自它。

与事件引擎交互

class BaseGateway(ABC):

    # Default name for the gateway.
    default_name: str = ""

    # Fields required in setting dict for connect function.
    default_setting: Dict[str, Any] = {}

    # Exchanges supported in the gateway.
    exchanges: List[Exchange] = []

    def __init__(self, event_engine: EventEngine, gateway_name: str) -> None:
        """"""
        self.event_engine: EventEngine = event_engine
        self.gateway_name: str = gateway_name

首先是初始化部分,简单地传入事件引擎实例和交易网关。

    def on_event(self, type: str, data: Any = None) -> None:
        """
        General event push.
        """
        event: Event = Event(type, data)
        self.event_engine.put(event)

on_event是回调函数的同一接口,它将回调事件封装为Event(type, data),再将事件推送到事件引擎中。在实盘时,需要作出反馈的事件如下:

  • 实时Tick数据
    • 每拿到一笔Tick数据,就将一个EVENT_TICK推送到事件引擎
    def on_tick(self, tick: TickData) -> None:
        """
        Tick event push.
        Tick event of a specific vt_symbol is also pushed.
        """
        self.on_event(EVENT_TICK, tick)
        self.on_event(EVENT_TICK + tick.vt_symbol, tick)
    
    • 这里可以回顾CtaEngine中的process_tick_event函数,此函数需要传入的参数event: Event来自于这里。
  • 成交记录
    • 每笔成交都被推送
    def on_trade(self, trade: TradeData) -> None:
        """
        Trade event push.
        Trade event of a specific vt_symbol is also pushed.
        """
        self.on_event(EVENT_TRADE, trade)
        self.on_event(EVENT_TRADE + trade.vt_symbol, trade)
    
  • 订单记录
    • 每次订单提交都被推送
    def on_order(self, order: OrderData) -> None:
        """
        Order event push.
        Order event of a specific vt_orderid is also pushed.
        """
        self.on_event(EVENT_ORDER, order)
        self.on_event(EVENT_ORDER + order.vt_orderid, order)
    
  • 持仓查询
    • 持仓查询事件
    def on_position(self, position: PositionData) -> None:
        """
        Position event push.
        Position event of a specific vt_symbol is also pushed.
        """
        self.on_event(EVENT_POSITION, position)
        self.on_event(EVENT_POSITION + position.vt_symbol, position)
    
  • 账户余额查询
    • 账户余额查询事件
    def on_account(self, account: AccountData) -> None:
        """
        Account event push.
        Account event of a specific vt_accountid is also pushed.
        """
        self.on_event(EVENT_ACCOUNT, account)
        self.on_event(EVENT_ACCOUNT + account.vt_accountid, account)
    
  • 报价查询
    • 报价查询事件
    def on_quote(self, quote: QuoteData) -> None:
        """
        Quote event push.
        Quote event of a specific vt_symbol is also pushed.
        """
        self.on_event(EVENT_QUOTE, quote)
        self.on_event(EVENT_QUOTE + quote.vt_symbol, quote)
    
  • 其他

    def on_log(self, log: LogData) -> None:
        """
        Log event push.
        """
        self.on_event(EVENT_LOG, log)
    
    def on_contract(self, contract: ContractData) -> None:
        """
        Contract event push.
        """
        self.on_event(EVENT_CONTRACT, contract)
    
    def write_log(self, msg: str) -> None:
        """
        Write a log event from gateway.
        """
        log: LogData = LogData(msg=msg, gateway_name=self.gateway_name)
        self.on_log(log)
    

与交易网关交互

具体的实现要因交易网关不同而不同。基本功能模板如下:

  • 连接交易网关
    @abstractmethod
    def connect(self, setting: dict) -> None:
    
        pass
    
  • 关闭与交易网关的连接
    @abstractmethod
    def close(self) -> None:
        """
        Close gateway connection.
        """
        pass
    
  • 订阅实时行情
    @abstractmethod
    def subscribe(self, req: SubscribeRequest) -> None:
        """
        Subscribe tick data update.
        """
        pass
    
  • 提交订单
    @abstractmethod
    def send_order(self, req: OrderRequest) -> str:

        pass
  • 取消订单
    @abstractmethod
    def cancel_order(self, req: CancelRequest) -> None:
        """
        Cancel an existing order.
        implementation should finish the tasks blow:
        * send request to server
        """
        pass
    
  • 提交报价
    def send_quote(self, req: QuoteRequest) -> str:
    
        return ""
    
  • 取消报价
    def cancel_quote(self, req: CancelRequest) -> None:
        """
        Cancel an existing quote.
        implementation should finish the tasks blow:
        * send request to server
        """
        pass
    
  • 查询:账户余额、持仓、历史行情
    @abstractmethod
    def query_account(self) -> None:
        """
        Query account balance.
        """
        pass
    
    @abstractmethod
    def query_position(self) -> None:
        """
        Query holding positions.
        """
        pass
    
    def query_history(self, req: HistoryRequest) -> List[BarData]:
        """
        Query bar history data.
        """
        pass
    
    def get_default_setting(self) -> Dict[str, Any]:
        """
        Return default setting dict.
        """
        return self.default_setting
    

VNPY数据流总结

posted @   superzzh  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示