2019独角兽企业重金招聘Python工程师标准>>>
1 首先在http://www.sfit.com.cn/5_2_DocumentDown.htm 下载CTP 后得到下面这些文件
2 我们选用32位版本20160606_tradeapi_windows 解压后得到
这其中包括两大部分thostmduserapi和thosttraderapi 其中这个mdxxx的api是管行情获取的,比方说你想得到期货"rb1710"当前价格 bid ask ....就用行情的api,在开盘状态下一秒返回俩tick. 如果想参与交易 就用thosttraderapi相关的. 剩下的.h文件里主要定义了客户端接口使用的业务数据结构,数据类型,接口等等,可以打开里面看看,注释还是很全面的,而且是中文.
然后我们安装Swig 这个自行去sf搜索安装即可,安装好后加入环境变量,这个工具帮我们把C++接口转为JAVA接口.
然后我们找一个32位的JDK安装好,也加入path中.这个如果是java程序员应该是人手一个了.
这些都做完后我们cd到\20160606_tradeapi_windows目录中 新建俩.i后缀的文件 thosttraderapi.i thostmduserapi.i 然后在建立一个various.i 这个用来定义一些类型转换的代码. 这些.i的文件是供Swig使用的,具体可以参考Swig教程. 如果不熟悉也不影响下一步. 具体代码如下
thosttraderapi.i
%module(directors="1") thosttradeapi
%include "various.i"
%apply char **STRING_ARRAY { char *ppInstrumentID[] }
%{
#include "ThostFtdcTraderApi.h"
%}
%feature("director") CThostFtdcTraderSpi;
%include "ThostFtdcUserApiDataType.h"
%include "ThostFtdcUserApiStruct.h"
%include "ThostFtdcTraderApi.h"
thostmduserapi.i
%module(directors="1") thostmdapi
%include "various.i"
%apply char **STRING_ARRAY { char *ppInstrumentID[] }
%{
#include "ThostFtdcMdApi.h"
%}
%feature("director") CThostFtdcMdSpi;
%include "ThostFtdcUserApiDataType.h"
%include "ThostFtdcUserApiStruct.h"
%include "ThostFtdcMdApi.h"
various.i
/* -----------------------------------------------------------------------------
* See the LICENSE file for information on copyright, usage and redistribution
* of SWIG, and the README file for authors - http://www.swig.org/release.html.
*
* various.i
*
* SWIG Typemap library for Java.
* Various useful typemaps.
* ----------------------------------------------------------------------------- */
/*
* char **STRING_ARRAY typemaps.
* These typemaps are for C String arrays which are NULL terminated.
* char *values[] = { "one", "two", "three", NULL }; // note NULL
* char ** is mapped to a Java String[].
*
* Example usage wrapping:
* %apply char **STRING_ARRAY { char **input };
* char ** foo(char **input);
*
* Java usage:
* String numbers[] = { "one", "two", "three" };
* String[] ret = modulename.foo( numbers };
*/
%typemap(jni) char **STRING_ARRAY "jobjectArray"
%typemap(jtype) char **STRING_ARRAY "String[]"
%typemap(jstype) char **STRING_ARRAY "String[]"
%typemap(in) char **STRING_ARRAY (jint size) {
int i = 0;
size = JCALL1(GetArrayLength, jenv, $input);
#ifdef __cplusplus
$1 = new char*[size+1];
#else
$1 = (char **)calloc(size+1, sizeof(char *));
#endif
for (i = 0; i
通过观察我们可以发现thostmduserapi里包含的是md的操作所用到的文件 thosttraderapi里包含的是trader操作所需要的文件
建好后我们用Swig去处理一下 以md的为例 执行如下命令
cd .一堆路径.\20160606_tradeapi_windows
swig.exe -c++ -java -package test.thostmduserapi -outdir srcmd -o thostmduserapi_wrap.cpp thostmduserapi.i
其中-java生成java接口 -package是指定包位置 这个大家根据情况自己来即可,-outdir是指定输出cpp代码位置, 这个也自己指定好即可
执行完成后得到了我们刚才命令里-o指定的文件 如下
thostmduserapi_wrap.h
thostmduserapi_wrap.cpp
可以理解成这俩文件是根据thostmduserapi翻译过来的 ,其中cpp文件大概5M左右
同时我们在srcmd目录下得到了一堆.java文件 这些文件对应的就是刚才那些api里的.h文件里的接口和po类等等.
我们做完这一步后 JAVA程序猿即可将这些java类拷贝自己项目里待用了,为了最终通过JNI调用,我们还需要得到thostmduserapi_wrap.dll动态库 这个是根据刚才生成的thosttraderapi_wrap.cpp 和thosttraderapi_wrap.h 还有刚才下载的那些api中md部分生成的,我们可以用下面这个工具来生成dll\
对于JAVA程序猿 可能压根没用过VS 不过我们也不需要你会C++ 只要会简单的安装VS基本可以搞定了
假设我们安装好了 然后启动VS
Ctrl+Shift+N新建项目->win32项目->名称叫"thostmduserapi_wrap" 然后我们选dll项目 如果
建好后我们把刚才的文件添加到项目中 需要添加的文件如图 添加方法是源文件那右键选添加->现有项
基本就是刚才生成的cpp和h 加上之前api里的md相关的文件和头文件
添加好后的项目如图
然后将你安装jdk目录\Java\jdk1.8.0_111\include下的jni.h和win32文件夹下的jni_md.h, jawt_md.h一共三个文件拷贝到安装vs的include目录底下\Microsoft Visual Studio 12.0\VC\include。这是因为thosttraderapi_wrap.cpp文件中包含了
然后我们按F7 生成解决方案 可以看到一堆报错的 我们把报错的那些方法都注释掉如图
就是上面这些 都注释掉 然后重新编译 运气好的话可以得到编译成功 然后在Debug目录可以找到
取里面的thostmduserapi_wrap.dll 放在刚才32位jdk的bin下方便调用 同时thostmduserapi.dll也放入该目录下,最终目的是确保java中loadLibrary时候可以读取到
------------------------------------------------------------------------------------
相同的操作 请在thosttraderapi也做一遍后得到thosttraderapi_wrap.dll
------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------
以上操作ok后基本就成功了 下面我们写几个测试类 真正获取一下'rb1710'的价位
public class Test {
static{
System.loadLibrary("thosttraderapi");
System.loadLibrary("thosttraderapi_wrap");
System.loadLibrary("thostmduserapi");
System.loadLibrary("thostmduserapi_wrap");
}
final static String ctp1_MdAddress = "tcp://180.168.146.187:10010";
public static void main(String[] args) {
String pszFlowPath = "C:\\out";
CThostFtdcMdApi mdApi = CThostFtdcMdApi.CreateFtdcMdApi(pszFlowPath,true);
MdSpiImpl mdSpi = new MdSpiImpl(mdApi);
mdApi.RegisterSpi(mdSpi);
mdApi.RegisterFront(ctp1_MdAddress);
mdApi.Init();
mdApi.Join();
return;
}
}
/**
* Created by
*/
public class MdSpiImpl extends CThostFtdcMdSpi {
final static String m_BrokerId = "9999";
final static String m_UserId = "123456";
final static String m_InvestorId = "123456";
final static String m_PassWord = "123456";
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
MdSpiImpl(CThostFtdcMdApi mdApi)
{
m_mdapi = mdApi;
}
private CThostFtdcMdApi m_mdapi;
@Override
public void OnFrontConnected() {
/*
客户端到行情前置的无身份验证连接建立之后,这个函数会被调用,用于说明连接已经建立。连接建立之后,才能请求登录
*/
super.OnFrontConnected();
System.out.println("On Front Connected");
test.thostmduserapi.CThostFtdcReqUserLoginField field = new test.thostmduserapi.CThostFtdcReqUserLoginField();
field.setBrokerID(m_BrokerId);
field.setUserID(m_UserId);
field.setPassword(m_PassWord);
field.setUserProductInfo("JAVA_API");
m_mdapi.ReqUserLogin(field, 0);
System.out.println("Send login ok");
//连接成功后获取下这俩的价格信息
String[] ppInstrumentID = new String[2];
ppInstrumentID[0] = "rb1710";
ppInstrumentID[1] = "hc1710";
int i = m_mdapi.SubscribeMarketData(ppInstrumentID,2);
System.out.println(i);
}
@Override
public void OnFrontDisconnected(int nReason) {
/*
如果客户端到行情前置的无身份验证连接建立失败,这个函数被调用。其中的参数说明连接失败的原因
*/
super.OnFrontDisconnected(nReason);
System.out.println("连接失败!!!" + nReason);
}
/*
交易系统对客户端的请求登录消息作出的响应
*/
@Override
public void OnRspUserLogin(CThostFtdcRspUserLoginField pRspUserLogin, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
//super.OnRspUserLogin(pRspUserLogin, pRspInfo, nRequestID, bIsLast);
if (pRspInfo != null && pRspInfo.getErrorID() != 0)
{
System.out.printf("Login ErrorID[%d] ErrMsg[%s]\n", pRspInfo.getErrorID(), pRspInfo.getErrorMsg());
return;
}
System.out.println("Login success!!!");
System.out.println("BrokerID:" + pRspUserLogin.getBrokerID());
System.out.println("CZCETime:"+pRspUserLogin.getCZCETime());
System.out.println("MaxOrderRef:"+pRspUserLogin.getMaxOrderRef());
System.out.println("UserID:"+pRspUserLogin.getUserID());
System.out.println("FrontID:"+pRspUserLogin.getFrontID());
System.out.println("SessionID:"+pRspUserLogin.getSessionID());
System.out.println("SystemName:" + pRspUserLogin.getSystemName());
}
/**
* 交易核心认为客户端请求订阅行情的消息不合法时通过这个函数返回错误信息。订阅请求合法时,也会调用,返回错误代码为 0
* @param pSpecificInstrument
* @param pRspInfo
* @param nRequestID
* @param bIsLast
*/
@Override
public void OnRspSubMarketData(CThostFtdcSpecificInstrumentField pSpecificInstrument, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
System.out.println("OnRspSubMarketData");
super.OnRspSubMarketData(pSpecificInstrument, pRspInfo, nRequestID, bIsLast);
}
/**
* 交易核心认为客户端请求退订行情的消息不合法时通过这个函数返回错误信息
* @param pSpecificInstrument
* @param pRspInfo
* @param nRequestID
* @param bIsLast
*/
@Override
public void OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField pSpecificInstrument, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
System.out.println("OnRspUnSubMarketData");
super.OnRspUnSubMarketData(pSpecificInstrument, pRspInfo, nRequestID, bIsLast);
}
/**
* 通过这个函数返回行情信息。频率是每秒两次
* @param pDepthMarketData
*/
@Override
public void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField pDepthMarketData) {
System.out.println("%"+pDepthMarketData.getInstrumentID()+"AskPrice1:"+pDepthMarketData.getAskPrice1()+"&&&&&&BidPrice1:"+pDepthMarketData.getBidPrice1());
}
/**
* 如果交易系统无法识别客户端发送的请求消息,就通过这个函数返回错误信息
* @param pRspInfo
* @param nRequestID
* @param bIsLast
*/
@Override
public void OnRspError(CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
System.out.println("OnRspError");
super.OnRspError(pRspInfo, nRequestID, bIsLast);
}
/**
* 如果超过一定时间在客户端和系统之间没有任何消息交换发生,这个函数会发送心跳用来说明客户端到系统服务器之间的连接是活跃的。
目前此接口已经不再起效
* @param nTimeLapse
*/
@Override
public void OnHeartBeatWarning(int nTimeLapse) {
System.out.println("OnHeartBeatWarning");
super.OnHeartBeatWarning(nTimeLapse);
}
}
上面是一个Test类 和 MdSpi的实现类 具体可以参考上期所给的api文档 虽然是C++的 但对着java代码看也基本是相通的.
正常运行后每秒在控制端打印4行记录 如图
---------------------------------------------------------------------------------------
下面说说测试帐号的获取方式和行情地址等
我们去http://www.simnow.com.cn/ 申请一个帐号后得到如下信息
investorId和userId相同 密码就是你注册的密码 地址在http://www.simnow.com.cn/product.action中可以找到 如下
BrokerID统一为:9999
第一套:
标准CTP:
第一组:Trade Front:180.168.146.187:10000,Market Front:180.168.146.187:10010;【电信】
第二组:Trade Front:180.168.146.187:10001,Market Front:180.168.146.187:10011;【电信】
第三组:Trade Front:218.202.237.33 :10002,Market Front:218.202.237.33 :10012;【移动】
交易阶段(服务时间):与实际生产环境保持一致
我们行情spi就选Market Front中的地址即可