配方集
针对反复出现的任务给出简短、可运行的答案。每个配方自成一体;规则背后的理由见各指南。
单条序列的滚动指标
#include <fincore/rolling_engine.hpp>
fincore::rolling_engine engine{returns, std::nullopt, 60U, 252.0};
const auto series = engine.compute({fincore::rolling_metric::sharpe,
fincore::rolling_metric::volatility,
fincore::rolling_metric::max_drawdown});
for (const auto& [name, frame] : series) {
const auto& values = frame.get_column<double>(fincore::returns_column);
// values.size() == returns.shape().first - 60 + 1
}
输出从第一个完整窗口开始,因此比输入少 window - 1 行。只有在需要 rolling_metric::beta 时才把基准帧作为第二个参数传入。
把最深的几次回撤列成表
#include <fincore/metrics/drawdown.hpp>
for (const auto& row : fincore::gen_drawdown_table(returns, 5U, "returns")) {
if (std::isnan(row.net_drawdown_percent)) {
continue; // unused row: fewer than five episodes in the series
}
std::printf("%.2f%% peak %lld valley %lld\n",
row.net_drawdown_percent,
static_cast<long long>(*row.peak),
static_cast<long long>(*row.valley));
}
深度是相对前高的正百分比,而 max_drawdown 返回负数小数。高点并列时,窗口从最新的那个开始。
VaR 加一个覆盖检验结论
#include <fincore/risk/backtesting.hpp>
#include <fincore/risk/models.hpp>
fincore::risk_forecast_config config;
config.confidence_level = 0.99;
const auto estimate = fincore::forecast_var(returns, config);
const auto verdict = fincore::backtest_var(forecast, realized, 0.99, 0.05);
if (estimate.status != fincore::risk_estimate_status::ok) {
// insufficient_data or failed: do not report the number as a result
} else if (verdict.status == fincore::risk_backtest_status::inconclusive) {
// fewer than three pairs or fewer than five expected exceptions
}
在 losses_negative 约定下 estimate 对亏损为负。inconclusive 不是通过:250 天 99% 期望 2.5 个例外,低于检验所需的五个。
多日期限的 GARCH VaR
config.method = fincore::risk_forecast_method::garch;
config.garch_kernel = fincore::garch_model::gjr_garch;
config.horizon = 10U;
const auto ten_day = fincore::forecast_var(returns, config);
只有 GARCH 方法会在 horizon 上组合;历史法与 EVT 估计忽略它。未收敛的拟合返回带 note 的 failed,而不是一个数字。
从因子面板取分位均值
const auto model = fincore::analyze_factor(prepared, factor_config);
const auto& table = model.mean_returns_by_quantile;
for (std::size_t q = 0; q < table.quantiles.size(); ++q) {
for (std::size_t l = 0; l < table.labels.size(); ++l) {
std::printf("Q%d %s %.5f\n", table.quantiles[q],
table.labels[l].c_str(),
table.means[q * table.labels.size() + l]);
}
}
行键是分位、列键是标签,缓冲区按行主序。保存表时把同一张表的 labels 一起保存;缺少标签顺序的表是有歧义的。
用修订数据构造时点面板
#include <fincore/factor/pit.hpp>
std::vector<fincore::pit_observation> observations{
{"AAA", {as_of, known_at, effective_from, 1.25}, true},
{"BBB", {as_of, known_at, effective_from, -0.40}, false}, // out of universe
};
const auto prepared = fincore::prepare_pit_factor_data(
observations, prices, evaluation_dates);
const auto model = fincore::analyze_factor(prepared, factor_config);
顺序是被检查的,不是被修正的:known_at 不得早于 as_of,effective_from 不得早于 known_at。在每个评估日期,资产贡献已生效且已知的修订中 (effective_from, known_at, as_of) 最大者;不在 universe 中的资产被静默跳过,这就是某一天截面数可能少于资产数的原因。
可复现的模拟
#include <fincore/simulation/paths.hpp>
const auto paths = fincore::geometric_brownian_motion(
100.0, // initial value
0.08, // annualized drift
0.20, // annualized volatility
1.0, // total years
1.0 / 252.0, // time step
1'000U, // path count
42U); // seed
同一 seed 在本库内复现同一帧。这是可复现性保证,不是与 NumPy 一致的承诺 —— 只有文档写明参考流的函数才带这个承诺。
给报表加一张表并导出
fincore::report_section section;
section.key = "risk";
section.title = "Risk";
fincore::report_table table;
table.key = "var";
table.title = "VaR by confidence level";
table.columns = {"level", "var", "verdict"};
for (const auto& row : rows) {
table.rows.push_back({fincore::report_cell::text_cell(row.level),
fincore::report_cell::number_cell(row.value),
fincore::report_cell::text_cell(row.verdict)});
}
section.tables.push_back(std::move(table));
document.sections.push_back(std::move(section));
const std::string html = fincore::render_html(document, {.language = "zh"});
language = "zh" 会用同一文档渲染中文标签;你提供的单元格按原样输出,因此自定义标签要在调用处翻译。