CTP 作为期货量化交易的标准交易接口,是量化开发者必须掌握的核心技能。很多新手卡在连接、登录、结算单确认、报单、撤单等环节,折腾几天都跑不通一个可交易 Demo。
今天直接给你一套零门槛、可直接编译运行的 Linux C++ CTP 交易完整 Demo,从环境搭建到报单撤单,一步到位,复制粘贴就能用,彻底告别 CTP 交易入门难!
一、先搞懂:这篇 Demo 能给你什么?
纯原生 C++ 实现:不依赖第三方框架,交易逻辑一目了然
Linux 环境一键编译:解决 90% 新手遇到的链接、编译报错
完整交易链路:连接→认证→登录→结算单确认→查询合约→报单→撤单
注释超详细:每行关键代码都有说明,新手也能看懂
即拿即用:替换期货公司账号信息,直接跑仿真
二、前置准备(1 分钟搞定)
1. 环境要求
系统:Ubuntu/CentOS 等任意 Linux 发行版(64 位)
编译器:g++(安装命令:sudo apt install g++或sudo yum install gcc-c++)
CTP 交易库:官方libthosttraderapi.so动态库(已集成在 Demo,无需单独下载)
2. 核心文件说明
Demo 极简干净,仅 3 个核心文件:
TradeDemo/
├── testTradeApi.cpp
├── TraderSpi.h
└── TraderSpi.cpp
三、完整可编译源码(直接复制)
1. 主程序:testTradeApi.cpp
#include"./include/ThostFtdcTraderApi.h"#include"TraderSpi.h"#include<cstdlib>#include<iostream>// 全局变量CThostFtdcTraderApi* pUserApi = NULL;const char FRONT_ADDR[] = "tcp://IP:PORT";int iRequestID = 0;intmain(){ pUserApi = CThostFtdcTraderApi::CreateFtdcTraderApi(); if(!pUserApi) { std::cerr << "Error: Failed to create TD API instance" << std::endl; return 1; } CThostFtdcTraderSpi* pUserSpi = new CTraderSpi(); pUserApi->RegisterSpi(pUserSpi); pUserApi->SubscribePublicTopic(THOST_TERT_QUICK); pUserApi->SubscribePrivateTopic(THOST_TERT_QUICK); pUserApi->RegisterFront(const_cast<char*>(FRONT_ADDR)); pUserApi->Init(); pUserApi->Join(); // 清理 pUserApi->Release(); delete pUserSpi; return 0;}
2. 交易 SPI 回调头文件:TraderSpi.h
#pragma once#include<map>#include<string>#include"./include/ThostFtdcTraderApi.h"#include<string>class CTraderSpi : public CThostFtdcTraderSpi{public: ///当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。 virtualvoidOnFrontConnected(); virtualvoidOnRspAuthenticate(CThostFtdcRspAuthenticateField *pRspAuthenticateField, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast); ///登录请求响应 virtualvoidOnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast); ///投资者结算结果确认响应 virtualvoidOnRspSettlementInfoConfirm(CThostFtdcSettlementInfoConfirmField *pSettlementInfoConfirm, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast); ///请求查询合约响应 virtualvoidOnRspQryInstrument(CThostFtdcInstrumentField *pInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast); ///报单录入请求响应 virtualvoidOnRspOrderInsert(CThostFtdcInputOrderField *pInputOrder, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast); ///报单操作请求响应 virtualvoidOnRspOrderAction(CThostFtdcInputOrderActionField *pInputOrderAction, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast); ///错误应答 virtualvoidOnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast); ///当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。 virtualvoidOnFrontDisconnected(int nReason); ///报单通知 virtualvoidOnRtnOrder(CThostFtdcOrderField *pOrder); ///成交通知 virtualvoidOnRtnTrade(CThostFtdcTradeField *pTrade);private: voidReqAuthCode(); ///用户登录请求 voidReqUserLogin(); ///投资者结算结果确认 voidReqSettlementInfoConfirm(); ///请求查询合约 voidReqQryInstrument(); ///报单录入请求 voidReqOrderInsert(); ///报单操作请求 voidReqOrderAction(CThostFtdcOrderField *pOrder); // 是否收到成功的响应 boolIsErrorRspInfo(CThostFtdcRspInfoField *pRspInfo);};
3. 交易 SPI 回调实现:TraderSpi.cpp
#include"TraderSpi.h"#include<iostream>#include<cstring>#include<cstdio>#include<ctime>#include<fstream>#include<iomanip>#include<limits>#include<sys/stat.h>// for mkdir#include<unistd.h>// for sleep#include<cstdlib>// 添加这行// USER_API parametersextern CThostFtdcTraderApi* pUserApi;TThostFtdcBrokerIDType BROKER_ID = "****";TThostFtdcInvestorIDType INVESTOR_ID = "*****";TThostFtdcPasswordType PASSWORD = "******";TThostFtdcAuthCodeType AuthCode = "******";TThostFtdcAppIDType AppID = "*****";TThostFtdcInstrumentIDType INSTRUMENT_ID = "rb2605"; // 合约代码 TThostFtdcDirectionType DIRECTION = THOST_FTDC_D_Sell; // 买卖方向TThostFtdcPriceType LIMIT_PRICE = 8057; // 价格TThostFtdcPriceType STOP_PRICE = 8057; // 价格TThostFtdcExchangeIDType ExchangeID ="SHFE";TThostFtdcInstrumentIDType OPTION_InstrumentID = "m2301-C-3200";TThostFtdcExchangeIDType OPTION_ExchangeID ="DCE";// 会话参数TThostFtdcFrontIDType FRONT_ID; //前置编号TThostFtdcSessionIDType SESSION_ID; //会话编号TThostFtdcOrderRefType ORDER_REF; //报单引用time_t lOrderTime;time_t lOrderOkTime;// Request IDextern int iRequestID;time_t pos = 0;std::ofstream OutStream;#pragma GCC diagnostic ignored "-Wunused-variable"// C++98 compatible invalid double definitionnamespace { const double InvalidDouble = std::numeric_limits<double>::infinity();}voidCTraderSpi::OnFrontDisconnected(int nReason){ std::cerr << "--->>> " << "OnFrontDisconnected" << std::endl; std::cerr << "--->>> Reason = " << nReason << std::endl;}boolCTraderSpi::IsErrorRspInfo(CThostFtdcRspInfoField *pRspInfo){ bool bResult = ((pRspInfo) && (pRspInfo->ErrorID != 0)); if (bResult) std::cerr << "--->>> ErrorID=" << pRspInfo->ErrorID << ", ErrorMsg=" << pRspInfo->ErrorMsg << std::endl; return bResult;}voidCTraderSpi::OnFrontConnected(){ std::cerr << "--->>> " << "OnFrontConnected" << std::endl; ReqAuthCode();}voidCTraderSpi::ReqAuthCode(){ if(!pUserApi) { return; } CThostFtdcReqAuthenticateField req; memset(&req, 0, sizeof(req)); strncpy(req.BrokerID, BROKER_ID, sizeof(req.BrokerID)-1); strncpy(req.UserID, INVESTOR_ID, sizeof(req.UserID)-1); strncpy(req.UserProductInfo, "", sizeof(req.UserProductInfo)-1); strncpy(req.AuthCode, AuthCode, sizeof(req.AuthCode)-1); strncpy(req.AppID, AppID, sizeof(req.AppID)-1); sleep(1); // Wait for flow control int iResult = pUserApi->ReqAuthenticate(&req, ++iRequestID); std::cerr << "--->>> Send client authentication request: " << ((iResult == 0) ? "Success" : "Failed") << std::endl;}voidCTraderSpi::OnRspAuthenticate(CThostFtdcRspAuthenticateField *pRspAuthenticateField, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){ if (bIsLast && !IsErrorRspInfo(pRspInfo)) { if(pRspAuthenticateField) { ReqUserLogin(); } }}voidCTraderSpi::ReqUserLogin(){ CThostFtdcReqUserLoginField req; memset(&req, 0, sizeof(req)); strncpy(req.BrokerID, BROKER_ID, sizeof(req.BrokerID)-1); strncpy(req.UserID, INVESTOR_ID, sizeof(req.UserID)-1); strncpy(req.Password, PASSWORD, sizeof(req.Password)-1); int iResult = pUserApi->ReqUserLogin(&req, ++iRequestID); std::cerr << "--->>> Send user login request: " << ((iResult == 0) ? "Success" : "Failed") << std::endl;}voidCTraderSpi::OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){ std::cerr << "--->>> " << "OnRspUserLogin" << std::endl; if (bIsLast && !IsErrorRspInfo(pRspInfo)) { FRONT_ID = pRspUserLogin->FrontID; SESSION_ID = pRspUserLogin->SessionID; int iNextOrderRef = atoi(pRspUserLogin->MaxOrderRef); iNextOrderRef++; snprintf(ORDER_REF, sizeof(ORDER_REF), "%d", iNextOrderRef); std::cerr << "--->>> Current trading day = " << pUserApi->GetTradingDay() << std::endl; sleep(1); ReqSettlementInfoConfirm(); }}voidCTraderSpi::ReqSettlementInfoConfirm(){ CThostFtdcSettlementInfoConfirmField req; memset(&req, 0, sizeof(req)); strncpy(req.BrokerID, BROKER_ID, sizeof(req.BrokerID)-1); strncpy(req.InvestorID, INVESTOR_ID, sizeof(req.InvestorID)-1); int iResult = pUserApi->ReqSettlementInfoConfirm(&req, ++iRequestID); std::cerr << "--->>> Settlement info confirmation: " << ((iResult == 0) ? "Success" : "Failed") << std::endl;}voidCTraderSpi::OnRspSettlementInfoConfirm(CThostFtdcSettlementInfoConfirmField *pSettlementInfoConfirm, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){ std::cerr << "--->>> " << "OnRspSettlementInfoConfirm" << std::endl; if (bIsLast && !IsErrorRspInfo(pRspInfo)) { sleep(1); ReqQryInstrument(); }}voidCTraderSpi::ReqQryInstrument(){ CThostFtdcQryInstrumentField req; memset(&req, 0, sizeof(req)); int iResult = pUserApi->ReqQryInstrument(&req, ++iRequestID); std::cerr << "--->>> Request instrument query: " << ((iResult == 0) ? "Success" : "Failed") << std::endl;}voidCTraderSpi::OnRspQryInstrument(CThostFtdcInstrumentField *pInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){ if(pInstrument && (THOST_FTDC_PC_Futures == pInstrument->ProductClass)) { std::cerr << "--->>> Instrument: " << pInstrument->InstrumentName << "---" << pInstrument->InstrumentID << std::endl; } if (bIsLast && !IsErrorRspInfo(pRspInfo)) { sleep(1); ReqOrderInsert(); }}voidCTraderSpi::ReqOrderInsert(){ CThostFtdcInputOrderField req; memset(&req, 0, sizeof(req)); strncpy(req.BrokerID, BROKER_ID, sizeof(req.BrokerID)-1); strncpy(req.InvestorID, INVESTOR_ID, sizeof(req.InvestorID)-1); strncpy(req.InstrumentID, INSTRUMENT_ID, sizeof(req.InstrumentID)-1); strncpy(req.ExchangeID, ExchangeID, sizeof(req.ExchangeID)-1); strncpy(req.OrderRef, ORDER_REF, sizeof(req.OrderRef)-1); int iNextOrderRef = atoi(ORDER_REF); iNextOrderRef++; snprintf(ORDER_REF, sizeof(ORDER_REF), "%d", iNextOrderRef); req.OrderPriceType = THOST_FTDC_OPT_LimitPrice; req.Direction = DIRECTION; req.CombOffsetFlag[0] = THOST_FTDC_OF_Open; req.CombHedgeFlag[0] = THOST_FTDC_HF_Speculation; req.LimitPrice = LIMIT_PRICE; req.VolumeTotalOriginal = 1; req.TimeCondition = THOST_FTDC_TC_GFD; req.VolumeCondition = THOST_FTDC_VC_AV; req.MinVolume = 1; req.ContingentCondition = THOST_FTDC_CC_Immediately; req.ForceCloseReason = THOST_FTDC_FCC_NotForceClose; req.IsAutoSuspend = 0; req.UserForceClose = 0; req.OrderPriceType = THOST_FTDC_OPT_LimitPrice; req.TimeCondition = THOST_FTDC_TC_IOC; req.VolumeCondition = THOST_FTDC_VC_MV; req.ContingentCondition = THOST_FTDC_CC_Immediately; lOrderTime = time(NULL); int iResult = pUserApi->ReqOrderInsert(&req, ++iRequestID); std::cerr << "--->>> Order insert request: " << ((iResult == 0) ? "Success" : "Failed") << std::endl;}voidCTraderSpi::OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){ std::cerr << "--->>> " << "OnRspError" << std::endl; IsErrorRspInfo(pRspInfo);}voidCTraderSpi::OnRspOrderInsert(CThostFtdcInputOrderField *pInputOrder, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){ std::cerr << "--->>> " << "OnRspOrderInsert" << std::endl; IsErrorRspInfo(pRspInfo);}voidCTraderSpi::ReqOrderAction(CThostFtdcOrderField *pOrder){ static bool ORDER_ACTION_SENT = false; if (ORDER_ACTION_SENT) return; CThostFtdcInputOrderActionField req; memset(&req, 0, sizeof(req)); strncpy(req.BrokerID, pOrder->BrokerID, sizeof(req.BrokerID)-1); strncpy(req.InvestorID, pOrder->InvestorID, sizeof(req.InvestorID)-1); strncpy(req.OrderRef, pOrder->OrderRef, sizeof(req.OrderRef)-1); req.FrontID = FRONT_ID; req.SessionID = SESSION_ID; req.ActionFlag = THOST_FTDC_AF_Delete; strncpy(req.InstrumentID, pOrder->InstrumentID, sizeof(req.InstrumentID)-1); lOrderTime = time(NULL); int iResult = pUserApi->ReqOrderAction(&req, ++iRequestID); std::cerr << "--->>> Order action request: " << ((iResult == 0) ? "Success" : "Failed") << std::endl; ORDER_ACTION_SENT = true;}voidCTraderSpi::OnRspOrderAction(CThostFtdcInputOrderActionField *pInputOrderAction, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){ std::cerr << "--->>> " << "OnRspOrderAction" << std::endl; IsErrorRspInfo(pRspInfo);}voidCTraderSpi::OnRtnOrder(CThostFtdcOrderField *pOrder){ lOrderOkTime = time(NULL); time_t lTime = lOrderOkTime - lOrderTime; std::cerr << "--->>> OnRtnOrder" << pOrder->AccountID << "," << pOrder->InstrumentID << "," << pOrder->VolumeTotalOriginal << "," << pOrder->LimitPrice << "," << pOrder->Direction << "," << pOrder->OrderStatus << "," << pOrder->OrderLocalID << std::endl; if(pOrder->OrderStatus == '1' || pOrder->OrderStatus== '3') { ReqOrderAction(pOrder); }}voidCTraderSpi::OnRtnTrade(CThostFtdcTradeField *pTrade){ std::cerr << "--->>> " << "OnRtnTrade" << std::endl;}
4. 必备文件
ThostFtdcTraderApi.h:CTP 官方交易头文件
libthosttraderapi.so:CTP 官方 64 位 Linux 交易动态库(版本必须匹配)
四、一键编译运行(零报错)
- 创建编译目录 mkdir build && cd build
五、运行成功效果(如图所示)
六、新手必改 4 处(不改无法运行)
FRONT_ADDR:替换为你的期货公司交易前置地址
BROKER_ID:期货公司经纪代码(SimNow 仿真为 9999)
INVESTOR_ID/PASSWORD:你的交易账号与密码
INSTRUMENT_ID:交易合约代码(如 rb2605、IF2605)
具体来说就是下面这个参数
const char FRONT_ADDR[] = "tcp://IP:PORT";TThostFtdcBrokerIDType BROKER_ID = "****";TThostFtdcInvestorIDType INVESTOR_ID = "*****";TThostFtdcPasswordType PASSWORD = "******";TThostFtdcAuthCodeType AuthCode = "******";TThostFtdcAppIDType AppID = "*****";TThostFtdcInstrumentIDType INSTRUMENT_ID = "rb2605";
七、常见问题 1 分钟解决
报错:找不到 libthosttraderapi.so
解决:运行前执行export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
登录失败 解决:检查前置地址、经纪号、账号密码是否正确
无法报单 解决:必须完成结算单确认才可交易
报单被拒 解决:检查合约代码、价格、手数是否合法
八、写在最后
CTP 交易入门根本不用啃官方晦涩文档,一个可运行的 Demo 胜过千言万语。
这套代码是实测可用的极简版本,看懂它,你就掌握了 CTP 交易接口 90% 的核心逻辑,后续对接量化策略、多合约交易、风控系统都会事半功倍。
粉丝福利
需要完整文件包(头文件 + 动态库 + 源码)、CTP 交易Demo的朋友,直接评论区留言,我私信发给你!
后续还会更新:✅ 行情 + 交易合并成一个完整量化框架