前面文章将traderapi和mdapi分开来打包,这样就会有两个jar包,里面有些结构体是完全重复的,给一些人造成困惑。其实分开打包的目的主要是方便需要单独用的人,有的人只想接交易,有的人只想接行情。这里根据别人编译的经验,给出一个合并在一起打包的方案,并且给了一个订阅全市场合约行情的demo,大家共同探讨。全部的代码及release版本都在github: https://github.com/nicai0609/JAVA-CTPAPI/可下。
完全参考文章CTP JAVA API(JCTP)编译(利用Swig封装C++动态库)windows版中的准备工作就可以了,注意jdk和libiconv都要是64位的。github上有编译好的libconv的静态库,不放心也可以到qq群里文件共享区下载vs2013的源码自己编译。
本文一开始采用的是第三方库libiconv转换,下面也以libiconv为例继续。C++11库中已有字节编码转换方式,采用这种方式可以不用libiconv
库,下面和libiconv
相关的都可以略去,见《Swig转换C++接口中文乱码解决方案》。
另外,我们在下载好的20180109_tradeapi64_windows
文件夹中预先建一些文件夹,结构图如下:
20180109_tradeapi64_windows
│
│─── ctp ─── thostapi
│
│─── demo
│
│─── src
│
│─── wrap
│
│ ThostFtdcMdApi.h
│ ThostFtdcTraderApi.h
│ ThostFtdcUserApiDataType.h
│ …
在刚刚下载得到的API文件夹20180109_tradeapi64_windows
内,新建文件thostap.i
,内容如下
%module(directors="1") thosttraderapi
%include "various.i"
%apply char **STRING_ARRAY { char *ppInstrumentID[] }
%{
#include "ThostFtdcMdApi.h"
#include "ThostFtdcTraderApi.h"
#include "iconv.h"
%}
%typemap(out) char[ANY], char[] {
if ($1) {
iconv_t cd = iconv_open("utf-8", "gb2312");
if (cd != reinterpret_cast(-1)) {
char buf[4096] = {};
char **in = &$1;
char *out = buf;
size_t inlen = strlen($1), outlen = 4096;
if (iconv(cd, in, &inlen, &out, &outlen) != static_cast(-1))
$result = JCALL1(NewStringUTF, jenv, (const char *)buf);
iconv_close(cd);
}
}
}
%feature("director") CThostFtdcMdSpi;
%ignore THOST_FTDC_VTC_BankBankToFuture;
%ignore THOST_FTDC_VTC_BankFutureToBank;
%ignore THOST_FTDC_VTC_FutureBankToFuture;
%ignore THOST_FTDC_VTC_FutureFutureToBank;
%ignore THOST_FTDC_FTC_BankLaunchBankToBroker;
%ignore THOST_FTDC_FTC_BrokerLaunchBankToBroker;
%ignore THOST_FTDC_FTC_BankLaunchBrokerToBank;
%ignore THOST_FTDC_FTC_BrokerLaunchBrokerToBank;
%include "ThostFtdcUserApiDataType.h"
%include "ThostFtdcUserApiStruct.h"
%include "ThostFtdcMdApi.h"
%feature("director") CThostFtdcTraderSpi;
%include "ThostFtdcTraderApi.h"
various.i
文件是swig自带的,将swig/Lib/java/various.i拷贝过来即可。然后再当前目录cmd
下运行命令:
swig.exe -c++ -java -package ctp.thostapi -outdir src -o thosttraderapi_wrap.cpp thostapi.i
等到运行完成后,可以看到当前目录下生成了
thosttraderapi_wrap.h
thosttraderapi_wrap.cpp
这两个文件是用于包装原来C++
接口的文件,下面要用。打开src
文件夹,可以看到这时在里面生成了一系列方法的java文件,如下:
CThostFtdcAccountregisterField.java
CThostFtdcAuthenticationInfoField.java
… … …
thosttradeapiJNI.java
在cmd
中cd
到src
文件夹底下,运行命令
javac *.java
运行结束之后可以看到生成了等量的class文件,将所有的class文件拷贝到\ctp\thostapi\
文件夹中,cmd
下cd
到20180109_tradeapi64_windows
目录下,运行命令
jar cf thostapi.jar ctp
这样我们就在当前文件夹下得到了jar包thostapi.jar
。
这一步也完全参考文章CTP JAVA API(JCTP)编译(利用Swig封装C++动态库)windows版,只不过工程名我这里改成了thostapi_wrap
。编译完成之后就能在x64
文件夹底下得到动态库thostapi_wrap.dll
。
windows下是建vs工程编译生成dll,linux下可以参考github上的makefile编译生成thostapi_wrap.so
。除了这一步,以上在windows上和linux上32位,64位都是通用的,不需要重复做。
这里给出一个订阅全市场行情的demo,可以到githuab上直接导入该工程,将账号密码交易及行情前置地址改成自己的就可以运行。
import ctp.thostapi.*;
import java.util.Vector;
import java.util.Iterator;
class TraderSpiImpl extends CThostFtdcTraderSpi{
final static String m_BrokerId = "9999";
final static String m_UserId = "070624";
final static String m_InvestorId = "070624";
final static String m_PassWord = "passwd";
final static String m_TradingDay = "20181122";
final static String m_AccountId = "070624";
final static String m_CurrencyId = "CNY";
TraderSpiImpl(CThostFtdcTraderApi traderapi,Vector instrs)
{
m_traderapi = traderapi;
m_instr_vec = instrs;
}
@Override
public void OnFrontConnected(){
System.out.println("On Front Connected");
CThostFtdcReqUserLoginField field = new CThostFtdcReqUserLoginField();
field.setBrokerID(m_BrokerId);
field.setUserID(m_UserId);
field.setPassword(m_PassWord);
field.setUserProductInfo("JAVA_API");
m_traderapi.ReqUserLogin(field,0);
System.out.println("Send login ok");
}
@Override
public void OnRspUserLogin(CThostFtdcRspUserLoginField pRspUserLogin, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean 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!!!");
CThostFtdcQryTradingAccountField qryTradingAccount = new CThostFtdcQryTradingAccountField();
qryTradingAccount.setBrokerID(m_BrokerId);
qryTradingAccount.setCurrencyID(m_CurrencyId);;
qryTradingAccount.setInvestorID(m_InvestorId);
//m_traderapi.ReqQryTradingAccount(qryTradingAccount, 1);
CThostFtdcQrySettlementInfoField qrysettlement = new CThostFtdcQrySettlementInfoField();
qrysettlement.setBrokerID(m_BrokerId);
qrysettlement.setInvestorID(m_InvestorId);
qrysettlement.setTradingDay(m_TradingDay);
qrysettlement.setAccountID(m_AccountId);
qrysettlement.setCurrencyID(m_CurrencyId);
//m_traderapi.ReqQrySettlementInfo(qrysettlement, 2);
CThostFtdcQryInstrumentField qryInstr = new CThostFtdcQryInstrumentField();
m_traderapi.ReqQryInstrument(qryInstr, 3);
System.out.println("Query success!!!");
}
@Override
public void OnRspQryTradingAccount(CThostFtdcTradingAccountField pTradingAccount, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast)
{
if (pRspInfo != null && pRspInfo.getErrorID() != 0)
{
System.out.printf("OnRspQryTradingAccount ErrorID[%d] ErrMsg[%s]\n", pRspInfo.getErrorID(), pRspInfo.getErrorMsg());
return;
}
if (pTradingAccount != null)
{
System.out.printf("Balance[%f]Available[%f]WithdrawQuota[%f]Credit[%f]\n",
pTradingAccount.getBalance(), pTradingAccount.getAvailable(), pTradingAccount.getWithdrawQuota(),
pTradingAccount.getCredit());
}
else
{
System.out.printf("NULL obj\n");
}
}
public void OnRspQrySettlementInfo(CThostFtdcSettlementInfoField pSettlementInfo, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast)
{
if (pRspInfo != null && pRspInfo.getErrorID() != 0)
{
System.out.printf("OnRspQrySettlementInfo ErrorID[%d] ErrMsg[%s]\n", pRspInfo.getErrorID(), pRspInfo.getErrorMsg());
return;
}
if (pSettlementInfo != null)
{
System.out.printf("%s",pSettlementInfo.getContent());
}
else
{
System.out.printf("NULL obj\n");
}
}
public void OnRspQryInstrument(CThostFtdcInstrumentField pInstrument, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast)
{
if (pRspInfo != null && pRspInfo.getErrorID() != 0)
{
System.out.printf("OnRspQryInstrument ErrorID[%d] ErrMsg[%s]\n", pRspInfo.getErrorID(), pRspInfo.getErrorMsg());
return;
}
if (pInstrument != null)
{
//System.out.printf("%s\n",pInstrument.getInstrumentID());
m_instr_vec.add(pInstrument.getInstrumentID());
}
else
{
System.out.printf("NULL obj\n");
}
}
private CThostFtdcTraderApi m_traderapi;
private Vector m_instr_vec;
}
class mdspiImpl extends CThostFtdcMdSpi{
final static String m_BrokerId = "9999";
final static String m_UserId = "070624";
final static String m_InvestorId = "070624";
final static String m_PassWord = "passwd";
final static String m_TradingDay = "20181122";
final static String m_AccountId = "070624";
final static String m_CurrencyId = "CNY";
mdspiImpl(CThostFtdcMdApi mdapi,Vector instrs)
{
m_mdapi = mdapi;
m_instr_vec = instrs;
}
public void OnFrontConnected(){
System.out.println("On Front Connected");
CThostFtdcReqUserLoginField field = new CThostFtdcReqUserLoginField();
field.setBrokerID(m_BrokerId);
field.setUserID(m_UserId);
field.setPassword(m_PassWord);
m_mdapi.ReqUserLogin(field, 0);
}
public void OnRspUserLogin(CThostFtdcRspUserLoginField pRspUserLogin, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
if (pRspUserLogin != null) {
System.out.printf("Brokerid[%s]\n",pRspUserLogin.getBrokerID());
}
String[] instruementid = new String[1];
Iterator iterator = m_instr_vec.iterator();
while (iterator.hasNext()) {
instruementid[0]=iterator.next().toString();
m_mdapi.SubscribeMarketData(instruementid,1);
System.out.println(instruementid[0]);
}
}
public void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField pDepthMarketData) {
if (pDepthMarketData != null)
{
System.out.printf("InstrumentID[%s]AskPrice1[%f]BidPrice1[%f]\n",
pDepthMarketData.getInstrumentID(),pDepthMarketData.getAskPrice1(),pDepthMarketData.getBidPrice1());
}
else
{
System.out.printf("NULL obj\n");
}
}
private CThostFtdcMdApi m_mdapi;
private Vector m_instr_vec;
}
public class demo {
static{
System.loadLibrary("thosttraderapi");
System.loadLibrary("thostmduserapi");
System.loadLibrary("thostapi_wrap");
}
final static String ctp1_TradeAddress = "tcp://180.168.146.187:10000";
final static String ctp1_MdAddress = "tcp://180.168.146.187:10010";
static Vector instr_vec = new Vector();
public static void main(String[] args) {
//System.out.println(System.getProperty("java.library.path"));
CThostFtdcTraderApi traderApi = CThostFtdcTraderApi.CreateFtdcTraderApi("trade");
TraderSpiImpl pTraderSpi = new TraderSpiImpl(traderApi,instr_vec);
traderApi.RegisterSpi(pTraderSpi);
traderApi.RegisterFront(ctp1_TradeAddress);
traderApi.SubscribePublicTopic(THOST_TE_RESUME_TYPE.THOST_TERT_QUICK);
traderApi.SubscribePrivateTopic(THOST_TE_RESUME_TYPE.THOST_TERT_QUICK);
traderApi.Init();
//这里sleep是为了保证traderapi的登录查询成功
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
CThostFtdcMdApi mdApi = CThostFtdcMdApi.CreateFtdcMdApi("md");
mdspiImpl pMdspiImpl = new mdspiImpl(mdApi,instr_vec);
mdApi.RegisterSpi(pMdspiImpl);
mdApi.RegisterFront(ctp1_MdAddress);
mdApi.Init();
traderApi.Join();
mdApi.Join();
return;
}
}