配方集

针对反复出现的任务给出简短、可运行的答案。每个配方自成一体;规则背后的理由见各指南。

单条序列的滚动指标

#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" 会用同一文档渲染中文标签;你提供的单元格按原样输出,因此自定义标签要在调用处翻译。