C++连接CTP接口实战:从零构建量化交易行情与交易策略

1次阅读
没有评论

共计 4542 个字符,预计需要花费 12 分钟才能阅读完成。

image.webp

背景介绍

CTP(Comprehensive Transaction Platform)是国内期货市场的主流交易接口,由上海期货交易所开发。对于量化交易开发者而言,掌握 CTP 接口是进入程序化交易的基础。CTP 提供了行情和交易两套 API,分别用于接收市场数据和发送交易指令。

C++ 连接 CTP 接口实战:从零构建量化交易行情与交易策略

学习 CTP 接口的主要难点在于:

  • 官方文档较为晦涩,示例代码较少
  • 需要处理复杂的网络通信和线程同步问题
  • 期货交易的特殊性(如合约换月、保证金计算等)
  • 生产环境下的稳定性和容错要求较高

技术选型

在量化交易领域,除了 CTP 外还有其他几种常见接口:

  1. CTP vs 券商 PB 接口
  2. CTP:直接连接期货公司,延迟低但功能单一
  3. PB 接口:功能丰富但延迟较高,适合股票交易

  4. CTP vs 飞马 / 易盛

  5. CTP:市场占有率最高,文档相对完善
  6. 飞马:性能优异但学习资料较少

  7. CTP vs REST API

  8. CTP:二进制协议,性能好但开发复杂
  9. REST API:开发简单但延迟高,不适合高频交易

对于期货量化交易,CTP 仍然是大多数专业机构的首选。

核心实现

CTP API 的 C ++ 封装

CTP 官方提供的是 C 语言接口,我们需要用 C ++ 进行面向对象封装。关键点包括:

  • 将回调函数封装为类成员函数
  • 使用智能指针管理资源
  • 异常安全设计
class CtpMdApi {
public:
    CtpMdApi(const std::string& flow_path) {m_pUserApi = CThostFtdcMdApi::CreateFtdcMdApi(flow_path.c_str());
        m_pUserApi->RegisterSpi(this);
    }

    ~CtpMdApi() {m_pUserApi->Release();
    }

    void Connect(const std::string& front_addr) {m_pUserApi->RegisterFront(const_cast<char*>(front_addr.c_str()));
        m_pUserApi->Init();}

    // 行情回调接口
    virtual void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData) override {// 处理行情数据}

private:
    CThostFtdcMdApi* m_pUserApi;
};

多线程处理行情数据

高频行情处理需要特别注意线程安全问题:

  1. 使用无锁队列作为行情缓冲区
  2. 主线程只负责接收原始数据
  3. 工作线程处理业务逻辑
  4. 合理设置线程优先级
// 使用 ConcurrentQueue 作为线程安全队列
moodycamel::ConcurrentQueue<MarketData> data_queue;

// 行情回调线程
void CtpMdApi::OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pData) {
    MarketData data;
    // 转换数据格式
    data_queue.enqueue(data);
}

// 策略处理线程
void strategy_thread() {
    MarketData data;
    while(running) {if(data_queue.try_dequeue(data)) {// 处理数据}
    }
}

简单 K 线策略实现

一个基于 5 分钟 K 线的简单策略逻辑:

  1. 收集 tick 数据合成 K 线
  2. 计算 MA 均线
  3. 价格上穿均线时开多
  4. 价格下穿均线时平仓
class KLineStrategy {
public:
    void onTick(const TickData& tick) {
        // 更新 K 线
        if (kline.isNewBar()) {onNewBar(kline);
        }
        kline.update(tick);
    }

    void onNewBar(const KLine& bar) {
        ma5 = ma5 * 0.8 + bar.close * 0.2;

        if (position == 0 && bar.close > ma5) {sendOrder(BUY, 1);
            position = 1;
        }
        else if (position > 0 && bar.close < ma5) {sendOrder(SELL, position);
            position = 0;
        }
    }

private:
    double ma5 = 0;
    int position = 0;
    KLine kline{300}; // 5 分钟 K 线
};

完整代码示例

下面是一个完整的 CTP 行情接收示例(交易接口类似):

#include <ThostFtdcMdApi.h>
#include <iostream>
#include <memory>
#include <string>

class MyMdSpi : public CThostFtdcMdSpi {
public:
    MyMdSpi(CThostFtdcMdApi* pUserApi) : m_pUserApi(pUserApi) {}

    void OnFrontConnected() override {
        std::cout << "Connected to CTP server" << std::endl;
        // 登录请求
        CThostFtdcReqUserLoginField req{};
        strcpy(req.BrokerID, "9999");
        strcpy(req.UserID, "123456");
        strcpy(req.Password, "password");
        m_pUserApi->ReqUserLogin(&req, ++m_requestId);
    }

    void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, 
                        CThostFtdcRspInfoField *pRspInfo, 
                        int nRequestID, bool bIsLast) override {if (pRspInfo && pRspInfo->ErrorID != 0) {
            std::cerr << "Login failed:" << pRspInfo->ErrorMsg << std::endl;
            return;
        }

        std::cout << "Login successful" << std::endl;

        // 订阅行情
        const char* instruments[] = {"rb2210", "hc2210"};
        m_pUserApi->SubscribeMarketData(const_cast<char**>(instruments), 2);
    }

    void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData) override {
        printf("InstrumentID: %s, LastPrice: %.2f, Volume: %d\n",
               pDepthMarketData->InstrumentID, pDepthMarketData->LastPrice, 
               pDepthMarketData->Volume);
    }

private:
    CThostFtdcMdApi* m_pUserApi;
    int m_requestId = 0;
};

int main() {
    std::string flow_path = "./md_flow/";
    std::string front_addr = "tcp://180.168.146.187:10131";

    auto pUserApi = CThostFtdcMdApi::CreateFtdcMdApi(flow_path.c_str());
    auto pSpi = std::make_unique<MyMdSpi>(pUserApi);

    pUserApi->RegisterSpi(pSpi.get());
    pUserApi->RegisterFront(const_cast<char*>(front_addr.c_str()));
    pUserApi->Init();

    std::cout << "Press any key to exit..." << std::endl;
    std::cin.get();

    pUserApi->Release();
    return 0;
}

性能优化

处理高频行情时需要注意以下几点:

  1. 减少内存分配 :预分配内存池,避免频繁 new/delete
  2. 批量处理 :积累一定数量 tick 后批量处理
  3. SIMD 优化 :使用 AVX 指令加速指标计算
  4. CPU 亲和性 :绑定关键线程到特定 CPU 核心
  5. 零拷贝设计 :尽量使用引用而非值传递
// 使用内存池的例子
class TickPool {
public:
    TickData* allocate() {if (free_list.empty()) {auto block = new TickData[BLOCK_SIZE];
            pool.push_back(block);
            for (int i = 0; i < BLOCK_SIZE; ++i) {free_list.push(&block[i]);
            }
        }
        auto p = free_list.top();
        free_list.pop();
        return p;
    }

    void deallocate(TickData* p) {free_list.push(p);
    }

private:
    static constexpr int BLOCK_SIZE = 1024;
    std::vector<TickData*> pool;
    std::stack<TickData*> free_list;
};

避坑指南

  1. 连接失败
  2. 检查防火墙设置
  3. 确认前置机地址正确
  4. 测试网络连通性

  5. 登录问题

  6. 检查 BrokerID、账号密码
  7. 确认账号有 API 权限
  8. 注意密码可能需要加密

  9. 行情接收异常

  10. 检查合约代码是否正确
  11. 确认已订阅合约
  12. 注意合约换月情况

  13. 交易报错

  14. 检查保证金是否充足
  15. 确认合约处于可交易状态
  16. 注意交易所的流控限制

安全性考量

  1. 网络隔离
  2. 交易系统部署在内网
  3. 使用 VPN 访问

  4. 权限控制

  5. 最小权限原则
  6. 单独的交易账户

  7. 风控措施

  8. 单笔最大亏损限制
  9. 每日最大亏损限制
  10. 自动熔断机制
class RiskManager {
public:
    bool checkOrder(const Order& order) {
        // 检查单笔最大手数
        if (order.volume > max_volume_per_order) {return false;}

        // 检查持仓限额
        if (position + order.volume > max_position) {return false;}

        // 检查当日已亏损
        if (daily_pnl < -max_daily_loss) {return false;}

        return true;
    }

private:
    int max_volume_per_order = 10;
    int max_position = 100;
    double max_daily_loss = 10000.0;
    double daily_pnl = 0;
    int position = 0;
};

总结与展望

本文介绍了使用 C ++ 连接 CTP 接口实现量化交易系统的基础框架。掌握了这些技术后,开发者可以进一步探索:

  1. 更复杂的策略模型(如机器学习策略)
  2. 跨品种套利策略
  3. 高频做市策略
  4. 分布式交易系统架构
  5. 实盘风控体系完善

量化交易是一个需要不断学习的领域,建议初学者从简单的策略开始,逐步积累经验,同时要特别注意风险控制。希望本文能为你的量化交易之旅提供一个良好的起点。

正文完
 0
评论(没有评论)