从 Python fincore 迁移

fincore_cpp 是原生库,而不是 Python 绑定。它不提供 import fincore 兼容性、Python 异常继承、pandas 视图或 NumPy dtype。迁移工作流时,应匹配原生的类型化输入和输出,而不是机械地重命名 Python 对象。

映射表

Python / pandas

原生等价物

说明

pd.DataFrame 与 DatetimeIndex

fincore::dataframe

索引是带符号 64 位 UTC 纳秒时间戳

series.values

get_column<double>("name")

以 span/视图返回;若需在帧之后存活请复制

series.pct_change()

fincore::simple_returns(frame, "close")

丢弃第一个观测,保留较晚的时间戳

series.shift(n)

不提供原语

多数 API 显式接受滞后(turnover_periods)

series.rolling(w).mean()

rolling_engine{..., w, 252.0}.compute(...)

仅完整窗口

series.std(ddof=1)

calc_distribution_stats(values).std

原生字段除以计数(总体),不是 count - 1

df.groupby(level=0).mean()

aggregate_returns(...) / 表构造器

分桶顺序是显式的,不是插入顺序

np.nan

双精度列中的 NaN

视为缺失,绝不当成 0

raise ValueError(...)

fincore::input_error / factor_data_error

带分类、代码与结构化字段

df.to_html() / to_excel()

render_html(document, ...) / render_xlsx(document)

渲染一份类型化文档,而不是帧

corr()、cov()

correlation_breakdown、beta、tracking_error

按共有时间戳成对对齐

迁移检查清单

  1. 找到原生符号。 每个公共符号都在 API 清单里;若某个 Python 调用没有原生对应物,那就是未实现,假装实现的包装层迟早会漂移。

  2. 检查单位契约。 原生函数接受小数收益而不是百分比,且从不做隐式换算。

  3. 检查对齐契约。 成对 API 按共有时间戳连接;严格 API 会拒绝重复或乱序索引。依赖位置顺序的 Python 查询必须重写,而不是直译。

  4. 检查失败契约。 这里的 NaN 结果与异常含义不同;见约定。

  5. 保留 oracle 预算。 复用你在 Python 里用的同一 fixture 与参考值,这样移植是对照证据验证,而不是自我验证。

容易踩到的已知差异

  • 带符号零与 NaN 载荷。 摘要把浮点序列化为 IEEE-754 位模式;部分写出器会规范负零,因为字节会被哈希。

  • 年化默认值。 日频为 252。用 365 的 Python 辅助函数会相差该比值的平方根。

  • 偏差除数。 calc_distribution_stats 报告总体偏差,而 pandas 默认 ddof=1;短样本上差异可见,只有渐近才消失。它也不过滤 NaN。

  • 回撤符号。 max_drawdown 返回负数小数,而 gen_drawdown_table 报告相对前高的正百分比。

  • 回测结论。 inconclusive 是真实结果而不是失败;五期望例外的下限是刻意设置的。

  • 可选路径。 标记为可选的 provider 或扩展接口不是 parity 承诺。迭代记录列出了已实现与推迟的部分。

这里的 parity 指什么

仓库的 parity 记录描述已实现的原生接口及其声明的差异。迭代 8 的记录是权威范围快照;后续性能工作不会追溯扩大它。依赖与平台状态请查有日期的验收记录,而不是从阶段编号推断。

做迁移评审时,把每个 Python 调用映射到公共原生符号,验证其单位与对齐契约,并保留同样的 fixture 或 oracle 预算。同时改动代码与参考值的移植什么也证明不了。