跳转至

> 交易接口

1. 说明

交易员API提供了两个接口,分别为CThostFtdcTraderApiCThostFtdcTraderSpi。这两个接口对FTD协议进行了封装,方便客户端应用程序的开发。客户端应用程序可以通过CThostFtdcTraderApi发出操作请求,通过继承CThostFtdcTraderSpi并重载回调函数来处理后台服务的响应。

交易员客户端应用程序至少由两个线程组成,一个是应用程序主线程,一个是交易员API工作线程。应用程序与交易系统的通讯是由 API工作线程驱动的。CThostFtdcTraderApi提供的接口是线程安全的,可以有多个应用程序线程同时发出请求。CThostFtdcTraderSpi提供的接口回调是由API工作线程驱动,通过实现SPI中的接口方法,可以从交易托管系统收取所需数据。

如果重载的某个回调函数阻塞,则等于阻塞了API工作线程,API与交易系统的通讯会停止。因此,在 CThostFtdcTraderSpi 派生类的回调函数中,通常应迅速返回,可以利用将数据放入缓冲区或通过Windows的消息机制来实现。

交易员 API 在运行过程中,会将一些数据写入本地文件中。调用CreateFtdcTraderApi函数,可以传递一个参数,指明存贮本地文件的路径。该路径必须在运行前已创建好。本地文件的扩展名都是”.con”。

注意以下事项:

init和release不是线程安全的,多线程使用需要加锁

CThostFtdcTraderSpi 派生类的回调函数中应及时返回,不要阻塞。

API请求的输入参数不能为 NULL。

API请求的返回参数,0表示正确,其他表示错误,详细错误编码请查error.xml。

API一个进程中linux最多开180-200左右的连接数,windows最多开400左右连接数,想建立更多连接请多开进程。

CTP系统支持的通讯模式有三种:对话通讯模式,私有通讯模式,广播通讯模式。详细说明见[通讯模式]

2. 代码示例

下面通过一个简单的代码示例,带大家快速了解下交易API的使用方法。该示例演示了API的初始化、订阅私有流、连接前置、登录交易系统、报单和查询等过程。

2.1. 源代码

01.     //以下是tradehandler.h文件
02.     
03.     #include "ThostFtdcTraderApi.h"
04.     
05.     #include "traderApi.h"
06.     
07.     #include <string.h>
08.     
09.     #include <stdio.h>
10.     
11.     #include <Windows.h>
12.     
13.      
14.     
15.     class CTraderHandler : public CThostFtdcTraderSpi
16.     
17.     {
18.     
19.     private:
20.     
21.       CThostFtdcTraderApi *m_ptraderapi;
22.     
23.      
24.     
25.     public:
26.     
27.       void connect()
28.     
29.       {
30.     
31.           //创建API实例
32.     
33.           m_ptraderapi = CThostFtdcTraderApi::CreateFtdcTraderApi(".//flow/"); //必须提前创建好flow目录,流文件*.con就会在该文件夹下面创建,如果想要区别不同session创建的流水文件名称可见如下示例“.//flow/a_”
34.     
35.           m_ptraderapi->RegisterSpi(this);
36.     
37.           m_ptraderapi->SubscribePublicTopic(THOST_TERT_QUICK);
38.     
39.           m_ptraderapi->SubscribePrivateTopic(THOST_TERT_QUICK); //设置私有流订阅模式
40.     
41.           m_ptraderapi->RegisterFront("tcp://127.0.0.1:41205");
42.     
43.           m_ptraderapi->Init();
44.     
45.           //输出API版本信息
46.     
47.           printf("%s\n", m_ptraderapi->GetApiVersion());
48.     
49.       }
50.     
51.      
52.     
53.       //释放
54.     
55.       void release()
56.     
57.       {
58.     
59.           m_ptraderapi->Release();
60.     
61.       }
62.     
63.      
64.     
65.       //登陆
66.     
67.       void login()
68.     
69.       {
70.     
71.           CThostFtdcReqUserLoginField t = {0};
72.     
73.           strcpy_s(t.BrokerID, "1701");
74.     
75.           strcpy_s(t.UserID, "1701_admin");
76.     
77.           strcpy_s(t.Password, "1701_admin");
78.     
79.           while (m_ptraderapi->ReqUserLogin(&amp;t, 1)!=0)   Sleep(1000);
80.     
81.       }
82.     
83.      
84.     
85.       //结算单确认
86.     
87.       void settlementinfoConfirm()
88.     
89.       {
90.     
91.           CThostFtdcSettlementInfoConfirmField t = {0};
92.     
93.           strcpy_s(t.BrokerID, "1701");
94.     
95.           strcpy_s(t.InvestorID, "00001");
96.     
97.           while (m_ptraderapi->ReqSettlementInfoConfirm(&amp;t, 2)!=0)  Sleep(1000);
98.     
99.       }
100.        
101.         
102.        
103.          //报单
104.        
105.          void orderinsert()
106.        
107.          {
108.        
109.              CThostFtdcInputOrderField t = { 0 };
110.        
111.              strcpy_s(t.BrokerID, "9999");
112.        
113.              strcpy_s(t.InvestorID, "00001");
114.        
115.              strcpy_s(t.UserID, "00001");
116.        
117.              t.Direction = THOST_FTDC_D_Buy;
118.        
119.              t.CombOffsetFlag[0] = THOST_FTDC_OF_Open;
120.        
121.              t.CombHedgeFlag[0] = THOST_FTDC_HF_Speculation;
122.        
123.              t.ContingentCondition = THOST_FTDC_CC_Immediately;
124.    
125.              strcpy_s(t.InstrumentID, "sc1807");
126.        
127.              t.ForceCloseReason = THOST_FTDC_FCC_NotForceClose;
128.        
129.              t.LimitPrice = 490;
130.        
131.              t.OrderPriceType = THOST_FTDC_OPT_LimitPrice;
132.        
133.              t.VolumeCondition = THOST_FTDC_VC_AV;
134.        
135.              t.TimeCondition = THOST_FTDC_TC_GFD;
136.        
137.              t.VolumeTotalOriginal = 1;
138.        
139.              strcpy_s(t.OrderRef, "0000001");
140.    
141.              strcpy_s(t.ExchangeID, "INE");
142.        
143.              while (m_ptraderapi->ReqOrderInsert(&amp;t, 3) != 0) Sleep(1000);
144.        
145.          }
146.        
147.          //查询合约
148.        
149.          void qryInstrument()
150.        
151.          {
152.        
153.              CThostFtdcQryInstrumentField t = { 0 };
154.        
155.              strcpy_s(t.ExchangeID, "SHFE");
156.        
157.              strcpy_s(t.InstrumentID, "zn1803");
158.        
159.              while (m_ptraderapi->ReqQryInstrument(&amp;t, 4) != 0) Sleep(1000);
160.        
161.          }
162.        
163.         
164.        
165.        //前置连接响应
166.        
167.          void OnFrontConnected()
168.        
169.          {
170.        
171.              printf("OnFrontConnected\n");
172.        
173.          }
174.        
175.         
176.        
177.          //登陆请求响应
178.        
179.          void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
180.        
181.          {
182.        
183.              printf("OnRspUserLogin\n");
184.        
185.          }
186.        
187.         
188.        
189.          //结算单确认响应
190.        
191.          void OnRspSettlementInfoConfirm(CThostFtdcSettlementInfoConfirmField *pSettlementInfoConfirm, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
192.        
193.          {
194.        
195.              printf("OnRspSettlementInfoConfirm\n");
196.        
197.          }
198.        
199.         
200.        
201.          //查询合约响应
202.        
203.          void OnRspQryInstrument(CThostFtdcInstrumentField *pInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
204.        
205.          {
206.        
207.              printf("OnRspQryInstrument\n");
208.        
209.          }
210.         
211.        
212.          //报单通知
213.        
214.          void OnRtnOrder(CThostFtdcOrderField *pOrder)
215.        
216.          {
217.        
218.              printf("OnRtnOrder\n");
219.        
220.          }
221.        
222.          
223.        
224.          //成交通知
225.        
226.          void OnRtnTrade(CThostFtdcTradeField *pTrade)
227.        
228.          {
229.        
230.              printf("OnRtnTrade\n");
231.        
232.          }
233.        
234.         
235.        
236.          //报单录入请求响应
237.        
238.          void OnRspOrderInsert(CThostFtdcInputOrderField *pInputOrder, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
239.        
240.          {
241.        
242.              printf("OnRspOrderInsert\n");
243.        
244.          }
245.        
246.          
247.        
248.          //错误应答
249.        
250.          void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
251.        
252.          {
253.        
254.              printf("OnRspError\n");
255.        
256.          }
257.        
258.        };
259.        
260.        //以下是main.cpp文件 
261.        
262.        #include "tradehandler.h"
263.        
264.        int main(int argc, char *argv[])
265.        
266.        {
267.        
268.          CTraderHandler *trader = new CTraderHandler();
269.        
270.          trader->connect();
271.        
272.          Sleep(2000);
273.        
274.          trader->login();
275.        
276.          Sleep(2000);
277.        
278.          trader->settlementinfoConfirm();
279.        
280.          Sleep(2000);
281.        
282.          trader->qryInstrument();
283.        
284.          Sleep(2000);
285.        
286.          trader->orderinsert();
287.        
288.          Sleep(INFINITE);
289.    
290.          trader->release();
291.        
292.          return 1;
293.        
294.        }

2.2. 代码说明

2.2.1. 继承CThostFtdcTraderSpi类

代码一开始通过#include "ThostFtdcTraderApi.h",将thosttraderapi.lib中声明的类和数据类型包括进来,该头文件中有两个重要的基类:CThostFtdcTraderSpiCThostFtdcTraderApi

CThostFtdcTraderSpi类提供了交易相关的回调接口,用户需要继承该类并重载这些接口,以获取响应数据。

CThostFtdcTraderApi类则是提供了交易相关功能的请求接口,例如查询请求、报单请求、报价请求等。

第15行我们声明了一个CTraderHandler类,该类正是继承了CThostFtdcTraderSpi类,并且重载了OnFrontConnectedOnRspUserLoginOnRspErrorOnRspOrderInsert等接口。通过这种方式,例如当我们API和前置建立连接时,便会触发OnFrontConnected;当我们发起登录请求后,便会触发OnRspUserLogin。通过这些回调返回的数据,我们可以得知当前业务处理的结果或者获取我们想要的业务数据,进一步实现自己的业务逻辑。

第21行,声明了一个CThostFtdcTraderApi类型的变量m_ptraderapi,后续我们会实例化它,以便我们使用traderapi提供的登录、报单和查询等功能。

2.2.2. 初始化

第27行到43行connect()函数里,实现了线程的初始化,步骤为:

Step 1 创建Api实例(CreateFtdcTraderApi)并为其注册对应的回调接口类的实例(RegisterSpi)。

Step 2 订阅私有流(SubscribePrivateTopic)。

Step 3 订阅公有流(SubscribePublicTopic)。

Step 4 注册名称服务器网络地址(RegisterNameServer)或注册前置机网络地址(RegisterFront)。

Step 5 初始化Api(Init),使API建立与前置的连接,连接成功后回调OnFrontConnected

第33行创建了一个api实例,并将其赋值给m_ptraderapi,同时参数".//flow/"指明我们api流水文件存放的目录为flow目录,因此在运行程序前,我们需要手工创建好这个目录!

流水文件以.con文件类型方式存放流水序号和交易日信息,这些信息对api正常工作有重要意义,不可随意修改和删除!并且多个api实例不可共享一个流水文件,必须通过不同文件夹或者不同流水文件名区分开。

第35行将自己定义的继承了Spi类的CTraderHandler注册给CThostFtdcTraderApi,这样API就能将收到的各种数据通过Spi类的接口回调给用户。

第37行使用QUICK模式订阅了私有流。订阅私有流的方式有很多种,可以参见SubscribePrivateTopic的接口说明。

第41行注册了前置地址,如果有多个地址,可以多次调用RegisterFront函数传入不同的地址来实现。

第43行调用Init()函数开始正式初始化api,也就是说前面的工作只是准备工作,到了这里api才真正开始工作。此时api会按照之前设置的私有流订阅方式和注册的地址发起与CTP前置的连接。

init和release不是线程安全的,多线程使用需要加锁。

完成以上步骤后,客户端就已经和ctp的交易前置建立了连接。

第167行到173行,如果成功建立连接,OnFrontConnected就会被调用。后续我们就可以调用各种API接口完成业务需求。

2.2.3. 显示api版本号

第47行,打印当前api的版本号。

2.2.4. 释放api线程

第55行到61行,释放了api线程,释放后api资源会被回收,并且与前置断开连接。

2.2.5. 登录

第67到81行,前置连接成功后能够开始登陆交易系统了,先初始化登陆结构体,再赋值相应字段。brokerid、userid、password都是必要字段。之后发送登陆(ReqUserLogin)指令。通过返回值可以判断是否发送成功,0表示成功,其他则表示失败,具体可以参考接口ReqUserLogin。发送不成功可以尝试等待一小会重发。

注意这里返回0不表示登录成功,而是仅仅表示api指令发出去了。该规则同样适用于其他请求接口,建议在实际应用中做好超时重发机制,以便在网络丢包的情况下能够及时重发指令。

在响应里,FrontID和SessionID是非常重要的两个ID,他标识了本次登录的当天唯一编号。对于api用户来说,如果同一用户有多个同时在线的会话,鉴于api的私有流机制,他们彼此间可以互收私有流(例如0001投资者,session1报单,session2也能收到回报),此时就可以用这组ID来过滤非本会话的私有流数据,因为对于不同的会话,这组ID是不同的;而对于交易系统来说,则可以用来定位客户的各种交易行为。

第179到185行,为请求登录后的响应。

2.2.6. 结算单确认

第87到99行,每一个交易日第一次登陆都需要先确认结算,不然只能做查询,不能执行报单等交易请求。

第191到197行,为请求确认结算单后的响应。

2.2.7. 报单

第105到143行,报入一笔限价立即单,报单属性为,多头,开仓,投机,立即单,合约为sc1807,非强平单,限价单价格为490,任意数量,当天有效,手数为1,报单引用OrderRef为"0000001",交易所为原油交易所。

注意,报单引用(OrderRef)是同一个sessionid下必须唯一且递增的一个值,不要求连续递增,但是必须要递增。而且,必须是数字,不能有英文字母!

第238到244行,如果报单填错了字段,或者客户资金不足,则报单指令发出后会被CTP拒绝,触发OnRspOrderInsert回调,返回"CTP:..."这类的报错。

第214到220行,报单请求通过交易核心的合法性验证后,通过OnRtnOrder返回。一开始会返回未知单,之后会实时推送更新的报单状态。

第226到232行,报单成交后响应,通过OnRtnTrade返回。

2.2.8. 查询合约

第149到161行,请求查询合约信息。对于大部分查询接口,如果请求信息填空,则会返回所有的记录;但是也有例外,比如保证金率和手续费率,C必须指定合约,可以参考对应的接口说明。

第203到209行,请求查询合约后的响应。

2.2.9. 错误应答

第250到256,错误应答返回。一些特殊的报错会通过该接口返回,例如错将行情端口当成交易端口,则会报无此功能,或者为没有权限的投资者报单会报无此权限,等等。

2.2.10. 程序运行流程

第264到292行,首先初始化CTraderHandler类。调用connect函数开始连接ctp前置,依次执行登录,结算单确认,查询合约,报单操作。