前言
前面文章將traderapi和mdapi分開來打包,這樣就會有兩個jar包,裏面有些結構體是完全重複的,給一些人造成困惑。其實分開打包的目的主要是方便需要單獨用的人,有的人只想接交易,有的人只想接行情。這裏根據別人編譯的經驗,給出一個合併在一起打包的方案,並且給了一個訂閱全市場合約行情的demo,大家共同探討。全部的代碼及release版本都在github: https://github.com/nicai0609/JAVA-CTPAPI/可下。
1.準備工作
完全參考文章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
│ …
2.通過Swig得到jar包
在剛剛下載得到的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<iconv_t>(-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<size_t>(-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
。
3.通過C++得到java可調用的dll動態庫
這一步也完全參考文章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位都是通用的,不需要重複做。
4.Demo
這裏給出一個訂閱全市場行情的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<String> 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<String> 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<String> 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<String> 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<String> instr_vec = new Vector<String>();
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;
}
}