ctp期货量化 (ctp期货程序化)

如果用一句话来概括CTP API的功能,那就是“ 将客户端的请求信息发往服务端,接收到服务端返回的信息时执行客户端开发者自定义的业务处理逻辑 ”。

CTP API的开发语言是C++,属于强类型语言,要求所有的类型在编译时都是确定的。就有两个问题需要解决:其一, API的功能实现逻辑 需要封装到库文件里,因为暴露出来既不安全,对客户端开发者也不友好;其二,在编译好的库文件中,要能够调用进行客户端开发时才实现的 业务处理逻辑

CTP API利用了虚函数沿着类的继承关系链向下穿透的性质,暴露两个基类CThostFtdcTraderApCThostFtdcTraderSpi的声明,实现了API的功能模块与客户端自定义业务处理模块的隔离。

对于 API的功能模块 而言,客户端自定义业务处理模块的类型是 CThostFtdcTraderSpi ,使用这个类型可以调用到业务处理逻辑。相应的,对于 客户端自定义业务处理模块 而言,API的功能模块的类型是 CThostFtdcTraderApi ,使用这个类型可以调用API的功能。

接下来,我们就来了解一下,什么是虚函数,以及虚函数是如何向下穿透的。

声明前加上virtual关键字的函数就是 虚函数 虚函数的作用是,使用父类类型的指针调用函数时,能够向下穿透到子类中的实现逻辑 。当然,只有是先将子类实例化,然后将子类指针转成父类类型的指针,父类类型指针调用虚函数才能穿透到子类的实现。如果是父类直接实例化,用这个父类实例的指针调用虚函数,执行的仍然是父类中的实现。

如果虚函数在声明时被赋值为0,就变成了 纯虚函数 ,赋值为0是因为此处不需要实现。包含纯虚函数的类是 纯虚类 ,通常被作为接口使用,所以也被称为 接口类 。因为纯虚类中声明的纯虚函数并没有被实现,所以纯虚类不能直接实例化,因此也被称为 抽象类

必须由一个继承了纯虚类的子类,将所有纯虚函数都实现,才能正常使用。 首先将实现了所有纯虚函数的子类实例化,再将子类的指针转成基类类型的指针,使用该基类类型的指针就可以调用子类中实现的功能

综上所示, 基类通过(纯)虚函数实现了封装子类的目的 。不同模块之间交互时,相互之间只需要使用基类类型的指针就可以调用需要的功能,不需要关心具体代码,实现了不同模块间代码的隔离。

1. CThostFtdcTraderApi

以CTP的交易API为例, CThostFtdcTraderApi 就是被暴露出来的基类声明。库文件(dll/so)中存在一个实现了所有纯虚函数功能的子类,客户端开发者不知道这个子类的具体内容,但是可以通过一个基类类型 CThostFtdcTraderApi 的指针调用这个子类中实现的API功能。

初始化阶段,首先使用 CreateFtdcTraderApi 获得的一个 CThostFtdcTraderApi 类型的指针。

可以想象生成这个指针的过程:首先,将库文件中实现API功能模块的子类实例化,该子类继承基类 CThostFtdcTraderApi 并且实现了所有纯虚函数的功能;然后,将该子类指针转成基类类型 CThostFtdcTraderApi 的指针,该基类类型指针作为结果被返回。客户端可以使用这个返回值调用API功能模块。

基类 CreateFtdcTraderApi 声明的虚函数,确保基类类型的指针可以穿透到子类的功能代码并执行。例如,客户端认证请求函数在 CThostFtdcTraderApi 类中的声明为:

ctp量化交易编程,ctp期货程序化交易

这是一个纯虚函数。如下代码,在 OnFrontConnected 时,用 m_pTraderApi 执行 ReqAuthenticate 将客户端认证请求发往服务端。其中, m_pTraderApi 是一个 CThostFtdcTraderApi 类型的指针,在初始化阶段通过 CreateFtdcTraderApi 获得。

ctp量化交易编程,ctp期货程序化交易

2. CThostFtdcTraderSpi

CThostFtdcTraderSpi 的情况类似。客户端开发者可以实现一个子类,例如命名为 CTraderSpiImpl ,对涉及到的业务响应函数进行重载,实现具体的业务处理逻辑。

子类 CTraderSpiImpl 实例化之后,被转成基类类型 CThostFtdcTraderSpi 的指针,并注册 RegisterSpi 到API功能模块的实例中。

从实现的先后顺序来看,API工作线程在先,客户端业务响应处理逻辑在后。正是基类 CThostFtdcTraderSpi 声明时使用了虚函数,先实现的API工作线程中才能调用后实现的客户端业务处理逻辑。

以下代码演示了SPI如何被注册到API中:

ctp量化交易编程,ctp期货程序化交易

API功能模块对客户端业务处理模块调用时,使用的是基类类型 CThostFtdcTraderSpi ,也就是被 RegisterSpi 注册到API中的指针。

在收到信息时,API工作线程使用这个指针执行相关的SPI函数。例如,前面给出的 OnFrontConnected 的定义。当收到 OnFrontConnected 信息时,API工作线程执行客户端定义的功能,例子中执行的就是发出认证请求。

CThostFtdcTraderSpi 中定义的响应函数不是纯虚函数,有默认的实现,空函数。所以, CThostFtdcTraderSpi 是可以被实例化的,但是这样做没有意义,因为默认不执行任何操作。子类不需要将所有的响应函数都重新实现一遍,只需要实现和业务相关的响应就可以了。不相关的业务响应时,调用默认的空函数。

CThostFtdcTraderSpi 中声明的函数都是以 On 开头,表明都是回调函数。从命名规则上,由可以细分为以下几类:

  • OnRspQry/OnRspQuery ,响应查询的回调函数,与一个查询请求ReqQry/ReqQuery对应。只有发起请求的会话连接能收到。
  • OnRsp ,响应非查询类业务的回调函数,与一个非查询类业务请求Req对应。也是只有发起请求的会话连接能收到。
  • OnRtn/OnErrRtn ,收到服务端主动推送的信息时的回调,也被称为通知。所有的会话连接都能收到。

在不同的场景下,我们可能会提到 回调 响应 通知 推送 触发 等词,表达的都是API工作线程收到信息时执行由客户端定义的业务处理逻辑。