指标与滚动窗口
本指南说明各指标族之间的关系、数值遵循的约定,以及输入退化时每个族返回什么。它是地图,不能替代 API 参考:每个函数的确切公式与失败规则都写在它自己的文档里。
输入与单位
指标函数接受 fincore::dataframe、列名(多数函数默认 "returns")以及(在相关时)无风险利率。数值是小数收益(0.01 表示 1%)。时间戳是 UTC 纳秒时刻,用于排序、窗口边界与按日期分组,而不是对数值做算术。
NaN 在本库中到处表示“缺失”。它不会被静默替换为 0,也不会被从帧中删掉。每个函数都会说明它是按配对、按列还是完全不丢弃缺失观测;标量比率的默认做法是丢弃 NaN 并用剩下的值计算。
空列或类型不符的列是结构性错误,会抛 input_error。列存在但过短通常返回 NaN 而不是异常,因为“数据不足”是一个值,不是编程错误。
年化
annualization_factor(frequency) 返回每年的期数:
频率 |
因子 |
|---|---|
|
252 |
|
52 |
|
12 |
|
4 |
|
1 |
把显式数值作为第二个参数传入会原样返回,包括 0 或负数 —— 校验责任在调用方。按 sqrt(annualization) 缩放的比率直接使用该因子,因此切换频率会改变夏普比率,但不会改变均值的符号。
收益与比率族
组 |
函数 |
说明 |
|---|---|---|
水平与超额 |
|
|
风险调整 |
|
每个都有自己的偏差除数与退化规则 |
尾部 |
|
尾部由 cutoff 分位定义,而不是由固定样本数定义 |
相对基准 |
|
与基准列按配对对齐 |
两条规则解释了大部分意外:
样本与总体。
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。
回撤族
函数 |
返回 |
|---|---|
|
每个观测点上的滚动回撤帧 |
|
单个深度,为负数或零的小数 |
|
最深一次回撤的峰、谷以及可选的恢复时间戳 |
|
深度列表,或含峰/谷/恢复与持续时间的行 |
|
按该日历桶衡量的最差回撤深度 |
|
最差回撤的恢复时间 |
当多个高点相等时,窗口从最新的那个开始,因此它覆盖造成该谷底的那一段,而不是从第一个等高点算起的全部区间。从不下跌的序列没有回撤窗口,这正是窗口函数返回 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 参考列出每个函数的确切公式与失败规则;当某个数字出乎意料时,函数自己的文档就是权威。