穿透式监管CTPAPI使用说明

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

穿透式监管CTPAPI使用说明
文档修订历史记录
内容申明
目录
第1章引言 (2)
1.1实现目的 (2)
1.2背景 (2)
1.3定义 (2)
第2章流程设计 (3)
2.1终端认证方案 (3)
2.1.1 背景条件 (3)
2.1.2 appid对应的授权码分发流程 (3)
2.1.3 登录前认证 (4)
2.2使用CTP交易API进行终端信息采集 (6)
2.2.1 直接使用CTP交易API直连模式 (6)
2.2.2 使用中继服务器操作员登录模式 (8)
2.2.3 使用中继服务器多对多登录模式 (11)
第3章CTP API使用说明 (12)
3.1采集API使用说明 (12)
3.1.1 采集API说明 (12)
3.2T RADER API (12)
3.2.1 穿透式监管涉及到的API (12)
3.2.2 客户使用流程 (14)
上海期货信息技术有限公司第
第1章引言
1.1 实现目的
简要描述CTP对于终端数据采集和终端认证的实现方案。

用于指导终端厂商正确使用CTP的交易API和采集API。

1.2 背景
证监会发布《关于进一步加强期货经营机构客户交易终端信息采集有关事项的公告》/pub/zjhpublic/zjh/201807/t20180709_340951.htm
监控中心发布的《期货公司客户交易终端信息采集及接入认证技术规范》
关于进一步加强期
货经营机构客户交易终
期货公司客户交易
终端信息采集及接入认
1.3 定义
第2章穿透式监管方案设计
2.1 终端认证方案
2.1.1背景条件
每个期货终端软件需要向期货公司申请自己的appid。

中继服务器软件需要向期货公司申请自己的relayappid。

2.1.2appid对应的授权码分发流程
期货公司确认终端软件集成了正确的数据采集模块后,为该appid的终端软件分配授权码。

终端软件需要保护好自己的appid和授权码,防止被其他软件盗用。

2.1.2.1 直连终端认证流程
对于直接连接期货公司交易柜台的终端,期货公司确认终端软件集成了正确的数据采集模块后。

给该终端软件(根据appid)分配一个授权码。

这个授权码和appid 是绑定的,当终端试图登录期货公司交易软件的时候,交易后台会验证该终端是否持有合法的appid和授权码。

2.1.2.2 中继和中继下属终端的认证流程
对于使用中继服务器连接期货公司交易柜台的终端,期货公司确认终端软件集成了正确的数据采集模块和确认中继可以正常报送终端信息后。

期货公司给该终端软件(根据appid)分配一个授权码,给中继服务器(根据semiapp)分配一个授权码。

当终端登录中继服务器时,中继服务器负责验证终端的合法性;当中继服务器登录期货公司交易软件的时候,交易后台会验证该中继服务器是否持有合法的relayappid和授权码。

2.1.3登录前认证
当客户直接使用CTP交易API时,客户的终端软件(或者中继服务器)必须存有期货公司分配的授权码,在调用ReqAuthenticate()时填入appid和对应的授权码,交易API将该授权码(暂定16字节)缓存下来作为加密的AES_KEY,以对后续信息进行AES加密。

接入认证的流程如下:
图1.1
处理流程:
1.在终端登录之前,用户通过交易API发起终端认证请求ReqAuthenticate。

需要用
户填写appid(relayappid)和授权码(authcode), 该授权码只会缓存交易API中,
不会在网络中直接传输授权码。

2.交易前置随机生成128字节的TimeStamp,将TimeStamp保存在session信息中
,并将其通过RspAuthenticate回调信息返回给终端。

3.终端收到RspAuthenticate回调信息后,使用AES_KEY加密TimeStamp,并将其赋
值到AuthInfo字段,再次发起ReqAuthenticate请求
4.交易前置收到第二次的FTD_TID_ReqAuthenticate之后,将之前session保存的
TimeStamp 赋值到请求里面的TimeStamp字段中。

将认证消息发送给交易核心
5.交易核心使用内存数据库中终端信息对应的授权码加密TimeStamp字段,并将加
密结果与AuthInfo比较。

如果相同设置当前终端为已经认证。

并返回
RspAuthenticate成功结果给交易前置。

6.交易前置通过API回调,将认证结果返回给用户
对于认证失败的连接,不允许进行登录。

2.2 使用CTP交易API进行终端信息采集
用户可以直接使用CTP交易API进行交易,也可以通过中继服务器间接调用交易API 进行交易。

这就需要将信息采集动态库和CTP的交易API分离开来,因此信息的采集和上报有需要分为两步。

2.2.1直接使用CTP交易API直连模式
直接使用交易API进行交易时,API会直接调用GetSytemInfo()采集终端信息,并将信息填入LocalSystemInnerInfo字段,通过ReqUserLogin()将采集到的信息送给前置。

前置收到交易API的ReqUserLogin请求后采集客户端的公网IP,并将该信息填写到登录请求的LocalSystemOuterInfo,然后将登录请求发送给交易核心。

交易核心收到用户登录请求后,验证密码等信息。

若通过验证则将请求中的客户端信息发送给TMDB(内存数据回写数据库组件),TMDB(内存数据回写数据库组件)用监控中心发布的公钥对LocalSystemOuterInfo字段的信息进行加密,然后将该信息与其它信息一起回写数据库,结算系统读取数据库,将信息上报给监控中心。

流程如下图:
图1.2
LocalSystemInnerInfo 里面存储的为公钥加密后的终端数据。

LocalSystemOuterInfo里面存储的为明文的公网IP和登录时间,入库之前需要用公钥加密。

带下划线的函数,为CTP交易系统内部函数。

如_SubmitUserSystemInfo。

步骤:
1.当终端软件通过交易API发起登录请求时,交易API调用GetSystemInfo()采集
终端信息,将该信息填写到登录请求的LocalSystemInnerInfo字段中。

上报用户系统信息给交易前置。

2.交易前置收到用户系统信息上报后,采集终端的公网IP填入
LocalSystemOuterInfo字段,缓存该用户的系统信息,每个用户只缓存一条信息。

3.交易API发送登录请求给交易前置。

交易前置转发给交易核心。

4.交易核心验证登录请求,并返回登录响应。

5.如果交易前置收到成功的登录响应,将响应中的登录时间与
LocalSystemOuterInfo合并,通过tresult发送给TMDB。

6.TMDB用监控中心的公钥加密LocalSystemOuterInfo信息,然后将所有的用户
系统信息回写到物理数据库中。

7.结算系统读取物理数据库中的信息,每日汇总所有的采集信息,将信息报送给
保证金监控中心
2.2.2使用中继服务器操作员登录模式
采用中继服务器操作员模式时,信息采集的流程分为两个步骤:
●中继服务器以操作员的的身份调用TradeAPI登录CTP交易系统,这时交易后台对
信息采集的处理方式与2.2.1中相同。

●中继服务器成功登录CTP交易系统后,须将投资者的终端信息报送上来。

流程如下图所示:
图1.3
具体步骤:
1.中继服务器先以操作员的身份调用交易API登录CTP系统,登录时采集信息的
方式与用户直接连接时相同。

2.中继服务器须采集客户端的信息,然后调用SubmitUserSystemInfo()上报终端信
息(包含客户终端采集的信息、终端的appid、终端的登录时间和公网IP、中继
服务器的relayappid等)。

3.Front采集中继服务器的公网IP,并将信息填入LocalSystemOuterInfo字段中,
然后将信息发送给tkernel。

ernel收到数据上报消息时,判断该登录账户是否有数据上报权限(只有操作
员和超级用户有为其他用户上报信息的权限)。

如果没有权限,直接丢弃消息。

如果有权限则将信息发给TMDB。

5.TMDB用监控中心的密钥加密LocalSystemOuterIno和RemoteSystemOuterInfo
字段,然后将加密后的信息和采集的终端信息写入物理数据库,结算系统读取该数据库,将信息上报给监控中心。

SubmitUserSystemInfo交易API接口只对操作员类型的中继服务器开放使用,其他类型的终端或者中继调用会直接返回失败。

2.2.3使用中继服务器多对多登录模式
图1.4
具体步骤:
1.中继服务器先为用户调用交易API的RegisterUserSystemInfo()接口(填写信息
包含客户终端采集的信息、终端的appid、终端的登录时间和公网IP、中继服务器的relayappid等)。

2.其余过程与用户直接连接时相同。

RegisterUserSystemInfo交易API接口只对多对多类型的中继服务器开放调用,其他类型的终端或者中继调用会直接返回失败。

第3章穿透式API使用说明
为了实现穿透式监管方案,CTP会发布两个API,TraderAPI和信息采集API。

下面新增或修改的API的说明。

3.1 采集API使用说明
3.1.1采集API说明
///获取AES加密和RSA加密的终端信息
/// pSystemInfo的空间需要调用者自己分配至少270个字节
/// pSystemInfo 并不是一个字符串而是数组因为多次加密后可能断串
int CTP_GetSystemInfo(char* pSystemInfo, int& nLen);
中继类型终端使用该函数获取终端信息数据(linux、windows ),安卓和ios的也会发布。

3.2 TraderAPI
3.2.1穿透式监管涉及到的API
1.///客户端认证请求
virtual int ReqAuthenticate(CThostFtdcReqAuthenticateField
*pReqAuthenticateField, int nRequestID) = 0;
修改了结构体
///客户端认证请求
struct CThostFtdcReqAuthenticateField
{
///经纪公司代码
TThostFtdcBrokerIDType B rokerID;
///用户代码
TThostFtdcUserIDType UserID;
///用户端产品信息
TThostFtdcProductInfoType UserProductInfo;
///认证码
TThostFtdcAuthCodeType A uthCode;
///App代码
TThostFtdcAppIDType AppID; ///需要按照规则定义
};
2.///客户端认证响应
virtual void OnRspAuthenticate(CThostFtdcRspAuthenticateField
*pRspAuthenticateField, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};
///客户端认证响应
struct CThostFtdcRspAuthenticateField
{
///经纪公司代码
TThostFtdcBrokerIDType B rokerID;
///用户代码
TThostFtdcUserIDType UserID;
///用户端产品信息
TThostFtdcProductInfoType UserProductInfo;
///App代码
TThostFtdcAppIDType AppID;
///App类型
TThostFtdcAppTypeType AppType;
};
3.///上报用户终端信息,用于中继服务器操作员登录模式
///操作员登录后,可以多次调用该接口上报客户信息
virtual int SubmitUserSystemInfo(CThostFtdcUserSystemInfoField
*pUserSystemInfo) = 0;
///用户系统信息
struct CThostFtdcUserSystemInfoField
{
///经纪公司代码
TThostFtdcBrokerIDType B rokerID;
///用户代码
TThostFtdcUserIDType UserID;
///用户端系统内部信息长度
TThostFtdcSystemInfoLenType ClientSystemInfoLen;
///用户端系统内部信息
TThostFtdcClientSystemInfoType ClientSystemInfo;
///用户公网IP
TThostFtdcIPAddressType ClientPublicIP;
///终端IP端口
TThostFtdcIPPortType ClientIPPort;
///登录成功时间
TThostFtdcTimeType ClientLoginTime;
///App代码
TThostFtdcAppIDType ClientAppID;
};
ClientSystemInfoLen存储的为加密后的用户终端系统内部信息的长度
ClientSystem Info存储的为加密后的用户终端系统内部信息。

ClientPublicIP存储的为用户终端IP,由中继服务器采集和填写
ClientLoginTime 存储的为用户登录中继时间,由中继服务器采集和填写
ClientAppid存储的为用户终端的appid,由中继服务器采集和填写
4.///注册用户终端信息,用于中继服务器多连接模式
///需要在终端认证成功后,用户登录前调用该接口
virtual int RegisterUserSystemInfo(CThostFtdcUserSystemInfoField *pUserSystemInfo) = 0;
3.2.2客户使用流程
每个类型的终端调用流程不同
3.2.2.1 直连终端使用流程
///在API连接后发起认证
void CUser::OnFrontConnected()
{
cout << "OnFrontConnected." << endl;
//增加版本输出2017/7/23
static const char *version = m_pUserApi->GetApiVersion();
cout << "------当前版本号:" << version << " ------" << endl;
ReqAuthenticate();
}
int CUser::ReqAuthenticate()
{
CThostFtdcReqAuthenticateField field;
memset(&field, 0, sizeof(field));
strcpy(field.BrokerID, "8000");
strcpy(erID, "001888");
//strcpy(erID, "8000_admin");
strcpy(field.AppID, " XY_Q7_V1.0.0");
strcpy(field.AuthCode, "5A5P4V7AZ5LCFEAK");
return m_pUserApi->ReqAuthenticate(&field, 5);
}
///认证成功后发起登录
void CUser::OnRspAuthenticate(CThostFtdcRspAuthenticateField
*pRspAuthenticateField, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
printf("OnRspAuthenticate\n");
if (pRspInfo != NULL && pRspInfo->ErrorID == 0)
{
::printf("认证成功,ErrorID=0x%04x, ErrMsg=%s\n\n", pRspInfo->ErrorID, pRspInfo->ErrorMsg);
ReqUserLogin();
}
else
cout << "认证失败," << "ErrorID=" << pRspInfo->ErrorID << " ,ErrMsg=" << pRspInfo->ErrorMsg << endl;
}
int CUser::ReqUserLogin()
{
printf("====ReqUserLogin====,用户登录中...\n\n");
CThostFtdcReqUserLoginField reqUserLogin;
memset(&reqUserLogin, 0, sizeof(reqUserLogin));
strcpy_s(reqUserLogin.BrokerID, "8000");
strcpy(erID, "001888");
strcpy(reqUserLogin.Password, "1");
strcpy(reqUserLogin.TradingDay, "20150715");
return m_pUserApi->ReqUserLogin(&reqUserLogin, ++RequestID);
}
3.2.2.2 多对多中继终端使用流程
///终端侧采集信息向中继发起登录并将终端信息发送给中继
char pSystemInfo[344];
int len;
CTP_GetSystemInfo(pSystemInfo, len);
cout << "CTP_GetSystemInfo once" << endl;
///中继收到终端的登录请求,发起终端认证
void CUser::OnFrontConnected()
{
cout << "OnFrontConnected." << endl;
//增加版本输出2017/7/23
static const char *version = m_pUserApi->GetApiVersion();
cout << "------当前版本号:" << version << " ------" << endl;
ReqAuthenticate();
}
int CUser::ReqAuthenticate()
{
CThostFtdcReqAuthenticateField field;
memset(&field, 0, sizeof(field));
strcpy(field.BrokerID, "8000");
strcpy(erID, "001888");
//strcpy(erID, "8000_admin");
strcpy(field.AppID, "XY_RELAY_V1.0.0");
strcpy(field.AuthCode, "5A5P4V7AZ5LCFEAK");
return m_pUserApi->ReqAuthenticate(&field, 5);
}
///认证成功后注册信息发起登录
void CUser::OnRspAuthenticate(CThostFtdcRspAuthenticateField
*pRspAuthenticateField, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
printf("OnRspAuthenticate\n");
if (pRspInfo != NULL && pRspInfo->ErrorID == 0)
{
::printf("认证成功,ErrorID=0x%04x, ErrMsg=%s\n\n", pRspInfo->ErrorID,
pRspInfo->ErrorMsg);
RegSystemInfo();
ReqUserLogin();
}
else
cout << "认证失败," << "ErrorID=" << pRspInfo->ErrorID << " ,ErrMsg="
<< pRspInfo->ErrorMsg << endl;
}
void CUser::RegSystemInfo()
{
char pSystemInfo[344];
int len;
////将从终端得到的信息赋值给下面结构体
CThostFtdcUserSystemInfoField field;
memset(&field, 0, sizeof(field));
strcpy(field.BrokerID, "8000");
strcpy(erID, "001888");
//strcpy(field.ClientSystemInfo, pSystemInfo); 不能用因为不是字符串
memcpy(field.ClientSystemInfo, pSystemInfo, len);
field.ClientSystemInfoLen = len;
strcpy(field.ClientPublicIP, "198.4.4.124");
field.ClientIPPort = 65535;
strcpy(field.ClientLoginTime, "11:28:28");
strcpy(field.ClientAppID, "Q7");
int ret = m_pUserApi->RegisterUserSystemInfo(&field);
cout << "retd = " << ret << endl;
}
int CUser::ReqUserLogin()
{
printf("====ReqUserLogin====,用户登录中...\n\n");
//
CThostFtdcReqUserLoginField reqUserLogin;
memset(&reqUserLogin, 0, sizeof(reqUserLogin));
strcpy_s(reqUserLogin.BrokerID, "8000");
strcpy(erID, "001888");
strcpy(reqUserLogin.Password, "1");
strcpy(reqUserLogin.TradingDay, "20150715");
return m_pUserApi->ReqUserLogin(&reqUserLogin, ++RequestID);
}
3.2.2.3 多对一中继终端使用流程
///终端侧采集信息向中继发起登录并将终端信息发送给中继
char pSystemInfo[344];
int len;
CTP_GetSystemInfo(pSystemInfo, len);
cout << "CTP_GetSystemInfo once" << endl;
///中继在启动后,在API连接后发起认证
void CUser::OnFrontConnected()
{
cout << "OnFrontConnected." << endl;
//增加版本输出2017/7/23
static const char *version = m_pUserApi->GetApiVersion();
cout << "------当前版本号:" << version << " ------" << endl;
ReqAuthenticate();
}
int CUser::ReqAuthenticate()
{
CThostFtdcReqAuthenticateField field;
memset(&field, 0, sizeof(field));
strcpy(field.BrokerID, "8000");
strcpy(erID, "8000_admin");
//strcpy(erID, "001888");
strcpy(field.AppID, " XY_RELAYB_V1.0.0");
strcpy(field.AuthCode, "BLFLTYGD16FZZ91T");
return m_pUserApi->ReqAuthenticate(&field, 5);
}
///认证成功后中继发起登录
void CUser::OnRspAuthenticate(CThostFtdcRspAuthenticateField
*pRspAuthenticateField, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
printf("OnRspAuthenticate\n");
if (pRspInfo != NULL && pRspInfo->ErrorID == 0)
{
::printf("认证成功,ErrorID=0x%04x, ErrMsg=%s\n\n", pRspInfo->ErrorID, pRspInfo->ErrorMsg);
ReqUserLogin();
}
else
cout << "认证失败," << "ErrorID=" << pRspInfo->ErrorID << " ,ErrMsg="
<< pRspInfo->ErrorMsg << endl;
}
int CUser::ReqUserLogin()
{
printf("====ReqUserLogin====,用户登录中...\n\n");
//
CThostFtdcReqUserLoginField reqUserLogin;
memset(&reqUserLogin, 0, sizeof(reqUserLogin));
strcpy_s(reqUserLogin.BrokerID, "8000");
strcpy(erID, "8000_admin");
strcpy(reqUserLogin.Password, "1");
strcpy(reqUserLogin.TradingDay, "");
strcpy(reqUserLogin.ClientIPAddress, "1.1.1.1");
reqUserLogin.ClientIPPort = 11;
return m_pUserApi->ReqUserLogin(&reqUserLogin, ++RequestID);
}
////中继使用操作员登录CTP后
///终端登录中继时,中继发起终端信息的上报
void CUser::SubSystemInfo()
{
char pSystemInfo[344];
int len;
///CTP_GetSystemInfo(pSystemInfo, len);
/////终端信息由终端发送到中继
//cout << "CTP_GetSystemInfo" << endl;
cout << "SubSystemInfo 1" << endl;
CThostFtdcUserSystemInfoField field;
memset(&field, 0, sizeof(field));
strcpy(field.BrokerID, "8000");
strcpy(erID, "001888");
//strcpy(field.ClientSystemInfo, pSystemInfo); 不能用因为不是字符串
memcpy(field.ClientSystemInfo, pSystemInfo, len);
field.ClientSystemInfoLen = len;
strcpy(field.ClientPublicIP, "198.114.114.124");
field.ClientIPPort = 65535;
strcpy(field.ClientLoginTime, "11:28:28");
strcpy(field.ClientAppID, "Q7");
int retx = m_pUserApi->SubmitUserSystemInfo(&field);
cout << "ret = " << retx << endl;
CThostFtdcUserSystemInfoField field1;
memset(&field1, 0, sizeof(field1));
strcpy(field1.BrokerID, "8000");
strcpy(erID, "001888");
//strcpy(field.ClientSystemInfo, pSystemInfo);
memcpy(field1.ClientSystemInfo, pSystemInfo, len);
field1.ClientSystemInfoLen = len;
strcpy(field1.ClientPublicIP, "198.4.4.124");
field1.ClientIPPort = 65532;
strcpy(field1.ClientLoginTime, "11:28:28");
strcpy(field1.ClientAppID, "Q7");
m_pUserApi->SubmitUserSystemInfo(&field1);
}。

相关文档
最新文档