当前位置:首页>Linux>CTP 从 0 到 1:Linux 下 C++ 行情完整 Demo(可直接编译运行)

CTP 从 0 到 1:Linux 下 C++ 行情完整 Demo(可直接编译运行)

  • 2026-10-11 08:43:19
CTP 从 0 到 1:Linux 下 C++ 行情完整 Demo(可直接编译运行)
CTP 作为期货量化交易的标准接口,是每个量化开发者必须掌握的核心技能,但很多新手卡在环境配置、编译报错、接口调用上,折腾几天都跑不通一个最简单的行情 Demo。
今天直接给你一套零门槛、可直接编译运行的 Linux C++ CTP 行情完整 Demo,从环境搭建到代码运行,一步到位,复制粘贴就能用,彻底告别 CTP 入门难的问题!

一、先搞懂:这篇 Demo 能给你什么?

1、纯原生 C++ 实现

不依赖第三方框架,底层逻辑一目了然

2、Linux 环境一键编译

解决 90% 新手遇到的链接、编译报错

3、完整行情接收

实时获取期货合约最新价、买一卖一、成交量、持仓量等全量行情数据

4、注释超详细

每行关键代码都有说明,新手也能看懂

5、即拿即用

替换期货公司账号信息,直接运行接收实盘 / 仿真行情

二、前置准备(1 分钟搞定)

1. 环境要求

系统:Ubuntu/CentOS 等任意 Linux 发行版(64 位)

编译器:g++(安装命令:sudo apt install g++或sudo yum install gcc-c++)

CTP 行情库:官方thostmduserapi.so动态库(我已集成在 Demo 里,无需单独下载)

2. 核心文件说明

Demo 只包含 3 个核心文件,极简干净:
MdDemo/
├── testMdApi.cpp
├── MdSpi.h
└── MdSpi.cpp

三、完整可编译源码(直接复制)

1. 主程序:testMdApi.cpp

#include"./include/ThostFtdcMdApi.h"#include"MdSpi.h"#include<cstdlib>#include<iostream>// 全局变量CThostFtdcMdApi* pUserApi = NULL;const char FRONT_ADDR[] = "tcp://ip:port";int iRequestID = 0;intmain(){    pUserApi = CThostFtdcMdApi::CreateFtdcMdApi();    if(!pUserApi) {        std::cerr << "Error: Failed to create MD API instance" << std::endl;        return 1;    }    CThostFtdcMdSpi* pUserSpi = new CMdSpi();    if(!pUserSpi) {        std::cerr << "Error: Failed to create MD SPI instance" << std::endl;        return 2;       }    pUserApi->RegisterSpi(pUserSpi);    pUserApi->RegisterFront(const_cast<char*>(FRONT_ADDR));    pUserApi->Init();    pUserApi->Join();    // 清理    pUserApi->Release();//#pragma GCC diagnostic push//#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"        delete pUserSpi;//#pragma GCC diagnostic pop    return 0;}

2、SPI回调类头文件 MdSpi.h

#pragma once#include"./include/ThostFtdcMdApi.h"#include<string>class CMdSpi : public CThostFtdcMdSpi{public:    virtual ~CMdSpi() {}    virtualvoidOnRspError(CThostFtdcRspInfoField *pRspInfo,        int nRequestID, bool bIsLast);    virtualvoidOnFrontDisconnected(int nReason);    virtualvoidOnHeartBeatWarning(int nTimeLapse);    virtualvoidOnFrontConnected();    virtualvoidOnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin,        CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);    virtualvoidOnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument,        CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);    virtualvoidOnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument,        CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);    virtualvoidOnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData);private:    voidReqUserLogin();    voidSubscribeMarketData();    boolIsErrorRspInfo(CThostFtdcRspInfoField *pRspInfo);};

3、SPI回调类CPP文件 MdSpi.CPP

#include"MdSpi.h"#include<iostream>#include<cstring>#include<cstdio>#include<ctime>#include<fstream>#include<iomanip>#include<limits>// USER_API参数extern CThostFtdcMdApi* pUserApi;// 配置参数const TThostFtdcBrokerIDType BROKER_ID = "9999";const TThostFtdcInvestorIDType INVESTOR_ID = "";const TThostFtdcPasswordType PASSWORD = "";char* ppInstrumentID[] = {(char*)"rb2605",(char*)"AP605"};const int iInstrumentID = 2;// 请求编号extern int iRequestID;time_t pos = 0;std::ofstream OutStream;// C++98 兼容的无效浮点数定义namespace {    const double InvalidDouble = 1.0 / 0.0; // 产生inf,可通过isinf检测}boolCMdSpi::IsErrorRspInfo(CThostFtdcRspInfoField *pRspInfo){    bool bResult = (pRspInfo && (pRspInfo->ErrorID != 0));    if (bResult)    {        std::cerr << "--->>> ErrorID=" << pRspInfo->ErrorID                  << ", ErrorMsg=";        if(pRspInfo->ErrorMsg)             std::cerr << pRspInfo->ErrorMsg;        std::cerr << std::endl;    }    return bResult;}voidCMdSpi::OnRspError(CThostFtdcRspInfoField *pRspInfo,        int nRequestID, bool bIsLast){    std::cerr << "--->>> "<< __FUNCTION__ << std::endl;    IsErrorRspInfo(pRspInfo);}voidCMdSpi::OnFrontDisconnected(int nReason){    std::cerr << "--->>> " << __FUNCTION__ << std::endl;    std::cerr << "--->>> Reason = " << nReason << std::endl;}voidCMdSpi::OnHeartBeatWarning(int nTimeLapse){    std::cerr << "--->>> " << __FUNCTION__ << std::endl;    std::cerr << "--->>> nTimerLapse = " << nTimeLapse << std::endl;}voidCMdSpi::OnFrontConnected(){    std::cerr << "--->>> " << __FUNCTION__ << std::endl;    ReqUserLogin();}voidCMdSpi::ReqUserLogin(){    CThostFtdcReqUserLoginField req;    std::memset(&req, 0, sizeof(req));    std::strncpy(req.BrokerID, BROKER_ID, sizeof(req.BrokerID) - 1);    std::strncpy(req.UserID, INVESTOR_ID, sizeof(req.UserID) - 1);    std::strncpy(req.Password, PASSWORD, sizeof(req.Password) - 1);    int iResult = pUserApi->ReqUserLogin(&req, ++iRequestID);    std::cerr << "--->>> 发送用户登录请求: " << ((iResult == 0) ? "成功" : "失败") << std::endl;}voidCMdSpi::OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin,        CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){    std::cerr << "--->>> " << __FUNCTION__ << std::endl;    if (bIsLast && !IsErrorRspInfo(pRspInfo))    {        std::cerr << "--->>> 获取当前交易日 = " << pUserApi->GetTradingDay() << std::endl;        OutStream.open("quot.csv", std::ios::out | std::ios::app);        if(!OutStream.is_open())        {            std::cerr << "输出文件路径错误" << std::endl;            return;        }        SubscribeMarketData();     }}voidCMdSpi::SubscribeMarketData(){    int iResult = pUserApi->SubscribeMarketData(ppInstrumentID, iInstrumentID);    std::cerr << "--->>> 发送行情订阅请求: " << ((iResult == 0) ? "成功" : "失败") << std::endl;}voidCMdSpi::OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData){    if(pDepthMarketData)    {        // 兼容C++98的无效值检测        if(!(pDepthMarketData->LastPrice == pDepthMarketData->LastPrice) || // NaN检测           pDepthMarketData->LastPrice == InvalidDouble)        {            pDepthMarketData->LastPrice = 0.00;        }        // 其他字段同理...        char szRec[256]={0};        std::snprintf(szRec, sizeof(szRec), "%s,%s,%d,%s,%.2f,%d,%.2f,%d,%.2f",             pDepthMarketData->TradingDay,             pDepthMarketData->UpdateTime,            pDepthMarketData->UpdateMillisec,             pDepthMarketData->InstrumentID,             pDepthMarketData->BidPrice1,            pDepthMarketData->BidVolume1,             pDepthMarketData->AskPrice1,             pDepthMarketData->AskVolume1,            pDepthMarketData->LastPrice);        OutStream << szRec << "\n";        OutStream.flush();        std::cerr << "--->>> "<< szRec << std::endl;    }}voidCMdSpi::OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument,    CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){    std::cerr << __FUNCTION__ << std::endl;    if (pSpecificInstrument)        std::cerr << "InstrumentID: " << pSpecificInstrument->InstrumentID << std::endl;    IsErrorRspInfo(pRspInfo);}voidCMdSpi::OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument,    CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){    std::cerr << __FUNCTION__ << std::endl;    if (pSpecificInstrument)        std::cerr << "InstrumentID: " << pSpecificInstrument->InstrumentID << std::endl;    IsErrorRspInfo(pRspInfo);}

4. 必备文件(直接下载 / 复制)

ThostFtdcMdApi.h

CTP 官方行情头文件(直接从期货公司官网下载,或私信我获取)

libthostmduserapi.so

CTP 官方 64 位 Linux 动态库(必须和头文件版本匹配)

四、一键编译运行(零报错)

1、进入到代码运行目录

cd ~/code/MdDemo

2、创建build文件夹

mkdir build

3、进入build目录

cd build

4、编译

cmake ..
make

5、进入到执行程序目录

cd ../Bin/

6、运行程序

./MdDemo

五、运行成功效果(如图所示)

六、新手必改的 3 个地方(不修改无法运行)

FRONT_ADDR替换为你的期货公司行情前置地址

ppInstrumentID替换为你需要订阅的合约

iInstrumentID根据ppInstrumentID来填写实际订阅数量

七、常见问题 1 分钟解决

报错:找不到 libthostmduserapi.so

解决:执行export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH

订阅失败

解决:合约代码写错(如 rb2605 小写、空格)

八、写在最后

CTP 入门根本不用啃官方晦涩的文档,一个可运行的 Demo 胜过千言万语。
这套代码是我实测可用的极简版本,没有任何冗余代码,完全贴合新手入门需求:看懂它,你就掌握了 CTP 行情接口 80% 的核心逻辑,后续对接交易接口、写量化策略都会事半功倍。

粉丝福利

需要完整文件包(头文件 + 动态库 + 源码)、CTP 行情Demo的朋友,直接评论区扣「1」,我私信发给你!
后续还会更新:✅ CTP 交易接口完整 Demo

最新文章

随机文章