You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用boost.python将pandas多层索引列DataFrame导入C++完成回测代码迁移

基于Boost.Python实现pandas多层索引DataFrame C++侧读取的落地方案

本方案完全保留原有Python代码执行逻辑,改动量极小,无需复杂的C++侧DataFrame实现即可快速落地。

核心思路

不强行在C侧实现多层索引CSV解析、DataFrame操作等复杂逻辑,采用「Python负责数据加载+行筛选,C负责核心回测计算」的混合架构,仅通过Boost.Python做极简的数据透传即可。

具体实现步骤

  • 原有Python侧数据读取代码完全不动:保留load_data读取多层索引DataFrame的逻辑,CSV解析、索引筛选、行切片全部用成熟的pandas实现,避免踩多层表头解析的坑
  • C侧策略类、Broker类的接口和原有Python类完全对齐:方法名、入参、返回值结构和原Python代码完全一致,仅把计算逻辑用C重写
  • 多层索引行数据极简透传:Python侧把需要传给C的prev_fs_row、fs_row、indi_row直接调用to_dict()方法转为嵌套字典,结构自动适配为{股票代码: {字段名: 字段值}},Boost.Python可直接解析为C的std::map<std::string, std::map<std::string, 对应类型>>,完全不需要处理pandas内部数据结构
  • 结果回传逻辑对齐:C++侧计算完成后把buy_signal、sell_signal、cash、position按原有结构返回Python,原有的update_data、performance统计逻辑完全不用修改

核心代码示例

C++侧实现与Boost.Python绑定代码

#include <boost/python.hpp>
#include <map>
#include <vector>
#include <string>
#include <algorithm>

using namespace boost::python;

// 类型定义和Python侧数据结构完全对齐
using FsRow = std::map<std::string, std::map<std::string, std::string>>;
using IndiRow = std::map<std::string, std::map<std::string, double>>;

class No_Strategy {
public:
    std::vector<std::string> codes;
    int unit;
    double cash;
    std::vector<int> buy_signal;
    std::vector<int> sell_signal;
    std::vector<int> position;
    int pass_st;
    FsRow prev_fs;
    FsRow fs;
    IndiRow indi;

    No_Strategy(list code_list, int unit_, double cash_, list position_list) {
        // 把Python入参转为C++类型
        for (int i=0; i<len(code_list); i++) codes.push_back(extract<std::string>(code_list[i]));
        unit = unit_;
        cash = cash_;
        buy_signal.resize(codes.size(), 0);
        sell_signal.resize(codes.size(), 0);
        position.resize(codes.size(), 0);
        for (int i=0; i<len(position_list); i++) position[i] = extract<int>(position_list[i]);
        pass_st = 0;
    }

    void set_data(dict prev_fs_dict, dict fs_dict, dict indi_dict) {
        // 直接把Python传过来的字典转成C++ map,一行代码完成多层索引数据解析
        prev_fs = extract<FsRow>(prev_fs_dict);
        fs = extract<FsRow>(fs_dict);
        indi = extract<IndiRow>(indi_dict);
    }

    std::pair<int, int> _strat(std::map<std::string, std::string>& prev_fs, std::map<std::string, std::string>& curr_fs, std::map<std::string, double>& curr_indi) {
        // 逻辑完全抄原Python代码,一字不差对齐
        long long prev_rev = 0, curr_rev = 0, prev_ni = 0, curr_ni = 0, curr_asset = 0, noncurr_asset = 0;
        try {
            std::string prev_str = prev_fs["매출액"];
            prev_str.erase(std::remove(prev_str.begin(), prev_str.end(), ','), prev_str.end());
            prev_rev = std::stoll(prev_str);
            std::string curr_str = curr_fs["매출액"];
            curr_str.erase(std::remove(curr_str.begin(), curr_str.end(), ','), curr_str.end());
            curr_rev = std::stoll(curr_str);
        } catch(...) {
            pass_st++;
            return {0, 0};
        }

        try {
            std::string prev_str = prev_fs["당기순이익"];
            prev_str.erase(std::remove(prev_str.begin(), prev_str.end(), ','), prev_str.end());
            prev_ni = std::stoll(prev_str);
            std::string curr_str = curr_fs["당기순이익"];
            curr_str.erase(std::remove(curr_str.begin(), curr_str.end(), ','), curr_str.end());
            curr_ni = std::stoll(curr_str);
        } catch(...) {
            pass_st++;
            return {0, 0};
        }

        try {
            std::string curr_str = curr_fs["유동자산"];
            curr_str.erase(std::remove(curr_str.begin(), curr_str.end(), ','), curr_str.end());
            curr_asset = std::stoll(curr_str);
            std::string noncurr_str = curr_fs["비유동자산"];
            noncurr_str.erase(std::remove(noncurr_str.begin(), noncurr_str.end(), ','), noncurr_str.end());
            noncurr_asset = std::stoll(noncurr_str);
        } catch(...) {
            pass_st++;
            return {0, 0};
        }

        // 买卖策略完全和原Python对齐,修改时直接改这里即可
        if (curr_indi["golden_cross"] == 1 || curr_indi["rsi_k"] < 0.65) {
            return {1, 0};
        }
        if (curr_indi["dead_cross"] == 1) {
            return {0, 1};
        }
        return {0, 0};
    }

    void run() {
        // 逻辑完全抄原Python代码
        for (int i=0; i<codes.size(); i++) {
            std::string code = codes[i];
            buy_signal[i] = 0;
            sell_signal[i] = 0;
            int valid = 0;

            if (prev_fs.count(code) == 0 || fs.count(code) == 0 || indi.count(code) == 0) {
                pass_st++;
                continue;
            }

            auto& prev_fs_cell = prev_fs[code];
            auto& curr_fs_cell = fs[code];
            auto& curr_indi_cell = indi[code];
            if (prev_fs_cell.empty() || curr_fs_cell.empty()) {
                pass_st++;
                continue;
            }

            auto [buy, sell] = _strat(prev_fs_cell, curr_fs_cell, curr_indi_cell);
            double price = curr_indi_cell["close"];
            if (buy) {
                if (cash >= unit * price) {
                    buy_signal[i] = unit;
                    position[i] += unit;
                    cash -= unit * price;
                }
            } else if (sell) {
                if (position[i] > 0) {
                    int sell_num = position[i] - position[i]/2;
                    sell_signal[i] = sell_num;
                    position[i] = position[i]/2;
                    cash += price * sell_num;
                }
            }
        }
    }

    // 把C++结果转成Python可读取的结构
    list get_buy_signal() { list l; for (auto v: buy_signal) l.append(v); return l; }
    list get_sell_signal() { list l; for (auto v: sell_signal) l.append(v); return l; }
    list get_position() { list l; for (auto v: position) l.append(v); return l; }
    double get_cash() { return cash; }
    int get_pass_st() { return pass_st; }
};

// Boost.Python绑定导出
BOOST_PYTHON_MODULE(cpp_backtest) {
    class_<No_Strategy>("No_Strategy", init<list, int, double, list>())
        .def("set_data", &No_Strategy::set_data)
        .def("run", &No_Strategy::run)
        .def("get_buy_signal", &No_Strategy::get_buy_signal)
        .def("get_sell_signal", &No_Strategy::get_sell_signal)
        .def("get_position", &No_Strategy::get_position)
        .def("get_cash", &No_Strategy::get_cash)
        .def("get_pass_st", &No_Strategy::get_pass_st);
}

Python侧对接修改(仅需改3行代码,其余完全不动)

# 原有import部分替换No_Strategy导入即可
import pandas as pd
import numpy as np
from utils import load_data, load_list_csv, to_int
from cpp_backtest import No_Strategy # 替换原来的Python类导入

# ---------------------- 以下原有代码完全不用修改 ----------------------
class Broker():
    # 原有Broker代码不变
    def run(self):
        for date in self.time_index:
            if date.year == 2021:
                break
            else:
                prev_fs_row = self.fs.loc[date.year-1]
                fs_row = self.fs.loc[date.year]
                indi_row = self.indi.loc[date]
                # 仅这里加3行代码转字典传给C++
                strategy = self.strategy(self.codes, self.unit, self.cash, self.position)
                strategy.set_data(prev_fs_row.to_dict(), fs_row.to_dict(), indi_row.to_dict())
                strategy.run()
                # 回传结果到Python,和原有逻辑对齐
                self.cash = strategy.get_cash()
                self.position = list(strategy.get_position())
                self.buy_signal.loc[date] = list(strategy.get_buy_signal())
                self.sell_signal.loc[date] = list(strategy.get_sell_signal())
                self.pass_st += strategy.get_pass_st()

# 原有主逻辑代码完全不变

方案优势

  • 落地成本极低:不需要理解pandas多层索引的底层存储结构,不需要自己写CSV多层表头解析代码,仅需重写策略逻辑+简单绑定
  • 结果100%对齐:所有计算逻辑完全照搬原Python代码,不会出现逻辑偏差
  • 性能提升显著:回测核心循环全部在C++侧运行,Python仅负责数据加载和最终统计,速度可提升10~100倍

内容的提问来源于stack exchange,提问作者hyemin ju

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 20:24:01