做了三个简单的demo程序,只是查询并打印收到的订单、成交、持仓、资金等数据,当然也包含订单回报、成交回报等推送数据的显示,中泰证券、华鑫证券都借鉴了CTP的运营模式,接口的设计也是参照了CTP的形式,但是也有一定的差异,其中XTP的差异非常大,但是好的是这三套柜台系统都是开放API接口的,证券柜台的API接口虽然前几年管的很严,但是最近两年都已放开对私募、专业投资者等层级的客户限制,只要客户的程序放在证券公司的托管机房运行即可,当然是给你远程那台机器的权限的,不然怎么玩。
CTP开放平台的TTS系统已支持A股、国内期货及部分港美股合约模拟交易,也提供了通过仿CTPAPI直连华鑫证券、中泰证券(行情接口已发布,交易接口本周发布)柜台的能力,为了便于开发者快速了解各柜台接口的交互逻辑,发布了三个demo程序的源码,都是交易接口的demo,行情接口由于比较简单,所以就不针对行情接口写demo了。
粘一下ctp接口的demo源码:
#include <stdio.h>
#include <string>
#include &#34;./CTP/ThostFtdcTraderApi.h&#34;
#include <thread>
#include <mutex>
#include <functional>
#include <condition_variable>
#pragma comment (lib, &#34;./CTP/thosttraderapi_se.lib&#34;)
class semaphore
{
public:
semaphore(int value = 1) :count(value) {}
void wait()
{
std::unique_lock<std::mutex> lck(mt);
if (--count < 0)//资源不足挂起线程
cv.wait(lck);
}
void signal()
{
std::unique_lock<std::mutex> lck(mt);
if (++count <= 0)//有线程挂起,唤醒一个
cv.notify_one();
}
private:
int count;
std::mutex mt;
std::condition_variable cv;
};
semaphore _semaphore(0);
class CApplication : public CThostFtdcTraderSpi
{
public:
CApplication(std::string host, std::string broker, std::string user, std::string password, std::string appid, std::string authcode) :
m_host(host),
m_broker(broker),
m_user(user),
m_password(password),
m_appid(appid),
m_authcode(authcode)
{
m_pUserApi = CThostFtdcTraderApi::CreateFtdcTraderApi();
m_pUserApi->RegisterSpi(this);
}
~CApplication()
{
}
const std::string direction_to_string(TThostFtdcDirectionType Direction)
{
std::string str = Direction == THOST_FTDC_D_Buy ? &#34;买&#34; : &#34;卖&#34;;
return std::move(str);
}
const std::string posidirection_to_string(TThostFtdcPosiDirectionType Direction)
{
std::string str = Direction == THOST_FTDC_PD_Long ? &#34;买&#34; : &#34;卖&#34;;
return std::move(str);
}
double double_format(double value)
{
return value==DBL_MAX?0.0:value;
}
int Run()
{
m_pUserApi->RegisterFront((char*)m_host.c_str());
m_pUserApi->SubscribePrivateTopic(THOST_TERT_QUICK);
m_pUserApi->SubscribePublicTopic(THOST_TERT_QUICK);
m_pUserApi->Init();
return 0;
}
//连接成功
void OnFrontConnected()
{
printf(&#34;Connected.\n&#34;);
CThostFtdcReqAuthenticateField Req;
memset(&Req, 0x00, sizeof(Req));
strncpy_s(Req.BrokerID, m_broker.c_str(), sizeof(Req.BrokerID) - 1);
strncpy_s(Req.UserID, m_user.c_str(), sizeof(Req.UserID) - 1);
strncpy_s(Req.AuthCode, m_authcode.c_str(), sizeof(Req.AuthCode) - 1);
strncpy_s(Req.AppID, m_appid.c_str(), sizeof(Req.AppID) - 1);
m_pUserApi->ReqAuthenticate(&Req, 0);
}
//连接断开
void OnFrontDisconnected(int nReason)
{
printf(&#34;Disconnected.\n&#34;);
}
///认证响应
void OnRspAuthenticate(CThostFtdcRspAuthenticateField* pRspAuthenticateField, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
if (pRspInfo->ErrorID != 0)
printf(&#34;终端认证失败:%s\n&#34;, pRspInfo->ErrorMsg);
else
printf(&#34;终端认证成功.\n&#34;);
// 登录
printf(&#34;登录 ...\n&#34;);
CThostFtdcReqUserLoginField Req;
memset(&Req, 0x00, sizeof(Req));
strncpy_s(Req.BrokerID, m_broker.c_str(), sizeof(Req.BrokerID) - 1);
strncpy_s(Req.UserID, m_user.c_str(), sizeof(Req.UserID) - 1);
strncpy_s(Req.Password, m_password.c_str(), sizeof(Req.Password) - 1);
m_pUserApi->ReqUserLogin(&Req, 0);
}
//登录应答
void OnRspUserLogin(CThostFtdcRspUserLoginField* pRspUserLogin, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
if (pRspInfo && pRspInfo->ErrorID != 0) {
printf(&#34;Login failed. %d - %s\n&#34;, pRspInfo->ErrorID, pRspInfo->ErrorMsg);
return;
}
printf(&#34;Login succeeded.TradingDay:%s,FrontID=%d,SessionID=%d\n&#34;, pRspUserLogin->TradingDay, pRspUserLogin->FrontID, pRspUserLogin->SessionID);
// 确认结算单
CThostFtdcSettlementInfoConfirmField SettlementInfoConfirmField;
memset(&SettlementInfoConfirmField, 0x00, sizeof(SettlementInfoConfirmField));
strncpy_s(SettlementInfoConfirmField.BrokerID, pRspUserLogin->BrokerID, sizeof(SettlementInfoConfirmField.BrokerID) - 1);
strncpy_s(SettlementInfoConfirmField.InvestorID, pRspUserLogin->UserID, sizeof(SettlementInfoConfirmField.InvestorID) - 1);
strncpy_s(SettlementInfoConfirmField.ConfirmDate, pRspUserLogin->TradingDay, sizeof(SettlementInfoConfirmField.ConfirmDate) - 1);
strncpy_s(SettlementInfoConfirmField.ConfirmTime, pRspUserLogin->LoginTime, sizeof(SettlementInfoConfirmField.ConfirmTime) - 1);
m_pUserApi->ReqSettlementInfoConfirm(&SettlementInfoConfirmField, 0);
_semaphore.signal();
}
// 查询合约列表
void OnRspQryInstrument(CThostFtdcInstrumentField* pInstrument, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
if (pInstrument)
printf(&#34;InstrumentID:%s,InstrumentName:%s,ProductID:%s,PriceTick:%lf,UnderlyingInstrID:%s,StrikePrice:%lf,ExchangeID:%s\n&#34;, pInstrument->InstrumentID, pInstrument->InstrumentName, pInstrument->ProductID, pInstrument->PriceTick, pInstrument->UnderlyingInstrID, pInstrument->StrikePrice, pInstrument->ExchangeID);
if (bIsLast) {
_semaphore.signal();
}
}
// 查询行情列表
void OnRspQryDepthMarketData(CThostFtdcDepthMarketDataField* pDepthMarketData, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
if (pRspInfo && pRspInfo->ErrorID != 0) {
printf(&#34;查询行情失败. %d - %s\n&#34;, pRspInfo->ErrorID, pRspInfo->ErrorMsg);
return;
}
printf(&#34;InstrumentID:%s,LastPrice:%lf,Volume:%d,Turnover:%lf,OpenPrice:%lf,HighestPrice:%lf,LowestPrice:%lf,UpperLimitPrice:%lf,LowerLimitPrice:%lf,OpenInterest:%lf,PreClosePrice:%lf,PreSettlementPrice:%lf,SettlementPrice:%lf,UpdateTime:%s,ActionDay:%s,TradingDay:%s,ExchangeID:%s\n&#34;,
pDepthMarketData->InstrumentID, double_format(pDepthMarketData->LastPrice), pDepthMarketData->Volume, double_format(pDepthMarketData->Turnover), double_format(pDepthMarketData->OpenPrice),
double_format(pDepthMarketData->HighestPrice), double_format(pDepthMarketData->LowestPrice), double_format(pDepthMarketData->UpperLimitPrice), double_format(pDepthMarketData->LowerLimitPrice),
double_format(pDepthMarketData->OpenInterest), double_format(pDepthMarketData->PreClosePrice), double_format(pDepthMarketData->PreSettlementPrice),
double_format(pDepthMarketData->SettlementPrice), pDepthMarketData->UpdateTime, pDepthMarketData->ActionDay, pDepthMarketData->TradingDay, pDepthMarketData->ExchangeID);
if (bIsLast) {
_semaphore.signal();
}
}
// 查询委托列表
void OnRspQryOrder(CThostFtdcOrderField* pOrder, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
if (pRspInfo && pRspInfo->ErrorID != 0) {
printf(&#34;查询订单失败. %d - %s\n&#34;, pRspInfo->ErrorID, pRspInfo->ErrorMsg);
return;
}
if(pOrder)
printf(&#34;OrderLocalID:%s,InstrumentID:%s,Direction:%s,VolumeTotalOriginal:%d,LimitPrice:%lf,VolumeTraded:%d,VolumeTotal:%d,OrderSysID:%s,FrontID:%d,SessionID:%d,OrderRef:%s,OrderStatus:%c,StatusMsg:%s,InsertTime:%s\n&#34;,
pOrder->OrderLocalID, pOrder->InstrumentID, direction_to_string(pOrder->Direction).c_str(), pOrder->VolumeTotalOriginal, pOrder->LimitPrice, pOrder->VolumeTraded, pOrder->VolumeTotal, pOrder->OrderSysID, pOrder->FrontID, pOrder->SessionID, pOrder->OrderRef, pOrder->OrderStatus, pOrder->StatusMsg, pOrder->InsertTime);
if (bIsLast) {
_semaphore.signal();
}
}
// 查询成交列表
void OnRspQryTrade(CThostFtdcTradeField* pTrade, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
if (pRspInfo && pRspInfo->ErrorID != 0) {
printf(&#34;查询成交失败. %d - %s\n&#34;, pRspInfo->ErrorID, pRspInfo->ErrorMsg);
return;
}
if(pTrade)
printf(&#34;OrderLocalID:%s,InstrumentID:%s,Direction:%s,Volume:%d,Price:%lf,OrderSysID:%s,OrderRef:%d,TradeTime:%s\n&#34;,
pTrade->OrderLocalID, pTrade->InstrumentID, direction_to_string(pTrade->Direction).c_str(), pTrade->Volume, pTrade->Price, pTrade->OrderSysID, pTrade->OrderRef, pTrade->TradeTime);
if (bIsLast) {
_semaphore.signal();
}
}
// 查询持仓
void OnRspQryInvestorPosition(CThostFtdcInvestorPositionField* pInvestorPosition, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
if (pRspInfo && pRspInfo->ErrorID != 0) {
printf(&#34;查询持仓失败. %d - %s\n&#34;, pRspInfo->ErrorID, pRspInfo->ErrorMsg);
return;
}
if (pInvestorPosition)
printf(&#34;InstrumentID:%s,PosiDirection:%s,HedgeFlag:%c,Position:%d,YdPosition:%d,TodayPosition:%d,PositionCost:%lf,OpenCost:%lf,ExchangeID:%s\n&#34;,
pInvestorPosition->InstrumentID, posidirection_to_string(pInvestorPosition->PosiDirection).c_str(), pInvestorPosition->HedgeFlag, pInvestorPosition->Position, pInvestorPosition->YdPosition, pInvestorPosition->TodayPosition, pInvestorPosition->PositionCost, pInvestorPosition->OpenCost, pInvestorPosition->ExchangeID);
if (bIsLast) {
_semaphore.signal();
}
}
// 查询持仓明细
void OnRspQryInvestorPositionDetail(CThostFtdcInvestorPositionDetailField* pInvestorPositionDetail, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
if (pRspInfo && pRspInfo->ErrorID != 0) {
printf(&#34;查询持仓明细失败. %d - %s\n&#34;, pRspInfo->ErrorID, pRspInfo->ErrorMsg);
return;
}
if (pInvestorPositionDetail)
printf(&#34;InstrumentID:%s,Direction:%s,HedgeFlag:%c,Volume:%d,OpenDate:%s,OpenPrice:%lf,Margin:%lf,ExchangeID:%s\n&#34;,
pInvestorPositionDetail->InstrumentID, direction_to_string(pInvestorPositionDetail->Direction).c_str(), pInvestorPositionDetail->HedgeFlag, pInvestorPositionDetail->Volume, pInvestorPositionDetail->OpenDate, pInvestorPositionDetail->OpenPrice, pInvestorPositionDetail->Margin, pInvestorPositionDetail->ExchangeID);
if (bIsLast) {
_semaphore.signal();
}
}
///请求查询资金账户响应
void OnRspQryTradingAccount(CThostFtdcTradingAccountField* pTradingAccount, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
if (pTradingAccount)
printf(&#34;AccountID:%s,Available:%lf,FrozenCash:%lf,FrozenCommission:%lf\n&#34;,
pTradingAccount->AccountID, pTradingAccount->Available, pTradingAccount->FrozenCash, pTradingAccount->FrozenCommission);
if (bIsLast)
printf(&#34;Query completed.\n&#34;);
}
// 委托回报
void OnRtnOrder(CThostFtdcOrderField* pOrder)
{
printf(&#34;OrderLocalID:%s,InstrumentID:%s,Direction:%s,VolumeTotalOriginal:%d,LimitPrice:%lf,VolumeTraded:%d,VolumeTotal:%d,OrderSysID:%s,FrontID:%d,SessionID:%d,OrderRef:%d,OrderStatus:%c,StatusMsg:%s,InsertTime:%s\n&#34;,
pOrder->OrderLocalID, pOrder->InstrumentID, direction_to_string(pOrder->Direction).c_str(), pOrder->VolumeTotalOriginal, pOrder->LimitPrice, pOrder->VolumeTraded, pOrder->VolumeTotal, pOrder->OrderSysID, pOrder->FrontID, pOrder->SessionID, pOrder->OrderRef, pOrder->OrderStatus, pOrder->StatusMsg, pOrder->InsertTime);
}
// 成交回报
void OnRtnTrade(CThostFtdcTradeField* pTrade)
{
printf(&#34;OrderLocalID:%s,InstrumentID:%s,Direction:%s,Volume:%d,Price:%lf,OrderSysID:%s,OrderRef:%d,TradeTime:%s\n&#34;,
pTrade->OrderLocalID, pTrade->InstrumentID, direction_to_string(pTrade->Direction).c_str(), pTrade->Volume, pTrade->Price, pTrade->OrderSysID, pTrade->OrderRef, pTrade->TradeTime);
}
// 报单错误
void OnErrRtnOrderInsert(CThostFtdcInputOrderField* pInputOrder, CThostFtdcRspInfoField* pRspInfo)
{
if (pRspInfo && pRspInfo->ErrorID != 0)
printf(&#34;报单失败. %d - %s\n&#34;, pRspInfo->ErrorID, pRspInfo->ErrorMsg);
if(pInputOrder)
printf(&#34;OrderRef:%s,InstrumentID:%s,Direction:%s,VolumeTotalOriginal:%d,LimitPrice:%lf,ExchangeID:%s\n&#34;,
pInputOrder->OrderRef, pInputOrder->InstrumentID, direction_to_string(pInputOrder->Direction).c_str(), pInputOrder->VolumeTotalOriginal, pInputOrder->LimitPrice, pInputOrder->ExchangeID);
}
// 撤单错误回报
void OnErrRtnOrderAction(CThostFtdcOrderActionField* pOrderAction, CThostFtdcRspInfoField* pRspInfo)
{
if (pRspInfo && pRspInfo->ErrorID != 0)
printf(&#34;撤单失败. %d - %s\n&#34;, pRspInfo->ErrorID, pRspInfo->ErrorMsg);
if (pOrderAction)
printf(&#34;FrontID:%d,SessionID:%d,OrderRef:%s,InstrumentID:%s,OrderActionRef:%d,OrderSysID:%s,LimitPrice:%lf,ExchangeID:%s\n&#34;,
pOrderAction->FrontID, pOrderAction->SessionID, pOrderAction->OrderRef, pOrderAction->InstrumentID, pOrderAction->OrderActionRef, pOrderAction->OrderSysID, pOrderAction->LimitPrice, pOrderAction->ExchangeID);
}
public:
std::string m_host;
std::string m_broker;
std::string m_user;
std::string m_password;
std::string m_appid;
std::string m_authcode;
CThostFtdcTraderApi* m_pUserApi;
};
void display_usage()
{
printf(&#34;usage:ctpprint host user password appid authcode\n&#34;);
printf(&#34;example:ctpprint tcp://180.168.146.187:10130 000001 888888 simnow_client_test 0000000000000000\n&#34;);
}
int main(int argc, char* argv[])
{
if (argc != 7) {
display_usage();
return -1;
}
CApplication Spi(argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
// 启动
if (Spi.Run() < 0)
return -1;
// 等待登录完成
_semaphore.wait();
// 查询合约
printf(&#34;查询合约 ...\n&#34;);
CThostFtdcQryInstrumentField QryInstrument = { 0 };
Spi.m_pUserApi->ReqQryInstrument(&QryInstrument, 0);
_semaphore.wait();
// 查询行情
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
printf(&#34;查询行情 ...\n&#34;);
CThostFtdcQryDepthMarketDataField QryDepthMarketData = { 0 };
Spi.m_pUserApi->ReqQryDepthMarketData(&QryDepthMarketData, 0);
_semaphore.wait();
// 查询订单
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
printf(&#34;查询订单 ...\n&#34;);
CThostFtdcQryOrderField QryOrder = { 0 };
Spi.m_pUserApi->ReqQryOrder(&QryOrder, 0);
_semaphore.wait();
// 查询成交
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
printf(&#34;查询成交 ...\n&#34;);
CThostFtdcQryTradeField QryTrade = { 0 };
Spi.m_pUserApi->ReqQryTrade(&QryTrade, 0);
_semaphore.wait();
// 查询持仓
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
printf(&#34;查询持仓 ...\n&#34;);
CThostFtdcQryInvestorPositionField QryInvestorPosition = { 0 };
Spi.m_pUserApi->ReqQryInvestorPosition(&QryInvestorPosition, 0);
_semaphore.wait();
// 持仓明细
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
printf(&#34;持仓明细 ...\n&#34;);
CThostFtdcQryInvestorPositionDetailField QryInvestorPositionDetail = { 0 };
Spi.m_pUserApi->ReqQryInvestorPositionDetail(&QryInvestorPositionDetail, 0);
_semaphore.wait();
// 查询资金
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
printf(&#34;查询资金 ...\n&#34;);
CThostFtdcQryTradingAccountField QryTradingAccount = { 0 };
Spi.m_pUserApi->ReqQryTradingAccount(&QryTradingAccount, 0);
getchar();
return 0;
}
ctpdemo运行效果:
xtpdemo运行效果:
stpdemo运行效果:
demo源下载地址:https://github.com/krenx1983/openctp/tree/master/demo/print
喜欢的请多点点赞和收藏,Github集 1000 Star,知乎集 10000 收藏,将开启各仿CTPAPI接口源码开放过程。 |
|