指标与滚动窗口

本指南说明各指标族之间的关系、数值遵循的约定,以及输入退化时每个族返回什么。它是地图,不能替代 API 参考:每个函数的确切公式与失败规则都写在它自己的文档里。

输入与单位

指标函数接受 fincore::dataframe、列名(多数函数默认 "returns")以及(在相关时)无风险利率。数值是小数收益(0.01 表示 1%)。时间戳是 UTC 纳秒时刻,用于排序、窗口边界与按日期分组,而不是对数值做算术。

NaN 在本库中到处表示“缺失”。它不会被静默替换为 0,也不会被从帧中删掉。每个函数都会说明它是按配对、按列还是完全不丢弃缺失观测;标量比率的默认做法是丢弃 NaN 并用剩下的值计算。

空列或类型不符的列是结构性错误,会抛 input_error。列存在但过短通常返回 NaN 而不是异常,因为“数据不足”是一个值,不是编程错误。

年化

annualization_factor(frequency) 返回每年的期数:

频率

因子

daily

252

weekly

52

monthly

12

quarterly

4

yearly

1

把显式数值作为第二个参数传入会原样返回,包括 0 或负数 —— 校验责任在调用方。按 sqrt(annualization) 缩放的比率直接使用该因子,因此切换频率会改变夏普比率,但不会改变均值的符号。

收益与比率族

组

函数

说明

水平与超额

mean_return、annual_return、adjust_returns

adjust_returns 从每个值中减去常数,并让 NaN 保持为 NaN

风险调整

sharpe_ratio、adjusted_sharpe_ratio、sortino_ratio、calmar_ratio、mar_ratio、conditional_sharpe_ratio、deflated_sharpe_ratio、omega_ratio

每个都有自己的偏差除数与退化规则

尾部

conditional_sharpe_ratio、tail_ratio、common_sense_ratio,以及 metrics/risk.hpp 中的 value_at_risk 系列

尾部由 cutoff 分位定义,而不是由固定样本数定义

相对基准

alpha、beta、alpha_beta、up_alpha_beta、down_alpha_beta、tracking_error、information_ratio、capm_r_squared

与基准列按配对对齐

两条规则解释了大部分意外:

  • 样本与总体。 sharpe_ratio 除以样本标准差(一个自由度),而 sortino_ratio 内部的下行偏差把平方偏差除以可用观测数。因此两个比率的分母不可互换。

  • 零不做特殊处理。 零偏差交给 IEEE 算术处理,而不是替换成 NaN 或无穷,除非函数另有说明。当最大回撤为 0 或为正时,calmar_ratio 与 mar_ratio 返回 NaN,因为商没有意义。

adjusted_sharpe_ratio 在原始观测少于 20 个时把偏度/峰度调整夹在 [0.9, 1.1],否则夹在 [0.8, 1.3];原始值少于 4 个时返回 NaN。deflated_sharpe_ratio 把小于 1 的试验次数当作 1,并在估计出的夏普方差非正时返回精确的 0 或 1。

回撤族

函数

返回

drawdown_series

每个观测点上的滚动回撤帧

max_drawdown、second_max_drawdown、third_max_drawdown

单个深度,为负数或零的小数

max_drawdown_window、max_drawdown_underwater

最深一次回撤的峰、谷以及可选的恢复时间戳

get_all_drawdowns、get_all_drawdowns_detailed、get_top_drawdowns

深度列表,或含峰/谷/恢复与持续时间的行

max_drawdown_days / _weeks / _months

按该日历桶衡量的最差回撤深度

max_drawdown_recovery_days / _weeks / _months

最差回撤的恢复时间

当多个高点相等时,窗口从最新的那个开始,因此它覆盖造成该谷底的那一段,而不是从第一个等高点算起的全部区间。从不下跌的序列没有回撤窗口,这正是窗口函数返回 std::optional 的原因。

分布与稳定性统计

skewness、kurtosis、serial_correlation、hurst_exponent、stutzer_index、r_cubed、r_cubed_turtle、win_rate、loss_rate、relative_win_rate、safe_correlation,以及 metrics/perf_stats.hpp 中的业绩统计族

它们共享两条约定:非有限观测被排除在统计之外,并通过可用计数体现;除数采用各函数文档写明的那个。有些使用有偏(总体)形式以匹配参考实现;不要假设函数之间一致。

滚动窗口与表

rolling_engine 在固定窗口上计算一组请求的指标,并为每个指标返回一帧。窗口只取完整窗口:前 window - 1 个观测不产生行,所以输出索引比输入索引短。metrics/rolling.hpp 暴露单项滚动原语,供只需一条序列的调用方使用。

表构造器(metrics/yearly.hpp、metrics/frequencies.hpp 以及 factor/performance.hpp 中的分位表)按日期或周期分组。它们保留各自文档写明的排序规则 —— 有些按日历年与 ISO 周标签排序而不是按桶起点,因此可能出现 1 月的观测排在同一年的 12 月行之后。

示例

#include <fincore/dataframe.hpp>
#include <fincore/metrics/drawdown.hpp>
#include <fincore/metrics/ratios.hpp>

#include <cstdio>

int main() {
    const auto returns = fincore::make_series(
        {fincore::utc_timestamp(2025, 1, 2),
         fincore::utc_timestamp(2025, 1, 3),
         fincore::utc_timestamp(2025, 1, 6),
         fincore::utc_timestamp(2025, 1, 7)},
        "returns", {0.012, -0.02, 0.005, 0.03});

    std::printf("sharpe   %.4f\n", fincore::sharpe_ratio(returns, "returns"));
    std::printf("sortino  %.4f\n", fincore::sortino_ratio(returns, "returns"));
    std::printf("max dd   %.4f\n", fincore::max_drawdown(returns, "returns"));

    if (const auto window = fincore::max_drawdown_underwater(returns, "returns")) {
        std::printf("peak %lld valley %lld\n",
                    static_cast<long long>(window->peak),
                    static_cast<long long>(window->valley));
    }
    return 0;
}

四个观测足够算出夏普比率,但不足以具有代表性;示例展示的是调用形态,不是研究结论。

如何选择指标

  • 要报一个风险调整后的头条数字?用 sharpe_ratio,并说明你用的年化因子。

  • 只惩罚下行?用带显式 required_return 的 sortino_ratio。

  • 判断较短的业绩记录?用 adjusted_sharpe_ratio 或 deflated_sharpe_ratio;两者都试图折算运气,并都在夹紧区间里说明了这一点。

  • 与基准比较?用 alpha/beta 与 information_ratio,并记住对齐规则:两帧按共有的时间戳配对。

  • 概括一条路径?回撤窗口与恢复长度比单个最大值更有信息量,因为它们说明读者会承受多久。

API 参考列出每个函数的确切公式与失败规则;当某个数字出乎意料时,函数自己的文档就是权威。