标签:
国内程序化交易技术的爆发式发展几乎就是起源于上期技术公司基于CTP柜台推出了交易API,使得用户可以随意开发自己的交易软件直接连接到交易柜台上进行交易,同时CTP API的设计模式也成为了许多其他柜台上交易API的设计标准,本人已知的类CTP交易API包括:
所以这个教程系列选择从类CTP交易API中的LTS API开始来介绍API的Python封装方法,真正掌握了以后想要做其他类型API(比如恒生的T2)的封装也只是大同小异而已。
通常当用户从网上下载API的压缩包,解压后会看到以下的文件:
找不到压缩包的读者可以在这里直接看https://github.com/vnpy/vnpy/tree/master/vn.lts/ltsapi。
.dll、.lib、.so文件都是编译好的二进制文件,无法打开,所以从用户角度我们只需关注.h文件中的内容。对于不同的API而言,.h文件的前缀可能有所区别,如LTS是SecurityFtdc,CTP是ThostFtdc,下面分别介绍这4个.h文件。
该文件中包含了对API中用到的常量的定义,如以下代码定义了一个产品类型常量对应的字符:
1 1 #define SECURITY_FTDC_PC_Futures ‘1‘
以及类型的定义,如以下代码定义了产品名称类型是一个长度为21个字符的字符串:
1 typedef char TSecurityFtdcProductNameType[21];
该文件中包含了API中用到的结构体的定义,如以下代码定义了交易所这个结构体的构成:
1 ///交易所 2 struct CSecurityFtdcExchangeField 3 { 4 ///交易所代码 5 TSecurityFtdcExchangeIDType ExchangeID; 6 ///交易所名称 7 TSecurityFtdcExchangeNameType ExchangeName; 8 ///交易所属性 9 TSecurityFtdcExchangePropertyType ExchangeProperty; 10 };
MdApi.h例如TSecurityFtdcExchangeIDType这个类型的定义,可以在ApiDataType.h中找到。
该文件中包含了API中的行情相关组件的定义,文件通常开头会有一段这样的内容:
1 #if !defined(SECURITY_FTDCMDAPI_H) 2 #define SECURITY_FTDCMDAPI_H 3 4 #if _MSC_VER > 1000 5 #pragma once 6 #endif // _MSC_VER > 1000 7 8 #include "SecurityFtdcUserApiStruct.h" 9 10 #if defined(ISLIB) && defined(WIN32) 11 #ifdef LIB_MD_API_EXPORT 12 #define MD_API_EXPORT __declspec(dllexport) 13 #else 14 #define MD_API_EXPORT __declspec(dllimport) 15 #endif 16 #else 17 #define MD_API_EXPORT 18 #endif
然后是两个类CSecurityFtdcMdSpi和CSecurityFtdcMdApi的定义。这些内容主要是一些和操作系统、编译环境相关的定义,一般用户忽略就好(作者其实也不太懂…)。
MdSpi类中包含了行情功能相关的回调函数接口,什么是回调函数呢?简单来说就是由于柜台端向用户端发送信息后才会被系统自动调用的函数(非用户主动调用),对应的主动函数会在下面介绍。CSecurityFtdcMdSpi大概看起来是这么个样子:
1 class CSecurityFtdcMdSpi 2 { 3 public: 4 ...... 5 6 ///登录请求响应 7 virtual void OnRspUserLogin(CSecurityFtdcRspUserLoginField *pRspUserLogin, CSecurityFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {}; 8 9 ...... 10 11 ///深度行情通知 12 virtual void OnRtnDepthMarketData(CSecurityFtdcDepthMarketDataField *pDepthMarketData) {}; 13 };
回调函数都是以On开头。……省略了部分代码。从上面的代码中可以注意到:
MdApi类中包含了行情功能相关的主动函数结构,顾名思义,主动函数指的是由用户负责进行调用的函数,用于向柜台端发送各种请求和指令,大概样子如下:
1 class MD_API_EXPORT CSecurityFtdcMdApi 2 { 3 public: 4 ///创建MdApi 5 ///@param pszFlowPath 存贮订阅信息文件的目录,默认为当前目录 6 ///@return 创建出的UserApi 7 ///modify for udp marketdata 8 static CSecurityFtdcMdApi *CreateFtdcMdApi(const char *pszFlowPath = ""); 9 10 ...... 11 12 ///注册回调接口 13 ///@param pSpi 派生自回调接口类的实例 14 virtual void RegisterSpi(CSecurityFtdcMdSpi *pSpi) = 0; 15 16 ///订阅行情。 17 ///@param ppInstrumentID 合约ID 18 ///@param nCount 要订阅/退订行情的合约个数 19 ///@remark 20 virtual int SubscribeMarketData(char *ppInstrumentID[], int nCount, char* pExchageID) = 0; 21 22 ...... 23 24 ///用户登录请求 25 virtual int ReqUserLogin(CSecurityFtdcReqUserLoginField *pReqUserLoginField, int nRequestID) = 0; 26 27 ...... 28 };
MdApi对象不应该直接创建,而应该通过调用类的静态方法CreateFtdcMdApi创建,传入参数为你希望保存API的通讯用的.con文件的目录(可以选择留空,则.con文件会被放在程序所在的文件夹下)。以上代码中,需要注意的重点包括:
该文件中包含了API中的交易相关组件的定义,文件同样以一段看不懂的定义开头,然后包含了两个类CSecurityFtdcTraderSpi和CSecurityFtdcTraderApi,这两个类和MdApi中的两个类在结构上非常接近,区别仅仅在于类包含的方法函数上。
1 class CSecurityFtdcTraderSpi 2 { 3 public: 4 ///当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。 5 virtual void OnFrontConnected(){}; 6 7 ... 8 9 ///错误应答 10 virtual void OnRspError(CSecurityFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {}; 11 12 ///登录请求响应 13 virtual void OnRspUserLogin(CSecurityFtdcRspUserLoginField *pRspUserLogin, CSecurityFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {}; 14 15 ... 16 17 ///报单通知 18 virtual void OnRtnOrder(CSecurityFtdcOrderField *pOrder) {}; 19 20 ... 21 22 ///报单录入错误回报 23 virtual void OnErrRtnOrderInsert(CSecurityFtdcInputOrderField *pInputOrder, CSecurityFtdcRspInfoField *pRspInfo) {}; 24 25 ... 26 };
以On…开头,这种回调函数通常是返回API连接相关的信息内容,与业务逻辑无关,返回值(即回调函数的参数)通常为空或是简单的整数类型。Spi(包括MdSpi和TraderSpi)类的回调函数基本上可以分为以下四种:
1 class TRADER_API_EXPORT CSecurityFtdcTraderApi 2 { 3 public: 4 ///创建TraderApi 5 ///@param pszFlowPath 存贮订阅信息文件的目录,默认为当前目录 6 ///@return 创建出的UserApi 7 static CSecurityFtdcTraderApi *CreateFtdcTraderApi(const char *pszFlowPath = ""); 8 9 ... 10 11 ///初始化 12 ///@remark 初始化运行环境,只有调用后,接口才开始工作 13 virtual void Init() = 0; 14 15 ... 16 17 ///用户登录请求 18 virtual int ReqUserLogin(CSecurityFtdcReqUserLoginField *pReqUserLoginField, int nRequestID) = 0; 19 20 ... 21 };
Create…,类的静态方法,用于创建API对象,传入参数是用来保存API通讯.con文件的文件夹路径。Api类包括的主动函数通常分为以下三种:
简单介绍一下MdApi和TraderApi的一般工作流程,这里不会包含太多细节,仅仅是让读者有一个概念。
第一篇教程到这里已经接近结束了,如果你是一个没有任何交易API开发经验的读者,并且坚持看了下来,此时你心中很可能有这么个想法:我X,API开发这么复杂???!!!
相信我,这是人之常情(某些读者如果觉得很好理解那作者真是佩服你了),作者刚开始的时候大概在CTP API的头文件和网上的教程资料、示例中纠结了3个多月而不得入门,当时也没有任何C++的开发经验(我是金融工程出生,大学里编程只学了VBA和Matlab,还几乎都是些算法方面的内容),边学语言边研究怎么开发,真心痛苦。
在这里,我想告诉读者的一个好消息是:还剩两篇教程,我们基本就可以和C++ say goodbye,进入Python灵活快速开发的世界了。同时对于绝大部分不打算自己去封装API的读者,这三篇文章可以走马观花的过一遍,不会影响任何你未来对于vn.py框架的使用。
当然,对于有恒心和毅力的读者,100%自己掌握API的封装技术是一项绝对值得投入时间和精力的事情。在很多人的观念中Python并不适合用来开发低延迟的交易平台,这里作者可以用亲身经验告诉你:那只是在纯用Python的情况下。作为一门胶水语言,Python最大的特点之一就是易于通过混合编程来进行拓展,用户可以在真正需要优化的地方进行最深度的定制优化,把自己有限的时间、精力花在刀刃上。在交易API层面,可以定制的地方包括C++层面的数据结构改变、数据预处理、回调函数传递顺序调整等等诸多的优化,这些只有在你完全掌握API的封装后才能办得到。
[转载]Python量化交易平台开发教程系列1-类CTP交易API的工作原理
标签:
原文地址:http://www.cnblogs.com/MMonica/p/5188219.html