因子研究:从行到表

因子领域把准备好的面板变成 IC 研究、分位分桶、tears、artifacts 与导出表。本指南沿着这条路径走,并指出配置选择会改变结果的地方。

输入面板

prepared_factor_data 是唯一接受的输入:

struct prepared_factor_data {
    std::vector<factor_row> rows;      // retained observations
    std::vector<std::string> forward_labels;  // labels aligned with row returns
    factor_loss_report loss_report;    // cleaning-loss accounting
};

每个 factor_row 携带日期、资产、有限的因子值、由调用方给出一基分位标签、可选的分组标签,以及一个与 forward_labels 按位置对齐的前瞻收益向量。这个对齐就是契约:标签顺序与每行向量顺序必须描述同一批列。

分析配置

fincore::factor_analysis_config config;
config.periods = {"1D", "5D"};        // must name prepared labels
config.turnover_periods = {1, 5};     // positive, unique lags
config.by_group = true;               // needs a group column
config.include_portfolio_inputs = false;
const auto model = fincore::analyze_factor(prepared, config);

字段

默认值

改变了什么

long_short

true

按日期对权重和收益去均值;false 保留原始值

group_neutral

false

在日期与分组内去均值;需要分组

equal_weight

false

对正负两侧等权

by_group

false

增加逐组视图;需要分组列

periods

空(全部)

收窄被分析的前瞻收益标签

turnover_periods

{1}

正且唯一的滞后值

time_aggregation

{"M"}

月度 "M"/"ME" 是唯一的原生聚合

include_portfolio_inputs

true

为第一个选中的周期构建组合输入

portfolio_benchmark_period

"1D"

记录进 fingerprint 的非空标签

当面板没有行或标签、某行的收益个数与标签数不符、turnover_periods 或 time_aggregation 为空/重复/非法、基准周期为空、请求的周期不是已准备的整日标签,或在没有分组列时设置 by_group,analyze_factor 会抛 factor_data_error。

模型包含什么

  • 诸如 mean_returns_by_quantile 与 mean_returns_by_date 的聚合视图,以及 IC 与换手表。

  • 设置 by_group 时的逐组视图;这些表是在选择分组行之前用整个 universe 计算的,因此去均值可能跨组。

  • 一个 fingerprint(result_fingerprint),哈希的是输入而不是结果,因此相同输入的两次运行在比较数值之前就已经一致。

分位表有两张,展平方式不同。聚合表(quantile_return_table)是分位 × 标签:quantiles 是行键,labels 是列键。逐日表(quantile_date_return_table)多一条行轴,因此是日期 × 分位 × 标签,一个日期的块宽为 quantiles × labels。手工索引前先读字段注释;两者只有在输出分组时才携带 groups。

Tears 与 artifacts

build_factor_tear_charts(model) 把模型打包成可直接绘图的序列:信息系数、按分位与周期的平均收益、top-minus-bottom 价差、换手、秩自相关,以及可选的事件与分组快照。build_cumulative_returns_by_quantile、build_events_distribution、build_ic_qq、build_quantile_returns_violin 与 build_monthly_ic_heatmap 从同一批表生成单张图。

factor/artifacts.hpp 提供导出路径。它的表是有序契约:列顺序是摘要的一部分,因此即使在中间插入一列、数值完全相同,也属于契约变更。

时点连接

时点辅助函数用参考行上的三个时间戳把信号面板与参考面板连接起来:值何时生效、何时可知,以及被评估的 as-of 日期。参考行只有在 as-of 日期之前既已生效又已可知时才可选;并列时按头文件写明的顺序解决。universe 过滤 —— 例如剔除当天不在指数中的资产 —— 叠加在该截断之上,这就是为什么一个用例最后可能少于输入日期的截面数。

导出与比较

factor/research.hpp 写出 Alpha003 基准使用的研究表。比较是流式的,并且完全在每个计时区间之外运行;不使用 join、dropna、聚合或放宽容差来掩盖缺失观测。不一致会按表、行、列报告,而不是一个失败的布尔值。

由此得到两条实用规则:

  1. 改头文件、改标签或改列顺序都会改变摘要,因此即使数值相同,导出变更也会在成对基准中显现。

  2. tests/fixtures 与 tests/data 下的固定参考文件是证据。把它们的文本当数据,不要当作可以改写的散文。

最小研究

const auto model = fincore::analyze_factor(prepared, config);
const auto tears = fincore::build_factor_tear_charts(model);
const auto& table = model.mean_returns_by_quantile;

// Row keys are quantiles, column keys are labels: stride the row-major buffer.
for (std::size_t q = 0; q < table.quantiles.size(); ++q) {
    for (std::size_t l = 0; l < table.labels.size(); ++l) {
        const double mean = table.means[q * table.labels.size() + l];
        std::printf("Q%d %s %.5f
", table.quantiles[q],
                    table.labels[l].c_str(), mean);
    }
}

聚合表的标签顺序遵循请求,因此在保存任何表时同时打印 mean_returns_by_quantile.labels;一份离开进程的表若缺少标签顺序就是有歧义的。