Metrics
Return, risk, drawdown, distribution, rolling, yearly and performance-attribution statistics.
See the usage guide for task-oriented examples. Signatures below are generated from the public headers at the same commit as the rest of this site.
-
class matrix
Row-major dense matrix used where the pinned Python source consumes a NumPy 2-D array.
values().size()is alwaysrows() * columns().Public Functions
-
matrix() = default
Constructs an empty 0 x 0 matrix.
-
matrix(std::size_t row_count, std::size_t column_count, std::vector<double> values)
Constructs a
row_countxcolumn_countmatrix from a row-major buffer.Throws
input_errorwhen the shape is not representable orvalues.size()differs fromrow_count * column_count.
-
std::size_t rows() const noexcept
Returns the number of rows.
-
std::size_t columns() const noexcept
Returns the number of columns.
-
std::span<const double> values() const noexcept
Returns the row-major element buffer, sized
rows() * columns().
-
std::span<const double> row(std::size_t row_index) const
Returns a view of
row_index; throwsinput_errorwhen out of range.
-
double at(std::size_t row_index, std::size_t column_index) const
Returns the element at (
row_index,column_index); throwsinput_errorwhen either index is out of range.
-
double &at(std::size_t row_index, std::size_t column_index)
Returns a mutable reference to the element at (
row_index,column_index); throwsinput_errorwhen either index is out of range.
-
matrix() = default
-
struct bootstrap_metric_stats
Bootstrap distribution of one performance metric.
Public Members
-
std::string name
Display name of the metric.
-
std::vector<double> samples
Statistic of each draw, in draw order.
-
distribution_stats summary
Summary of
samples.
-
std::string name
-
struct daily_consecutive_statistics
Daily run statistics from a return series.
-
struct distribution_stats
Descriptive summary of a set of metric samples.
Every field is NaN for an empty sample. Quantiles use linear interpolation and
stdis the population deviation.Public Members
-
double mean
Arithmetic mean.
-
double median
50th percentile.
-
double std
Population standard deviation (divides by the count).
-
double p5
5th percentile.
-
double p25
25th percentile.
-
double p75
75th percentile.
-
double p95
95th percentile.
-
double iqr
Interquartile range,
p75 - p25.
-
double mean
-
struct drawdown_period
One underwater episode, from the first observation below the running peak through its valley and optional recovery.
Public Members
-
std::optional<timestamp> recovery
Return to the prior peak; nullopt when the episode never recovers.
-
double value
Depth at the valley, a negative fraction of peak.
-
std::size_t duration_periods
Observations from start to valley.
-
std::optional<std::size_t> recovery_duration_periods
Observations from valley to recovery.
-
std::optional<timestamp> recovery
-
struct drawdown_table_row
One row of gen_drawdown_table.
-
struct drawdown_window
Peak, valley and optional recovery timestamps of a drawdown.
-
struct gpd_risk_estimates_result
Generalized Pareto tail estimates of a return distribution.
-
struct named_returns
One named historical window extracted from a return series.
-
struct perf_attribution_result
Per-date risk exposures and return attribution.
-
struct perf_attribution_statistics
Summary statistics of a perf_attribution_result.
-
enum class fincore::frequency
Compounding period of a return series.
Values:
-
enumerator daily
Daily observations, annualized with 252 periods.
-
enumerator weekly
Weekly observations, annualized with 52 periods.
-
enumerator monthly
Monthly observations, annualized with 12 periods.
-
enumerator quarterly
Quarterly observations, annualized with 4 periods.
-
enumerator yearly
Yearly observations, annualized with 1 period.
-
enumerator daily
-
enum class fincore::return_aggregation_period
Calendar bucket used by aggregate_returns.
Values:
-
enumerator weekly
ISO-8601 weeks, keyed by week number within the year.
-
enumerator monthly
Calendar months.
-
enumerator quarterly
Calendar quarters.
-
enumerator yearly
Calendar years.
-
enumerator weekly
-
dataframe fincore::adjust_returns(const dataframe &returns, double adjustment_factor, std::string_view column = returns_column)
Returns a copy of
returnswithadjustment_factorsubtracted from each value incolumn.Timestamps are preserved and the result is stored under
column; NaN values remain NaN. Throwsinput_errorwhencolumnis absent, is not a double column, or does not match the frame index length.
-
double fincore::adjusted_sharpe_ratio(const dataframe &returns, double risk_free = 0.0, std::string_view column = returns_column)
Returns the Sharpe ratio adjusted for the skewness and kurtosis of the return distribution.
The Sharpe ratio is annualized with
daily_annualizationand multiplied by a dampened adjustment built from the biased skewness and excess kurtosis of the non-NaN values. The adjustment is clamped to [0.9, 1.1] for fewer than 20 raw observations and to [0.8, 1.3] otherwise. Fewer than four raw values or a NaN Sharpe ratio yield NaN; an embedded NUL incolumnraisesinput_error.
-
dataframe fincore::aggregate_returns(const dataframe &returns, return_aggregation_period period = return_aggregation_period::monthly, std::string_view column = returns_column)
Returns returns compounded into calendar buckets of
period.The output index holds each bucket’s start timestamp (Monday for weeks, clamped to January 1; the first day of the month or quarter; January 1 for years), with integer
yearandperiodcolumns and the compounded return undercolumn. NaN returns are treated as zero.columnmust not bereturn_aggregation_year_columnorreturn_aggregation_period_column; otherwiseinput_erroris thrown.
-
std::pair<dataframe, dataframe> fincore::aligned_series(const dataframe &left, std::string_view left_column, const dataframe &right, std::string_view right_column)
Returns two single-column series aligned on their common timestamps.
Native contract: the common index is emitted in left-frame order (the
align_on_common_indexsemantics), timestamps absent from either frame are dropped, and present NaN values are retained. Both indexes must contain unique timestamps and must not contain the NaT sentinel (numeric_limits<timestamp>::min()); duplicates are ambiguous for keyed alignment and raiseinput_error.Declared source variances: the pinned Python two-Series path uses
pd.concat(..., sort=False)(an outer union that keeps right-only rows as NaN), while its DataFrame and mixed DataFrame/Series paths intersect. This native helper follows the P101 common-index intersection for every input, and emits left order where the source DataFrame.align path sorts ascending.
-
std::pair<dataframe, dataframe> fincore::aligned_series(const dataframe &left, const dataframe &right)
Returns two frames aligned on their common timestamps, keeping every double column of each frame in source column order.
Same uniqueness, NaT, missingness and ordering rules as the single-column overload.
-
double fincore::alpha(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the annualized Jensen alpha on the common timestamps.
NaN pairs are dropped. The mean per-period excess return less beta times the excess benchmark return is compounded at
annualization. Returns NaN when beta is not finite.risk_freeis subtracted from both series.
-
std::pair<double, double> fincore::alpha_beta(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns {alpha, beta} computed on the common timestamps.
Only finite pairs are used. Returns {NaN, NaN} when fewer than two pairs remain,
risk_freeorannualizationis not finite,annualizationis not positive, or either result is not finite.
-
double fincore::alpha_percentile_rank(const dataframe &strategy_returns, std::span<const dataframe> peer_returns, const dataframe &benchmark, double risk_free = 0.0, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the fraction of peer strategy alphas strictly below the strategy’s alpha.
Peer series with fewer than three observations or a NaN alpha are ignored. Returns NaN when the strategy alpha is unavailable or no peer alpha is usable. Embedded NUL characters in the column names raise
input_error.
-
double fincore::annual_active_return(const dataframe &returns, const dataframe &benchmark, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the difference between the annual returns of
returnsandbenchmarkon their common timestamps.Both annual returns use
annualization_factor(period, annualization). Returns NaN when the aligned pair is empty. An unknownfrequencywithout an explicitannualizationraisesinput_error.
-
dataframe fincore::annual_active_return_by_year(const dataframe &returns, const dataframe &benchmark, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the active return per UTC calendar year in column “annual_active_return”.
The two frames are aligned on their common timestamps before bucketing, and each year holds the difference of the two annual returns using
annualization_factor(period, annualization). Years are indexed by January 1 and sorted ascending; an empty aligned pair yields an empty typed frame. An unknownfrequencywithout an explicitannualizationraisesinput_error.
-
dataframe fincore::annual_alpha(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns annual alpha per UTC calendar year, indexed by January 1 and stored in column “alpha”.
Each year uses
annualization_factor(period, annualization)and the same NaN-pair rules as alpha.
-
dataframe fincore::annual_beta(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns annual beta per UTC calendar year, indexed by January 1 and stored in column “beta”.
periodandannualizationare ignored because beta is not annualized.
-
double fincore::annual_return(const dataframe &returns, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the geometric annualized return of
column.Total wealth is raised to
annualization / observation_count, with NaN returns treated as zero. An empty frame yields NaN and non-positive ending wealth yields -1.
-
dataframe fincore::annual_return_by_year(const dataframe &returns, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view column = returns_column)
Returns the annual return per UTC calendar year in column “annual_return”.
Each year is indexed by its January 1 timestamp and uses annual_return with
annualization_factor(period, annualization). Years are sorted ascending, while observations keep their input order inside a year. An empty frame yields an empty typed frame, and an unknownfrequencywithout an explicitannualizationraisesinput_error.
-
double fincore::annual_volatility(const dataframe &returns, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the annualized sample standard deviation of
column.Non-NaN values are used with one degree of freedom and the result is scaled by
sqrt(annualization); fewer than two valid values yield NaN.
-
dataframe fincore::annual_volatility_by_year(const dataframe &returns, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view column = returns_column)
Returns the annualized volatility per UTC calendar year in column “annual_volatility”.
Each year uses annual_volatility, so NaN observations are ignored, with
annualization_factor(period, annualization). Years are indexed by January 1 and sorted ascending. An empty frame yields an empty typed frame, and an unknownfrequencywithout an explicitannualizationraisesinput_error.
-
double fincore::annualization_factor(frequency period, std::optional<double> annualization = std::nullopt)
Returns the number of periods per year used to annualize
period.An explicit
annualizationvalue is returned unchanged; otherwise the defaults are 252, 52, 12, 4 and 1 for daily through yearly observations.
-
double fincore::beta(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the OLS beta of
returnsagainstbenchmarkon their common timestamps.NaN pairs are dropped and
risk_freeis subtracted from both series before the regression. Returns NaN with fewer than two valid pairs, near-zero benchmark variance, or a non-finite covariance/beta result.
-
double fincore::beta_fragility_heuristic(const dataframe &returns, const dataframe &benchmark, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns a heuristic beta-fragility score comparing returns at the low and high ends of the factor-sorted sample with the middle observation.
Requires at least three aligned non-NaN pairs; otherwise returns NaN. Embedded NUL characters in the column names raise
input_error.
-
double fincore::burke_ratio(const dataframe &returns, double risk_free = 0.0, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the annual return in excess of
risk_freedivided by the Burke drawdown risk measure.The risk measure is the square root of the sum of squared drawdown depths; when there are no drawdowns or all are zero, it falls back to the population standard deviation of the negative returns (or of all usable values when none are negative). The annual return is compounded at
annualization. Returns positive infinity when the risk measure is below 1e-10 and the excess annual return is positive, NaN in the corresponding non-positive case, and NaN for fewer than two raw values or no usable value. An embedded NUL incolumnraisesinput_error.
-
double fincore::cal_treynor_ratio(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the annualized Treynor ratio of
returnsagainstbenchmark.Both series are aligned on their common timestamps; the annual return in excess of
risk_freeis divided by the beta of the aligned pair. Returns NaN when fewer than two aligned returns remain or beta is NaN, zero or negative. An embedded NUL in either column name raisesinput_error.
-
distribution_stats fincore::calc_distribution_stats(std::span<const double> values)
Returns the mean, deviation and percentiles of
values.Values are used as given (no NaN filtering); percentiles use linear interpolation at
(n - 1) * p / 100andstddivides by the sample count. An empty span yields NaN in every field.
-
double fincore::calmar_ratio(const dataframe &returns, double risk_free = 0.0, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the annual return in excess of
risk_freedivided by the absolute maximum drawdown.The annual return is compounded at
annualization. Returns NaN when the series never declines (the maximum drawdown is zero or NaN) or the quotient is not finite.
-
double fincore::capm_r_squared(const dataframe &returns, const dataframe &benchmark, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the squared CAPM correlation, clamped to [0, 1].
Returns NaN when the correlation is unavailable.
-
double fincore::capture(const dataframe &returns, const dataframe &benchmark, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the ratio of the annualized geometric return of
returnsto that ofbenchmark.Both series are aligned on their common timestamps and only finite pairs are kept; each side is compounded over its paired observations and annualized with
annualization. Returns NaN when no pair remains, either annual return is non-finite, the benchmark annual return is zero, or the quotient is not finite.
-
double fincore::common_sense_ratio(const dataframe &returns, std::string_view column = returns_column)
Returns the common-sense ratio: the tail ratio times the win rate divided by the loss rate.
Both components are taken from
columnwith their own NaN rules. Returns NaN for fewer than two raw values, a NaN component, or a zero win rate, and positive infinity when every usable observation is a gain. An embedded NUL incolumnraisesinput_error.
-
dataframe fincore::compute_bayes_cone(const dataframe &predicted_returns, double starting_value = 1.0)
Returns the cross-path percentile cone of cumulative predicted wealth.
Each double column of
predicted_returnsis one path and each row is a forecast step, in input order. Wealth starts atstarting_value, is multiplied by1 + returnper row, and at every row the seven linear percentiles across paths are stored in the fixedpercentile_*columns. The input index, including duplicates, is preserved, and the returned frame owns its values.At least one column is required, every column must be a double column matching the frame index, and
starting_value, every return, cumulative wealth and interpolated percentile must be finite; otherwiseinput_erroris thrown, including for unusable shapes and allocation failures. An index-empty frame with typed empty columns yields a typed empty seven-column frame.
-
std::vector<double> fincore::compute_consistency_score(const dataframe &returns_test, const matrix &predicted_returns, std::string_view column = returns_column)
Returns the per-date fraction of cumulative predicted paths strictly below the realized cumulative return.
predicted_returnsis row-major with one row per sampled path and one column per out-of-sample date.returns_testmust have one value per column. Source formula: realized wealth = cumprod(1 + returns) with NaN returns treated as 0 (the sourcecum_returnspolicy), predicted wealth = cumprod(1 + preds) per row, score[t] = |{path : cum_pred[path, t] < realized[t]}| / path_count. Predicted values must be finite with a finite1 + value; realized values may be NaN (treated as 0) but not infinite. Zero paths yields NaN scores for every date, matching the source mean of an empty axis.
-
dataframe fincore::compute_exposures(const dataframe &positions, const dataframe &factor_loadings, std::span<const std::string_view> factor_column_names, std::string_view cash_column_name = cash_column, std::string_view symbol_column_name = attribution_symbol_column)
Returns per-date factor exposures of
positions.For each requested factor, the exposure sums position value times factor loading over the assets, matched by (timestamp, symbol) against
factor_loadings; NaN products are skipped. The result keeps the positions index and one column per name infactor_column_names, in that order. Duplicate position timestamps, empty, duplicated or symbol-colliding factor names, a missing or non-string symbol column, a non-double factor-loading column, a duplicate (timestamp, symbol) key, or a missing key for a positioned asset raiseinput_error.
-
double fincore::conditional_sharpe_ratio(const dataframe &returns, double cutoff = 0.05, double risk_free = 0.0, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the Sharpe ratio of the left tail defined by
cutoff.The cutoff quantile is linearly interpolated at
(n - 1) * cutoff; every value at or below it forms the tail. The tail mean excess overrisk_freeis divided by the tail sample deviation (one degree of freedom) and scaled bysqrt(annualization). Returns NaN whencutoffis not in (0, 1), any raw value is NaN, the tail has fewer than two values, or the deviation is zero or non-finite. An embedded NUL incolumnraisesinput_error.
-
double fincore::conditional_value_at_risk(const dataframe &returns, double cutoff = 0.05, std::string_view column = returns_column)
Returns the mean of all observations at or below the empirical
cutoffquantile ofcolumn.Returns NaN unless every value is finite and
cutofflies in [0, 1].
-
std::string fincore::cone_bounds_column_name(double num_std)
Returns the native column label for one cone bound width, for example cone_1 or cone_-1.
The width is rendered in its shortest round-trip form; a non-finite width raises
input_error.
-
std::map<std::string, double> fincore::consecutive_stats(const dataframe &returns, std::string_view column = returns_column)
Returns a map of the six daily, weekly and monthly up/down run lengths, keyed by their snake_case names.
-
double fincore::cornell_timing(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the Cornell timing measure: the up-market slope minus the down-market slope.
Fits
excess_return = a + b * up + c * down, whereupismax(0, excess_market)anddownismin(0, excess_market), by ordinary least squares and returnsb - c. Only finite pairs are used andrisk_freeis subtracted from both series. Returns NaN with fewer than ten usable pairs, a non-finiterisk_free, a singular design, or a non-finite result.
-
perf_attribution_statistics fincore::create_perf_attrib_stats(const perf_attribution_result &result, double annualization = daily_annualization)
Returns the return summary and per-factor statistics of an attribution.
summaryholds “Annualized Specific Return”, “Annualized Common Return”, “Annualized Total Return”, “Specific Sharpe Ratio”, “Cumulative Specific
Return”, “Cumulative Common Return” and “Total Returns”; annualized values compound with
annualization, and the specific Sharpe ratio also uses it.factor_summaryhas a positional 0..n-1 index with columns “factor”, “Average Risk Factor Exposure” (mean non-NaN exposure), “Annualized Return” and “Cumulative Return” of each factor’s contribution. Mismatched indexes or a missing standard attribution column raiseinput_error.
-
double fincore::cumulative_return(const dataframe &returns, std::string_view column = returns_column)
Returns the total compounded return of
column, treating NaN returns as zero; an empty frame yields NaN.
-
double fincore::cumulative_return(const dataframe &returns, std::initializer_list<std::string_view> legacy_column)
Returns cumulative_return for the legacy single-name column form.
The initializer list may hold at most one name; more than one throws
input_error.
-
double fincore::cumulative_return(const dataframe &returns, double starting_value, std::string_view column = returns_column)
Returns the final cumulative value of
columnscaled bystarting_value.The result is
starting_value * wealthwhenstarting_valueis nonzero andwealth - 1when it is zero. NaN returns are treated as zero.
-
dataframe fincore::cumulative_returns(const dataframe &returns, std::string_view column = returns_column)
Returns the cumulative growth of
columnaswealth - 1at each timestamp, with NaN returns treated as zero.
-
dataframe fincore::cumulative_returns(const dataframe &returns, std::initializer_list<std::string_view> legacy_column)
Returns cumulative_returns for the legacy single-name column form.
The initializer list may hold at most one name; more than one throws
input_error.
-
dataframe fincore::cumulative_returns(const dataframe &returns, double starting_value, std::string_view column = returns_column)
Returns the cumulative growth of
columnscaled bystarting_value.Each output value is
starting_value * wealthwhenstarting_valueis nonzero andwealth - 1when it is zero. NaN returns are treated as zero.
-
dataframe fincore::cumulative_returns_less_costs(const dataframe &returns, std::string_view returns_column_name = returns_column)
Returns cumulative_returns of
returns_column_name.This overload applies no cost deduction.
-
dataframe fincore::cumulative_returns_less_costs(const dataframe &returns, const dataframe &costs, std::string_view returns_column_name = returns_column, std::string_view costs_column_name = returns_column)
Returns cumulative growth after subtracting per-date costs.
returnsandcostsmust have identical indexes; each return is reduced by the cost in the same row before the result is compounded with cumulative_returns. Mismatched indexes, malformed columns or invalid selectors raiseinput_error.
-
daily_consecutive_statistics fincore::daily_consecutive_stats(const dataframe &returns, std::string_view column = returns_column)
Returns the daily run statistics of
column.NaN values break a run. An empty input yields all-NaN fields; the gain/loss fields are also NaN when no run of that sign exists, while the run lengths are 0.
-
double fincore::deflated_sharpe_ratio(const dataframe &returns, double risk_free = 0.0, int num_trials = 1, std::string_view column = returns_column)
Returns the deflated Sharpe ratio probability for
num_trialstrials.NaN observations are dropped and the per-period Sharpe ratio uses a one-degree-of-freedom deviation. The trial-adjusted benchmark uses the sample skewness and kurtosis and the expected maximum of
num_trialsindependent trials (values below one are treated as one). The result is the standard normal CDF of the observed excess over that benchmark, or exactly 0 or 1 when the estimated Sharpe variance is non-positive. Fewer than three usable values yield NaN. An embedded NUL incolumnraisesinput_error.
-
std::pair<double, double> fincore::down_alpha_beta(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns {alpha, beta} using only dates with non-positive benchmark returns (zero is included).
Otherwise follows alpha_beta.
-
double fincore::down_capture(const dataframe &returns, const dataframe &benchmark, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the capture ratio on dates where the benchmark return is strictly negative.
Follows capture on the selected common finite pairs. Returns NaN when no date qualifies or the ratio is not finite.
-
double fincore::down_capture_return(const dataframe &returns, const dataframe &benchmark, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the annualized geometric return of
returnson dates where the benchmark return is strictly negative.Matches up_capture_return apart from selecting dates where the benchmark return is negative, including its NaN and -1 outcomes and its embedded-NUL
input_error.
-
double fincore::downside_risk(const dataframe &returns, double required_return = 0.0, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the annualized downside deviation relative to
required_return.The result is the square root of the mean squared shortfall over all finite observations, scaled by
sqrt(annualization). Returns NaN when there are no finite values orannualizationis negative or non-finite.
-
dataframe fincore::drawdown_series(const dataframe &returns, std::string_view column = returns_column)
Returns the underwater series of
column: wealth relative to its running peak, as a non-positive fraction.NaN returns are treated as zero and the result is stored under
returns_column. An empty frame yields an empty frame.
-
dataframe fincore::ensure_datetime_index_series(const dataframe &frame, frequency period = frequency::daily)
Validates and normalizes a frame’s UTC-nanosecond index.
A present index must be strictly increasing, duplicate-free and free of the NaT sentinel; the returned frame is an owning copy. A frame without an index but with exactly one double column receives the synthetic UTC index the source builds from
pd.date_range("1970-01-01", periods=n, freq=...): daily -> 1970-01-01, 1970-01-02, … weekly -> Sundays from 1970-01-04 monthly -> month ends from 1970-01-31 quarterly -> quarter ends from 1970-03-31 yearly -> 1970-12-31, 1971-12-31, … An empty frame is returned unchanged. Frames with several columns and no index, or with a length mismatch between columns and index, raiseinput_error; the source silently regenerates an index for an unindexed Series only.
-
dataframe fincore::ensure_datetime_index_series(std::span<const double> values, frequency period = frequency::daily, std::string_view column = returns_column)
Returns a single-column frame with a synthetic index built from
values.An empty span returns an empty frame. Values are copied verbatim (NaN retained).
-
double fincore::excess_sharpe(const dataframe &returns, const dataframe &benchmark, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the mean active return divided by its sample deviation.
Both series are aligned on their common timestamps and only finite pairs are kept. The result is not annualized. Returns NaN when fewer than two pairs remain or the tracking error is below 1e-15.
-
std::vector<named_returns> fincore::extract_interesting_date_ranges(const dataframe &returns, std::string_view column = returns_column)
Returns the pinned historical crisis windows that overlap
returns.Each window is inclusive of both UTC endpoints and contributes an entry only when it holds at least one observation. Entries follow the pinned window order, while observations keep their input order and the input column name; each returned frame owns its data. An embedded NUL, empty or invalid
columnraisesinput_error.
-
std::vector<double> fincore::flatten(const dataframe &frame)
Returns a frame flattened to a row-major vector.
Columns are visited in source insertion order and rows in index order, so the result is
row0[col0..colN], row1[col0..colN], .... Only double columns are supported; every column must have the frame index length. Present NaN cells are preserved (the native frame has no absent-cell state); a frame whose columns are longer than its index (unindexed data) raisesinput_error. For raw matrices use thematrixoverload, which returns the stored row-major buffer.
-
std::vector<double> fincore::flatten(const matrix &values)
Returns a copy of the matrix’s stored row-major buffer.
-
dataframe fincore::forecast_cone_bootstrap(const dataframe &in_sample_returns, std::size_t num_days, std::span<const double> cone_std = default_cone_standard_deviations, double starting_value = 1.0, std::size_t num_samples = 1000U, std::uint32_t random_seed = 0U, std::string_view column = returns_column)
Returns the forecast cone by composing simulate_paths and summarize_paths.
Simulated paths use
starting_value,num_samples,random_seedandcolumn; the cone usescone_stdandstarting_value. Errors are those of the two composed functions.
-
dataframe fincore::forecast_cone_bootstrap(const dataframe &in_sample_returns, std::size_t num_days, double cone_std, double starting_value = 1.0, std::size_t num_samples = 1000U, std::uint32_t random_seed = 0U, std::string_view column = returns_column)
Returns forecast_cone_bootstrap for a single cone width.
-
std::string_view fincore::frequency_name(frequency period)
Returns the canonical source name for a native frequency.
-
std::vector<drawdown_table_row> fincore::gen_drawdown_table(const dataframe &returns, std::size_t top = 10, std::string_view column = returns_column)
Returns
toptable rows for the deepest drawdown windows.Ranking matches get_top_drawdowns. Unused rows keep a NaN
net_drawdown_percentand nullopt timestamps.
-
std::vector<double> fincore::get_all_drawdowns(const dataframe &returns, std::string_view column = returns_column)
Returns the depth of every distinct underwater episode in chronological order.
Values are negative fractions; an episode that has not recovered is included with its current depth.
-
std::vector<drawdown_period> fincore::get_all_drawdowns_detailed(const dataframe &returns, std::string_view column = returns_column)
Returns every distinct underwater episode with its start, valley, recovery and durations, in chronological order.
-
std::vector<drawdown_window> fincore::get_top_drawdowns(const dataframe &returns, std::size_t top = 10, std::string_view column = returns_column)
Returns up to
topdeepest non-overlapping drawdown windows, ordered by depth.Ties keep the earlier window. Returns an empty vector for empty input or
top == 0.
-
gpd_risk_estimates_result fincore::gpd_risk_estimates(const dataframe &returns, double var_p = 0.01, std::string_view column = returns_column)
Returns GPD tail estimates of losses (negated negative returns), fitted by maximum likelihood with a threshold search starting at 0.2 and halving.
var_pis the tail probability. All fields are zero when there are fewer than three returns, no positive-shape fit is found, or the search does not converge.
-
double fincore::henriksson_merton_timing(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the Henriksson-Merton market-timing coefficient.
Fits
excess_return = a + b * excess_market + c * down, wheredownis one when the excess market return is negative and zero otherwise, and returnsc. Only finite pairs are used andrisk_freeis subtracted from both series. Returns NaN with fewer than ten usable pairs, a non-finiterisk_free, a singular design, or a non-finite coefficient.
-
double fincore::historical_value_at_risk(const dataframe &returns, double cutoff = 0.05, std::string_view column = returns_column)
Returns the empirical
cutoffquantile ofcolumnusing linear interpolation.No annualization is applied. Returns NaN unless every value is finite and
cutofflies in [0, 1].
-
double fincore::hurst_exponent(const dataframe &returns, std::string_view column = returns_column)
Returns the rescaled-range Hurst exponent of
column, ignoring NaN values.The estimate is clamped to [0, 1]. Returns NaN with fewer than eight usable values or a degenerate range/deviation.
-
double fincore::information_ratio(const dataframe &returns, const dataframe &benchmark, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the annualized information ratio of
returnsagainstbenchmark.Only finite common pairs are used. The annualized mean active return is divided by the active sample deviation scaled by
sqrt(annualization). Returns NaN when fewer than two pairs remain,annualizationis not finite or not positive, or the annualized tracking error is zero or non-finite.
-
dataframe fincore::information_ratio_by_year(const dataframe &returns, const dataframe &benchmark, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the information ratio per UTC calendar year in column “information_ratio”.
The two frames are aligned on their common timestamps before bucketing, and each year uses information_ratio with
annualization_factor(period, annualization); non-finite pairs are dropped within their own year. Years are indexed by January 1 and sorted ascending; an empty aligned pair yields an empty typed frame. An unknownfrequencywithout an explicitannualizationraisesinput_error.
-
double fincore::kappa_three_ratio(const dataframe &returns, double mar = 0.0, std::string_view column = returns_column)
Returns the Kappa 3 ratio: the mean excess over
mardivided by the cube root of the third lower partial moment.The lower partial moment averages
max(0, mar - value)^3over the non-NaN values. Returns NaN for fewer than two usable values, a NaNmar, or a moment below 1e-30 when the mean does not exceedmar; the same degenerate case returns positive infinity when it does. An embedded NUL incolumnraisesinput_error.
-
double fincore::kurtosis(const dataframe &returns, std::string_view column = returns_column)
Returns the biased excess kurtosis (fourth moment over squared variance, minus 3) of
column, ignoring NaN values.Returns NaN with fewer than four usable values or zero variance.
-
double fincore::loss_rate(const dataframe &returns, std::string_view column = returns_column)
Returns the fraction of usable observations in
columnthat are strictly negative.NaN values are ignored; an empty input yields NaN.
-
double fincore::m_squared(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the M-squared measure of
returnsagainstbenchmark.Both series are aligned on their common timestamps. The excess annual return is rescaled by benchmark volatility over portfolio volatility and
risk_freeis added back. Returns NaN when fewer than two aligned returns remain, portfolio volatility is zero, negative or NaN, or benchmark volatility is NaN. An embedded NUL in either column name raisesinput_error.
-
double fincore::mar_ratio(const dataframe &returns, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the annualized arithmetic mean return divided by the absolute maximum drawdown.
The mean uses the non-NaN values and is scaled by
annualizationrather than compounded. Returns NaN for fewer than two raw values, a maximum drawdown that is non-negative or NaN, or a non-finite quotient. An embedded NUL incolumnraisesinput_error.
-
double fincore::market_timing_return(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the Treynor-Mazuy coefficient times the mean squared excess market return.
Uses the same quadratic fit, finite-pair filtering and minimum of ten observations as treynor_mazuy_timing. Returns NaN under the same conditions or when the resulting product is not finite.
-
double fincore::max_consecutive_down_days(const dataframe &returns, std::string_view column = returns_column)
Returns the longest run of strictly negative observations in
column.Returns 0 when there is none and NaN for empty input.
-
std::optional<timestamp> fincore::max_consecutive_down_end_date(const dataframe &returns, std::string_view column = returns_column)
Returns the last timestamp of the longest negative run.
Ties keep the earliest run; returns nullopt when no negative run exists.
-
double fincore::max_consecutive_down_months(const dataframe &returns, std::string_view column = returns_column)
Returns the longest run of consecutive calendar months whose compounded return is negative.
Months without observations are skipped. Returns 0 when there is none and NaN for empty input.
-
std::optional<timestamp> fincore::max_consecutive_down_start_date(const dataframe &returns, std::string_view column = returns_column)
Returns the first timestamp of the longest negative run.
Ties keep the earliest run; returns nullopt when no negative run exists.
-
double fincore::max_consecutive_down_weeks(const dataframe &returns, std::string_view column = returns_column)
Returns the longest run of consecutive calendar weeks (ending Sunday) whose compounded return is negative.
Weeks without observations are skipped. Returns 0 when there is none and NaN for empty input.
-
double fincore::max_consecutive_gain(const dataframe &returns, std::string_view column = returns_column)
Returns the largest sum of returns over a consecutive positive run.
Returns NaN when no positive run exists.
-
double fincore::max_consecutive_loss(const dataframe &returns, std::string_view column = returns_column)
Returns the most negative sum of returns over a consecutive negative run.
Returns NaN when no negative run exists.
-
double fincore::max_consecutive_up_days(const dataframe &returns, std::string_view column = returns_column)
Returns the longest run of strictly positive observations in
column.Returns 0 when there is none and NaN for empty input.
-
std::optional<timestamp> fincore::max_consecutive_up_end_date(const dataframe &returns, std::string_view column = returns_column)
Returns the last timestamp of the longest positive run.
Ties keep the earliest run; returns nullopt when no positive run exists.
-
double fincore::max_consecutive_up_months(const dataframe &returns, std::string_view column = returns_column)
Returns the longest run of consecutive calendar months whose compounded return is positive.
Months without observations are skipped. Returns 0 when there is none and NaN for empty input.
-
std::optional<timestamp> fincore::max_consecutive_up_start_date(const dataframe &returns, std::string_view column = returns_column)
Returns the first timestamp of the longest positive run.
Ties keep the earliest run; returns nullopt when no positive run exists.
-
double fincore::max_consecutive_up_weeks(const dataframe &returns, std::string_view column = returns_column)
Returns the longest run of consecutive calendar weeks (ending Sunday) whose compounded return is positive.
Weeks without observations are skipped. Returns 0 when there is none and NaN for empty input.
-
double fincore::max_drawdown(const dataframe &returns, std::string_view column = returns_column)
Returns the deepest drawdown of
columnas a negative fraction.Returns 0 when the series never declines and NaN for an empty frame.
-
dataframe fincore::max_drawdown_by_year(const dataframe &returns, std::string_view column = returns_column)
Returns the maximum drawdown per UTC calendar year in column “max_drawdown”.
Each year uses max_drawdown, so a year that never declines reports 0. Years are indexed by January 1 and sorted ascending. An empty frame yields an empty typed frame.
-
double fincore::max_drawdown_days(const dataframe &returns, std::string_view column = returns_column)
Returns the elapsed UTC days from the peak to the valley of the deepest drawdown.
Returns 0 when the series never declines and NaN for an empty frame. The value is fractional when timestamps are not midnight-aligned.
-
double fincore::max_drawdown_months(const dataframe &returns, std::string_view column = returns_column)
Returns max_drawdown_days divided by 30 (approximate months).
-
std::pair<std::optional<timestamp>, std::optional<timestamp>> fincore::max_drawdown_period(const dataframe &returns, std::string_view column = returns_column)
Returns the {peak, valley} timestamps of the maximum drawdown.
Returns {first timestamp, first timestamp} when the series never declines and {nullopt, nullopt} for an empty frame.
-
double fincore::max_drawdown_recovery_days(const dataframe &returns, std::string_view column = returns_column)
Returns the elapsed UTC days from the valley to the recovery of the deepest drawdown.
Returns NaN when the episode has not recovered or the frame is empty, and 0 when the series never declines.
-
double fincore::max_drawdown_recovery_months(const dataframe &returns, std::string_view column = returns_column)
Returns max_drawdown_recovery_days divided by 30 (approximate months).
-
double fincore::max_drawdown_recovery_weeks(const dataframe &returns, std::string_view column = returns_column)
Returns max_drawdown_recovery_days divided by 7 (fractional weeks).
-
std::optional<drawdown_window> fincore::max_drawdown_underwater(const dataframe &underwater, std::string_view column = "drawdown")
Returns the peak/valley/recovery window from a precomputed underwater series.
The source helper returns (NaT, NaT, NaT) when the series never goes below zero; the native form returns nullopt for that case and for empty/non-finite data.
-
double fincore::max_drawdown_weeks(const dataframe &returns, std::string_view column = returns_column)
Returns max_drawdown_days divided by 7 (fractional weeks).
-
std::optional<drawdown_window> fincore::max_drawdown_window(const dataframe &returns, std::string_view column = returns_column)
Returns the peak, valley and optional recovery of the deepest episode.
Returns nullopt when the series never declines or the input is empty.
-
double fincore::max_single_day_gain(const dataframe &returns, std::string_view column = returns_column)
Returns the largest non-NaN observation in
column; NaN for empty input.
-
std::optional<timestamp> fincore::max_single_day_gain_date(const dataframe &returns, std::string_view column = returns_column)
Returns the timestamp of the largest non-NaN observation in
column.Ties keep the earliest occurrence; returns nullopt when no value exists.
-
double fincore::max_single_day_loss(const dataframe &returns, std::string_view column = returns_column)
Returns the smallest non-NaN observation in
column; NaN for empty input.
-
std::optional<timestamp> fincore::max_single_day_loss_date(const dataframe &returns, std::string_view column = returns_column)
Returns the timestamp of the smallest non-NaN observation in
column.Ties keep the earliest occurrence; returns nullopt when no value exists.
-
dataframe fincore::normalize(const dataframe &values, double starting_value = 1.0, std::string_view column = returns_column)
Returns
valuesrescaled so the first value ofcolumnequalsstarting_value.A zero first value makes every output NaN; other NaN values propagate. The result keeps the input timestamps and column name.
-
dataframe fincore::normalize_attribution_positions(const dataframe &positions, bool positions_in_dollars = true, std::string_view cash_column_name = cash_column)
Returns positions converted to per-timestamp weights when requested.
Every double column except
cash_column_nameis kept; non-double columns are ignored. Withpositions_in_dollars, each value is divided by that row’s gross (the sum of absolute non-NaN positions), so NaN positions stay NaN and an all-zero row yields NaN weights. Otherwise values are copied. The input index and order are preserved. Duplicate timestamps, a non-double cash column, an empty asset name, or no asset columns raiseinput_error.
-
double fincore::omega_ratio(const dataframe &returns, double risk_free = 0.0, double required_return = 0.0, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the Omega ratio of
columnabove the required threshold.Non-finite values are dropped. The per-period threshold is
risk_freeplusrequired_returnconverted to a per-period rate whenannualizationis not one. Gains above and losses below the threshold are summed and the ratio is gains over losses. Returns NaN for fewer than two usable values, a non-finite parameter, a non-positiveannualization, a required return at or below -1 with an annualization other than one, or a non-positive or non-finite gain/loss sum.
-
frequency fincore::pandas_frequency(std::string_view period)
Maps one canonical source frequency string to the native
frequencyenum.Source contract (
fincore.metrics.frequencies.pandas_frequency): only the five canonical names are accepted; any other value raises ValueError with the “Period cannot be …: unknown frequency” message. The native function raisesinput_errorwith the same predicate and message text.
-
std::string_view fincore::pandas_frequency_alias(frequency period)
Returns the pandas resampling alias for
period(D, W, ME, QE, YE).Alias for the pinned pandas (>= 2.2) environment, exposed as provenance for the source mapping; the native code consumes the enum, not the alias string.
-
perf_attribution_result fincore::perf_attrib(const dataframe &returns, const dataframe &positions, const dataframe &factor_returns, const dataframe &factor_loadings, bool positions_in_dollars = true, std::string_view returns_column_name = returns_column, std::string_view cash_column_name = cash_column, std::string_view symbol_column_name = attribution_symbol_column)
Returns risk exposures and attribution from raw positions.
With
positions_in_dollars, positions are first passed through normalize_attribution_positions; otherwise they are treated as weights and forwarded unchanged. The computation and errors are those of normalize_attribution_positions when used, followed by perf_attrib_core.
-
perf_attribution_result fincore::perf_attrib_core(const dataframe &returns, const dataframe &normalized_positions, const dataframe &factor_returns, const dataframe &factor_loadings, std::string_view returns_column_name = returns_column, std::string_view cash_column_name = cash_column, std::string_view symbol_column_name = attribution_symbol_column)
Returns risk exposures and return attribution for normalized positions.
returns,normalized_positionsandfactor_returnsmust have identical, duplicate-free indexes. Factors are all double columns offactor_returns, and at least one is required; their names must differ from the symbol column and the reserved attribution names. The attribution frame holds one contribution column per factor in factor-return column order, followed by “total_returns” (the input returns), “common_returns” (sum of finite contributions), “specific_returns” (total - common), “tilt_returns” (mean exposure per factor times that date’s factor return) and “timing_returns” (common - tilt). Both returned frames own their data.Mismatched or duplicate indexes, missing or non-string symbol columns, missing or non-double factor columns, reserved factor names, duplicate factor-loading keys, or factor-loading keys that do not cover the positions raise
input_error.
-
std::map<std::string, double> fincore::perf_stats(const dataframe &returns, frequency period = frequency::daily, std::string_view returns_column_name = returns_column)
Returns the standard performance statistics of
returnskeyed by display name.The map always holds the thirteen keys “Annual return”, “Cumulative
returns”, “Annual volatility”, “Sharpe ratio”, “Calmar ratio”, “Stability”, “Max drawdown”, “Omega ratio”, “Sortino ratio”, “Skew”, “Kurtosis”, “Tail
ratio” and “Daily value at risk”. Period-sensitive metrics use
annualization_factor(period), while the Omega ratio is always daily annualized. The column must be a double column matching the frame index, orinput_erroris thrown.
-
std::map<std::string, double> fincore::perf_stats(const dataframe &returns, const dataframe &benchmark, frequency period = frequency::daily, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the unary statistics plus “Alpha” and “Beta”.
The thirteen unary keys are computed as in the unary overload. Alpha and beta come from alpha_beta on the common timestamps with a zero risk-free rate and
daily_annualizationregardless ofperiod. Both selectors must be double columns matching their frame indexes, orinput_erroris thrown.
-
std::vector<bootstrap_metric_stats> fincore::perf_stats_bootstrap(const dataframe &returns, std::size_t n_samples = 1000, std::uint64_t seed = 0, std::string_view column = returns_column)
Returns bootstrap distributions for the thirteen perf_stats metrics.
Each of the
n_samplesdraws resamplesreturns.size()observations with replacement fromcolumnusingstd::mt19937_64seeded withseed; NaN values are drawn verbatim. Period-sensitive metrics usedaily_annualization. The result always has thirteen entries in the fixed bootstrap metric order; an empty input yields empty samples and NaN summaries. An embedded NUL, empty or invalidcolumnraisesinput_error.
-
std::vector<bootstrap_metric_stats> fincore::perf_stats_bootstrap(const dataframe &returns, std::span<const std::vector<std::size_t>> draw_indices, std::string_view column = returns_column)
Returns bootstrap distributions from caller-supplied draw indices.
Each draw contains positional row indices into the original input column. Rows must have the same length as the input population. Empty input accepts only an empty draw matrix and preserves the seeded overload’s empty result. An out-of-range index or an embedded NUL in
columnraisesinput_error.
-
double fincore::r_cubed(const dataframe &returns, const dataframe &benchmark, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the cube of safe_correlation.
Returns NaN when the correlation itself is NaN. Embedded NUL characters in the column names raise
input_error.
-
double fincore::r_cubed_turtle(const dataframe &returns, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the RAR/Turtle ratio: the annualized slope of NAV against time divided by the mean annual maximum drawdown magnitude.
Returns infinity when that mean drawdown is effectively zero and the annualized slope is positive; NaN when it cannot be computed.
-
double fincore::relative_win_rate(const dataframe &returns, const dataframe &benchmark, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the fraction of common finite dates where the strategy return is strictly greater than the benchmark return.
-
double fincore::residual_risk(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the annualized sample standard deviation of the residuals from a CAPM regression of excess strategy returns on excess benchmark returns.
Returns NaN when
risk_freeor beta is not finite.
-
dataframe fincore::roll_alpha(const dataframe &returns, const dataframe &benchmark, std::size_t window = 252U, double risk_free = 0.0, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the rolling annualized Jensen alpha at each window end.
Windows are built from the common timestamps of both frames and delegated to alpha with
risk_freeandannualization_factor(period, annualization). Every complete window produces one row indexed by the aligned window end; fewer thanwindowaligned observations, including no common timestamps, produce an empty typed frame. A zero window, an unknownfrequencywithout an explicitannualization, or an invalid column raisesinput_error.
-
dataframe fincore::roll_alpha_beta(const dataframe &returns, const dataframe &benchmark, std::size_t window = 252U, double risk_free = 0.0, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the rolling alpha and beta in the columns “alpha” and “beta”.
Windows are built from the common timestamps and delegated to alpha_beta with
risk_freeandannualization_factor(period, annualization); windows that alpha_beta cannot fit keep NaN values in both columns. Every complete window produces one row indexed by the aligned window end; fewer thanwindowaligned observations produce an empty two-column frame. A zero window, or an unknownfrequencywithout an explicitannualization, raisesinput_error.
-
dataframe fincore::roll_annual_volatility(const dataframe &returns, std::size_t window, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view column = returns_column)
Returns the annualized rolling volatility at each window end.
Unlike rolling_volatility, rows exist only for complete windows and NaN observations are dropped by annual_volatility inside each window, so a poisoned window is still evaluated when at least two finite observations remain. Fewer than
windowobservations produce an empty typed frame. A zero window, or an unknownfrequencywithout an explicitannualization, raisesinput_error.
-
dataframe fincore::roll_beta(const dataframe &returns, const dataframe &benchmark, std::size_t window = 252U, double risk_free = 0.0, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the rolling OLS beta at each window end.
Beta is not annualized, so
periodandannualizationare ignored. Windows are built from the common timestamps and delegated to beta withrisk_free. Every complete window produces one row indexed by the aligned window end; fewer thanwindowaligned observations produce an empty typed frame. A zero window or an invalid column raisesinput_error.
-
dataframe fincore::roll_down_capture(const dataframe &returns, const dataframe &benchmark, std::size_t window = 252U, frequency period = frequency::daily, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the rolling down-capture ratio at each window end.
Every complete window of the common timestamps is delegated to down_capture with the
annualization_factor(period)factor, soperiodis the only annualization control. Fewer thanwindowaligned observations produce an empty typed frame. A zero window or an unknownfrequencyraisesinput_error.
-
dataframe fincore::roll_max_drawdown(const dataframe &returns, std::size_t window = 252U, std::string_view column = returns_column)
Returns the maximum drawdown at each window end.
Wealth restarts at 1 for every window and NaN observations never update the running peak or the result, so a poisoned window reports 0.0 when no finite drawdown is seen. Every complete window produces one row indexed by its last timestamp; fewer than
windowobservations produce an empty typed frame. A zero window raisesinput_error.
-
dataframe fincore::roll_sharpe_ratio(const dataframe &returns, std::size_t window = 252U, double risk_free = 0.0, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view column = returns_column)
Returns the rolling Sharpe ratio at each window end.
Every complete window of
windowobservations produces one row, indexed by its last timestamp; fewer thanwindowobservations produce an empty typed frame. The factor isannualization_factor(period, annualization)and NaN values are dropped by sharpe_ratio. A zero window, or an unknownfrequencywithout an explicitannualization, raisesinput_error.
-
dataframe fincore::roll_sortino_ratio(const dataframe &returns, std::size_t window, double required_return = 0.0, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view column = returns_column)
Returns the rolling annualized Sortino ratio at each window end.
Every complete window is delegated to sortino_ratio with
required_returnandannualization_factor(period, annualization); NaN values are dropped by the scalar metric. Fewer thanwindowobservations produce an empty typed frame. A zero window, or an unknownfrequencywithout an explicitannualization, raisesinput_error.
-
dataframe fincore::roll_up_capture(const dataframe &returns, const dataframe &benchmark, std::size_t window = 252U, frequency period = frequency::daily, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the rolling up-capture ratio at each window end.
Every complete window of the common timestamps is delegated to up_capture with the
annualization_factor(period)factor, soperiodis the only annualization control. Fewer thanwindowaligned observations produce an empty typed frame. A zero window or an unknownfrequencyraisesinput_error.
-
dataframe fincore::roll_up_down_capture(const dataframe &returns, const dataframe &benchmark, std::size_t window = 252U, frequency period = frequency::daily, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the rolling up-down capture ratio at each window end.
Every complete window of the common timestamps is delegated to up_down_capture with the
annualization_factor(period)factor, soperiodis the only annualization control. Fewer thanwindowaligned observations produce an empty typed frame. A zero window or an unknownfrequencyraisesinput_error.
-
dataframe fincore::rolling_beta(const dataframe &returns, const dataframe &factor_returns, std::size_t rolling_window = 126U, std::string_view returns_column_name = returns_column, std::string_view factor_column_name = benchmark_column)
Returns the rolling beta of
returnsagainst one factor column.The result keeps the aligned common index; rows before the first complete window are NaN, as is any window with a non-finite value or zero factor variance. Beta is the covariance over factor variance, with no risk-free adjustment. A zero
rolling_windowor an invalid column raisesinput_error.
-
dataframe fincore::rolling_beta(const dataframe &returns, const dataframe &factor_returns, std::span<const std::string_view> factor_column_names, std::size_t rolling_window = 126U, std::string_view returns_column_name = returns_column)
Returns one rolling beta column per factor, named after the factor columns.
Each factor is aligned against
returnsseparately using the same warm-up and invalid-window rules as the single-factor overload. Factor names must be non-empty, unique and present as double columns; otherwiseinput_erroris raised, as it is for a zerorolling_window.
-
dataframe fincore::rolling_regression(const dataframe &returns, const dataframe &benchmark, std::size_t rolling_window = 126U, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the rolling OLS alpha and beta in the columns “alpha” and “beta”.
Windows are built from the common timestamps; beta is the covariance over factor variance and alpha is the mean return minus beta times the mean benchmark return, with no risk-free adjustment. Rows are indexed by the aligned window end, and windows with a non-finite value, zero factor variance or non-finite coefficients are omitted, so the index can be sparse. A zero
rolling_windowor an invalid column raisesinput_error.
-
dataframe fincore::rolling_sharpe(const dataframe &returns, std::size_t window, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view column = returns_column)
Returns the rolling Sharpe ratio aligned to the input index.
The index and warm-up shape match rolling_volatility, the risk-free rate is zero, and a window containing NaN yields NaN. The factor is
annualization_factor(period, annualization). A zero window, or an unknownfrequencywithout an explicitannualization, raisesinput_error.
-
dataframe fincore::rolling_volatility(const dataframe &returns, std::size_t window, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view column = returns_column)
Returns the annualized rolling volatility aligned to the input index.
Rows before the first complete window are NaN, and a window containing NaN yields NaN. Each window uses annual_volatility with
annualization_factor(period, annualization). Fewer thanwindowobservations produce all-NaN values. A zero window, or an unknownfrequencywithout an explicitannualization, raisesinput_error.
-
double fincore::safe_correlation(const dataframe &returns, const dataframe &benchmark, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the Pearson correlation of the two series on their common finite timestamps.
Returns NaN with fewer than two pairs or a near-zero standard deviation in either series.
-
double fincore::second_max_drawdown(const dataframe &returns, std::string_view column = returns_column)
Returns the second-deepest drawdown as a negative fraction.
Returns NaN when fewer than two episodes exist.
-
double fincore::second_max_drawdown_days(const dataframe &returns, std::string_view column = returns_column)
Returns the elapsed UTC days from the first underwater observation to the valley of the second-deepest drawdown.
Returns NaN when fewer than two episodes exist.
-
double fincore::second_max_drawdown_recovery_days(const dataframe &returns, std::string_view column = returns_column)
Returns the elapsed UTC days from the valley to the recovery of the second-deepest drawdown.
Returns NaN when that episode has not recovered or fewer than two episodes exist.
-
double fincore::serial_correlation(const dataframe &returns, std::ptrdiff_t lag = 1, std::string_view column = returns_column)
Returns the Pearson autocorrelation of
columnatlagobservations.NaN values are removed before lagging. Returns NaN when
lagis less than one or fewer thanlag + 2values remain.
-
double fincore::sharpe_ratio(const dataframe &returns, double risk_free = 0.0, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the annualized Sharpe ratio of
column.NaN observations are dropped; the mean of
value - risk_freeis divided by the sample standard deviation (one degree of freedom) and scaled bysqrt(annualization). Fewer than two usable observations yield NaN, and a zero deviation is not special-cased, so the quotient follows IEEE rules.
-
dataframe fincore::sharpe_ratio_by_year(const dataframe &returns, double risk_free = 0.0, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view column = returns_column)
Returns the Sharpe ratio per UTC calendar year in column “sharpe_ratio”.
Each year uses sharpe_ratio with
risk_freeandannualization_factor(period, annualization). Years are indexed by January 1 and sorted ascending. An empty frame yields an empty typed frame, and an unknownfrequencywithout an explicitannualizationraisesinput_error.
-
dataframe fincore::simple_returns(const dataframe &prices, std::string_view price_column)
Returns the period-over-period simple returns of
price_column, dropping the first observation.The result is stored under
returns_columnand keeps the timestamp of the later price of each pair. Fewer than two prices yield an empty frame.
-
matrix fincore::simulate_paths(std::span<const double> in_sample_returns, std::size_t num_days, double starting_value = 1.0, std::size_t num_samples = 1000U, std::uint32_t random_seed = 0U)
Draws
num_samplesxnum_daysbootstrap returns from in-sample returns.RNG: NumPy legacy RandomState-compatible MT19937 with the explicit 32-bit
random_seed(default 0; the source defaultNoneis not reproduced). Integer draws use NumPy’s masked rejection (rk_interval), so seeded sequences matchnp.random.RandomState(seed).randint(0, n, size=...)exactly for 32-bit seeds. This is not a claim about array seeding or the source’s non-deterministicNonedefault.starting_valueis accepted for source-signature parity and ignored, as in the source (_starting_value). NaN in-sample values are drawn verbatim; infinite values raiseinput_error. An empty population with a non-empty draw shape raisesinput_error(sourcerng.randint(0, 0, ...)raises ValueError), as do an unrepresentable shape and a population larger than a 32-bit draw index.
-
matrix fincore::simulate_paths(const dataframe &in_sample_returns, std::size_t num_days, double starting_value = 1.0, std::size_t num_samples = 1000U, std::uint32_t random_seed = 0U, std::string_view column = returns_column)
Returns the same paths as the span overload, selecting one double column.
A source DataFrame with several columns would make NumPy indexing return a 3-D array; that shape is not representable, so callers select the column to bootstrap.
-
double fincore::skewness(const dataframe &returns, std::string_view column = returns_column)
Returns the biased (population) skewness of
column, ignoring NaN values.Returns NaN with fewer than three usable values or zero variance.
-
double fincore::sortino_ratio(const dataframe &returns, double required_return = 0.0, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the annualized Sortino ratio of
column.NaN observations are dropped; the annualized mean excess over
required_returnis divided by the annualized downside deviation, where only negative excess values contribute and the squared deviations divide by the number of usable observations. Fewer than two raw values, or no usable observation, yield NaN, and a zero downside deviation is not special-cased.
-
double fincore::stability_of_timeseries(const dataframe &returns, std::string_view column = returns_column)
Returns the R-squared of cumulative log returns regressed on a linear time index.
Non-finite values are dropped and
log1pis applied to each; the cumulative sum is regressed on the observation position. Returns NaN for fewer than two usable values, a non-finite log return (for example a return at or below -1), an overflowing cumulative sum, or zero explained variance.
-
double fincore::sterling_ratio(const dataframe &returns, double risk_free = 0.0, double annualization = daily_annualization, std::string_view column = returns_column)
Returns the annual return in excess of
risk_freedivided by the average drawdown magnitude.The average drawdown is the absolute mean of all drawdown depths; with no drawdowns or only zeros it falls back to the absolute mean of the negative returns (or the absolute population deviation of all usable values when none are negative). The annual return is compounded at
annualization. Returns positive infinity when the denominator is below 1e-10 and the excess annual return is positive, NaN otherwise, and NaN for fewer than two raw values or no usable value. An embedded NUL incolumnraisesinput_error.
-
double fincore::stutzer_index(const dataframe &returns, double target_return = 0.0, std::string_view column = returns_column)
Returns the Stutzer index of
columnrelative totarget_return.NaN values are ignored; fewer than two usable values yield NaN. Returns 0 when the mean excess return is effectively zero.
-
dataframe fincore::summarize_paths(const matrix &samples, std::span<const double> cone_std = default_cone_standard_deviations, double starting_value = 1.0)
Returns the mean/standard-deviation cone from sampled paths.
samplesis row-major with one row per path and one column per day. Source formula: cumulative wealth per path from cumprod(1 + returns) with NaN returns as 0; boundsmean + std * num_stdandmean - std * num_stdfor each requested width, with sample standard deviation (ddof = 1).starting_value == 0yieldscumprod - 1; otherwise the wealth is scaled bystarting_value, matchingcum_returns. Columns are ordered upper then lower for each width, namedcone_<shortest-round-trip-width>(for examplecone_1,cone_-1,cone_1.5); Python float labels are represented as native strings, not claimed identical. Duplicate widths raiseinput_error.An empty
cone_stdspan returns an empty frame; a non-finite width, a non-finitestarting_valueor an infinite sampled return raisesinput_error.
-
dataframe fincore::summarize_paths(const matrix &samples, double cone_std, double starting_value = 1.0)
Returns the cone for a single standard-deviation width.
Equivalent to the span overload with a one-element width list.
-
double fincore::tail_ratio(const dataframe &returns, std::string_view column = returns_column)
Returns the ratio of the absolute 95th percentile to the absolute 5th percentile of the finite values in
column.Returns NaN for empty input or a zero lower tail.
-
double fincore::third_max_drawdown(const dataframe &returns, std::string_view column = returns_column)
Returns the third-deepest drawdown as a negative fraction.
Returns NaN when fewer than three episodes exist.
-
double fincore::third_max_drawdown_days(const dataframe &returns, std::string_view column = returns_column)
Returns the elapsed UTC days from the first underwater observation to the valley of the third-deepest drawdown.
Returns NaN when fewer than three episodes exist.
-
double fincore::third_max_drawdown_recovery_days(const dataframe &returns, std::string_view column = returns_column)
Returns the elapsed UTC days from the valley to the recovery of the third-deepest drawdown.
Returns NaN when that episode has not recovered or fewer than three episodes exist.
-
dataframe fincore::to_dataframe(std::span<const double> values, std::string_view column = returns_column)
Returns a frame representation of the input, replacing the source
to_pandasroute.Native replacement for the source
to_pandasroute (route kept under the namefincore.metrics.basic.to_pandasin the ledger, implementation namedto_dataframe). pandas/NumPy identity is not claimed.1-D input becomes a single-column frame with a positional 0..n-1 index (the native analogue of a RangeIndex); 2-D input becomes one double column per matrix column named “<prefix>_0”, “<prefix>_1”, … with the same positional index. An existing frame is returned as an owning copy. Inputs with more than two dimensions cannot be represented by
matrix, so the source ValueError branch has no native equivalent.
-
dataframe fincore::to_dataframe(const matrix &values, std::string_view column_prefix = "column")
Returns a frame with one double column per matrix column, named “<column_prefix>_0”, “<column_prefix>_1”, … and a positional index.
-
double fincore::tracking_difference(const dataframe &returns, const dataframe &benchmark, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the cumulative strategy return minus the cumulative benchmark return over their common finite timestamps.
Returns NaN when no common finite pair exists.
-
double fincore::tracking_error(const dataframe &returns, const dataframe &benchmark, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the annualized sample standard deviation of active returns (strategy minus benchmark) on common finite timestamps.
Returns NaN with fewer than two pairs, non-finite/negative
annualization, or a non-finite variance.
-
double fincore::trading_value_at_risk(const dataframe &returns, double sigma = 2.0, std::string_view column = returns_column)
Returns
mean - sigma * sample_standard_deviationofcolumn.Requires at least two values; NaN observations propagate to the result.
-
double fincore::treynor_mazuy_timing(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the Treynor-Mazuy market-timing coefficient.
Fits
excess_return = a + b * excess_market + c * excess_market^2by ordinary least squares on the common timestamps and returnsc. Only finite pairs are used andrisk_freeis subtracted from both series. Returns NaN with fewer than ten usable pairs, a non-finiterisk_free, a singular design, or a non-finite coefficient.
-
std::pair<double, double> fincore::up_alpha_beta(const dataframe &returns, const dataframe &benchmark, double risk_free = 0.0, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns {alpha, beta} using only dates with strictly positive benchmark returns.
Otherwise follows alpha_beta.
-
double fincore::up_capture(const dataframe &returns, const dataframe &benchmark, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the capture ratio on dates where the benchmark return is strictly positive.
Follows capture on the selected common finite pairs. Returns NaN when no date qualifies or the ratio is not finite.
-
double fincore::up_capture_return(const dataframe &returns, const dataframe &benchmark, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns the annualized geometric return of
returnson dates where the benchmark return is strictly positive.Only common timestamps are considered. Selected NaN returns are treated as zero when compounding. Returns NaN when no date qualifies and -1 when the compounded wealth is non-positive. An embedded NUL in either column name raises
input_error.
-
double fincore::up_down_capture(const dataframe &returns, const dataframe &benchmark, double annualization = daily_annualization, std::string_view returns_column_name = returns_column, std::string_view benchmark_column_name = benchmark_column)
Returns up_capture divided by down_capture.
Both components use the common finite pairs. Returns NaN when either component is non-finite or the down capture is zero.
-
double fincore::var_cov_var_normal(double portfolio_value, double confidence, double mean = 0.0, double standard_deviation = 1.0)
Returns the variance-covariance value at risk under a normal assumption:
portfolio_value * (z(confidence) * standard_deviation - mean).No annualization is applied. Returns NaN when
confidenceis outside [0, 1] orstandard_deviationis not positive.
-
double fincore::var_excess_return(const dataframe &returns, double cutoff = 0.05, double risk_free = 0.0, frequency period = frequency::daily, std::optional<double> annualization = std::nullopt, std::string_view column = returns_column)
Returns the annualized return in excess of
risk_free, divided by the absolute historical value at risk atcutoff.The return is annualized with
. Returns NaN with fewer than two values, a zero or NaN value at risk, or a NaN annual return.annualization_factor(period,
annualization)
-
double fincore::win_rate(const dataframe &returns, std::string_view column = returns_column)
Returns the fraction of usable observations in
columnthat are strictly positive.NaN values are ignored; an empty input yields NaN.
-
constexpr std::string_view fincore::attribution_common_returns_column{"common_returns"}
Attribution column holding the sum of finite factor contributions.
-
constexpr std::string_view fincore::attribution_factor_column = {"factor"}
Column holding factor names in the factor summary frame.
-
constexpr std::string_view fincore::attribution_specific_returns_column{"specific_returns"}
Attribution column holding the residual return (
total - common).
-
constexpr std::string_view fincore::attribution_symbol_column = {"symbol"}
Default factor-loading column holding asset symbols.
-
constexpr std::string_view fincore::attribution_tilt_returns_column{"tilt_returns"}
Attribution column holding the sum of mean-exposure tilt contributions.
-
constexpr std::string_view fincore::attribution_timing_returns_column{"timing_returns"}
Attribution column holding the timing return (
common - tilt).
-
constexpr std::string_view fincore::attribution_total_returns_column{"total_returns"}
Attribution column holding the total (input) returns.
-
constexpr std::string_view fincore::bayesian_cone_percentile_1_column = {"percentile_1"}
Result column holding the 1st percentile of cumulative path wealth.
-
constexpr std::string_view fincore::bayesian_cone_percentile_25_column = {"percentile_25"}
Result column holding the 25th percentile of cumulative path wealth.
-
constexpr std::string_view fincore::bayesian_cone_percentile_50_column = {"percentile_50"}
Result column holding the 50th percentile of cumulative path wealth.
-
constexpr std::string_view fincore::bayesian_cone_percentile_5_column = {"percentile_5"}
Result column holding the 5th percentile of cumulative path wealth.
-
constexpr std::string_view fincore::bayesian_cone_percentile_75_column = {"percentile_75"}
Result column holding the 75th percentile of cumulative path wealth.
-
constexpr std::string_view fincore::bayesian_cone_percentile_95_column = {"percentile_95"}
Result column holding the 95th percentile of cumulative path wealth.
-
constexpr std::string_view fincore::bayesian_cone_percentile_99_column = {"percentile_99"}
Result column holding the 99th percentile of cumulative path wealth.
-
constexpr std::string_view fincore::benchmark_column = {"benchmark"}
Default name of the column holding benchmark returns.
-
constexpr double fincore::daily_annualization = 252.0
Default annualization factor for daily observations (252 periods per year).
-
constexpr std::string_view fincore::daily_frequency_name = {"daily"}
Canonical source name of the daily frequency (“daily”).
-
constexpr std::array<double, 3U> fincore::default_cone_standard_deviations{1.0, 1.5, 2.0}
Default cone widths (1.0, 1.5 and 2.0 standard deviations).
-
constexpr std::string_view fincore::monthly_frequency_name = {"monthly"}
Canonical source name of the monthly frequency (“monthly”).
-
constexpr std::string_view fincore::quarterly_frequency_name = {"quarterly"}
Canonical source name of the quarterly frequency (“quarterly”).
-
constexpr std::string_view fincore::return_aggregation_period_column = {"period"}
Name of the integer period-within-year column added by aggregate_returns.
-
constexpr std::string_view fincore::return_aggregation_year_column = {"year"}
Name of the integer calendar-year column added by aggregate_returns.
-
constexpr std::string_view fincore::returns_column = {"returns"}
Default name of the column holding periodic simple returns.
-
constexpr std::string_view fincore::weekly_frequency_name = {"weekly"}
Canonical source name of the weekly frequency (“weekly”).
-
constexpr std::string_view fincore::yearly_frequency_name = {"yearly"}
Canonical source name of the yearly frequency (“yearly”).