Performance

Cashflow-adjusted returns, disclosure contexts, returns inference and performance 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.

struct disclosure_context

Display metadata rendered by render_disclosure().

Fields are free-form text and are not interpreted or validated. Empty string fields render as unspecified, except an empty benchmark, which renders as none.

Public Members

std::string convention = "TWR"

Return convention label.

std::string sample_period

Sample-period description.

std::string data_quality

Data-quality description.

std::string fees = "gross-of-fees"

Fee-basis label.

std::string cashflows = "none"

Cashflow-treatment label.

std::string benchmark

Benchmark label; empty renders “none”.

std::string risk_free

Risk-free-rate description.

bool annualized = true

Whether values are annualized.

std::vector<std::string> notes

Extra notes, joined with “; “.

std::string return_type

Return-type label.

std::string units

Unit label.

std::string frequency

Observation-frequency label.

enum class fincore::fee_treatment

Treatment of fees when computing cashflow-adjusted performance.

Values:

enumerator net

Uses valuations as supplied; fees do not adjust returns.

enumerator gross

Adds fees back to the closing valuation of their period.

dataframe fincore::cashflow_adjusted_returns(const dataframe &valuations, const std::optional<dataframe> &cashflows = std::nullopt, const std::optional<dataframe> &fees = std::nullopt, fee_treatment treatment = fee_treatment::net, std::string_view value_column = "value", std::string_view amount_column = "amount", std::string_view timing_column = "timing", std::string_view fees_column = "fees")

Returns one cashflow-adjusted simple return per adjacent valuation pair.

The output is indexed by the later timestamp of each valuation pair and stores decimal returns under returns. A period return is (closing - end_flows) / (opening + start_flows) - 1; under fee_treatment::gross the closing capital also includes fees recorded on the closing valuation date. Cashflows and fees must fall on valuation dates; same-day entries are summed, and nonzero entries on the first valuation date are rejected.

Parameters:
  • valuations – Valuation frame with a required double value_column, at least two rows and a strictly increasing, unique index. Values must be finite and non-negative, and every value except the last must be positive.

  • cashflows – Optional ledger with double amount_column and timing_column columns. Timing 1 marks a start-of-period flow and timing 0 an end-of-period flow.

  • fees – Optional fee ledger with a double fees_column.

  • treatment – Fee treatment used for closing valuations.

  • value_column – Column holding valuations in valuations.

  • amount_column – Column holding signed cashflow amounts in cashflows.

  • timing_column – Column holding the start/end timing flag in cashflows.

  • fees_column – Column holding fee amounts in fees.

Throws:

input_error – If a column name is empty, the column is absent, not double or misaligned; the valuation index is not strictly increasing; a valuation is non-finite or negative; an opening valuation is not positive; a cashflow or fee date is absent from the valuation index; a timing flag is not 0 or 1; a nonzero cashflow or fee falls on the first valuation date; or a period capital base or return is not representable.

Returns:

Cashflow-adjusted simple returns indexed by the later timestamp of each valuation pair.

double fincore::cashflow_adjusted_twr(const dataframe &valuations, const std::optional<dataframe> &cashflows = std::nullopt, const std::optional<dataframe> &fees = std::nullopt, fee_treatment treatment = fee_treatment::net, std::string_view value_column = "value", std::string_view amount_column = "amount", std::string_view timing_column = "timing", std::string_view fees_column = "fees")

Returns the compounded cashflow-adjusted time-weighted return.

Compounding multiplies 1 + r over the per-period returns from cashflow_adjusted_returns() and subtracts one.

Parameters:
  • valuations – Valuation frame accepted by cashflow_adjusted_returns().

  • cashflows – Optional cashflow ledger accepted by cashflow_adjusted_returns().

  • fees – Optional fee ledger accepted by cashflow_adjusted_returns().

  • treatment – Fee treatment used for closing valuations.

  • value_column – Column holding valuations in valuations.

  • amount_column – Column holding signed cashflow amounts in cashflows.

  • timing_column – Column holding the start/end timing flag in cashflows.

  • fees_column – Column holding fee amounts in fees.

Throws:

input_error – If cashflow_adjusted_returns() rejects the inputs or the compounded value is not representable.

Returns:

Decimal cumulative time-weighted return over all periods.

double fincore::mwr(const dataframe &cashflows, std::optional<double> period = std::nullopt, std::string_view amount_column = "cashflow")

Returns the money-weighted return of periodic cashflows.

Uses row order, not timestamps, as the period axis. The first row is period zero and each subsequent row is period intervals later. Zero amounts are dropped after their positions are fixed. A rate is solved only for flows with exactly one sign change.

Parameters:
  • cashflows – Frame whose rows are cashflow periods.

  • period – Optional positive finite interval between rows; defaults to 1.0.

  • amount_column – Double column of signed cashflow amounts.

Throws:

input_error – If the column is invalid, fewer than two amounts are present, an amount is non-finite, or period is not positive and finite.

Returns:

Decimal per-period internal rate of return, or NaN when the flows are not conventional or no representable root is found.

std::string fincore::render_disclosure(const disclosure_context &context)

Returns a deterministic, newline-separated disclosure block.

Lines appear in this order: convention, return type, units, frequency, sample period, data quality, fees, cashflows, benchmark, risk-free and annualized. A non-empty notes list adds a final Notes: line whose entries are joined by ;; that final line has no trailing newline.

Parameters:

context – Disclosure values to render.

Returns:

Plain-text disclosure block.

std::pair<double, double> fincore::sharpe_confidence_interval(const dataframe &returns, double risk_free = 0.0, double z = 1.96, std::string_view column = returns_column)

Returns a normal-approximation confidence interval for the per-period Sharpe ratio.

The interval is S - z * SE to S + z * SE, using the Sharpe ratio and standard error described by sharpe_standard_error(). Fewer than two non-NaN observations yield {NaN, NaN}. No annualization is applied.

Parameters:
  • returns – Frame containing periodic simple returns.

  • risk_free – Per-period risk-free rate subtracted from each return.

  • z – Normal quantile multiplier, such as 1.96 for a 95% interval.

  • column – Double column of returns.

Throws:

input_error – If the column name is empty, the column is absent, not a double column, or does not match the frame index length.

Returns:

Lower and upper interval bounds.

double fincore::sharpe_standard_error(const dataframe &returns, double risk_free = 0.0, std::string_view column = returns_column)

Returns the standard error of the per-period Sharpe ratio.

Each non-NaN observation is shifted by risk_free. The Sharpe ratio is the shifted mean divided by the sample standard deviation (zero when that deviation is at most 1e-15), and the standard error is sqrt((1 + 0.5 * S^2) / n). Fewer than two non-NaN observations yield NaN. No annualization is applied.

Parameters:
  • returns – Frame containing periodic simple returns.

  • risk_free – Per-period risk-free rate subtracted from each return.

  • column – Double column of returns.

Throws:

input_error – If the column name is empty, the column is absent, not a double column, or does not match the frame index length.

Returns:

Standard error of the per-period Sharpe ratio.

double fincore::standard_error_of_mean(const dataframe &values, std::string_view column = returns_column)

Returns the standard error of the sample mean of column.

NaN observations are dropped. The result is the sample standard deviation (n - 1 denominator) divided by sqrt(n). Fewer than two non-NaN observations yield NaN.

Parameters:
  • values – Frame containing the observations.

  • column – Double column of observations.

Throws:

input_error – If the column name is empty, the column is absent, not a double column, or does not match the frame index length.

Returns:

Standard error in the same units as column.

double fincore::twr(const dataframe &returns, std::string_view column = returns_column)

Returns the total time-weighted return of column.

Multiplies 1 + r over all non-NaN observations and subtracts one. NaN observations are skipped; infinities and returns at or below -1 are used as supplied. An empty series or a series containing only NaN yields 0.0.

Parameters:
  • returns – Frame containing the return series.

  • column – Double column of periodic simple returns.

Throws:

input_error – If the column name is empty, the column is absent, not a double column, or does not match the frame index length.

Returns:

Decimal cumulative simple return.

double fincore::xirr(const dataframe &cashflows, std::string_view amount_column = "cashflow")

Returns the annualized internal rate of return of dated cashflows.

Cashflows are summed by UTC calendar day and sorted by date; zero daily totals are dropped. Period fractions use actual elapsed days divided by

  1. A rate is solved only for flows with exactly one sign change, and the result is rejected when no representable root meets the solver’s residual budget.

Parameters:
  • cashflows – Frame whose index holds cashflow dates.

  • amount_column – Double column of signed cashflow amounts.

Throws:

input_error – If the column is invalid, an amount is non-finite, or fewer than two distinct UTC cashflow dates are present.

Returns:

Decimal annualized rate, or NaN when the flows are not conventional (not exactly one sign change) or no representable root is found.