Simulation

Bootstrap, Monte Carlo and stress scenarios with seeded reproducibility.

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 simulation_result

Owning simulation result with paths, terminal statistics and risk measures.

Instances are created by analyze_simulation_paths; the constructor that accepts ready-made state is private. Copy and move operations copy or transfer the owned path frame, statistics and terminal distribution.

Public Functions

simulation_result(const simulation_result&) = default

Copies the path frame, statistics and terminal distribution.

Parameters:

other – Result to copy.

simulation_result(simulation_result&&) = default

Moves the path frame, statistics and terminal distribution.

Parameters:

other – Result to move from.

simulation_result &operator=(const simulation_result&) = default

Assigns by copying the path frame, statistics and terminal distribution.

Parameters:

other – Result to copy.

Returns:

This result after assignment.

simulation_result &operator=(simulation_result&&) = default

Assigns by moving the path frame, statistics and terminal distribution.

Parameters:

other – Result to move from.

Returns:

This result after assignment.

~simulation_result() = default

Destroys the owned path frame, statistics and terminal distribution.

const dataframe &paths() const noexcept

Returns the owned path frame.

Returns:

Const reference valid for the lifetime of this result.

const simulation_terminal_statistics &statistics() const noexcept

Returns the cached terminal statistics.

Returns:

Const reference to the statistics computed at construction.

std::size_t path_count() const noexcept

Returns the number of simulated paths.

Returns:

Number of path columns and terminal values.

std::size_t observation_count() const noexcept

Returns the number of observations per path.

Returns:

Number of rows in the path frame index.

double terminal_value_at_risk(double alpha = 0.05) const

Computes the terminal value at risk at alpha.

The result is the linearly interpolated alpha percentile of the terminal values.

Parameters:

alpha – Tail probability in (0, 1); defaults to 0.05.

Returns:

The percentile, or NaN when alpha is not finite or outside (0, 1).

double terminal_conditional_value_at_risk(double alpha = 0.05) const

Computes the terminal conditional value at risk at alpha.

The result is the arithmetic mean of the terminal values at or below the corresponding value at risk.

Parameters:

alpha – Tail probability in (0, 1); defaults to 0.05.

Throws:

input_error – If no terminal value lies in the tail or the mean is not finite.

Returns:

The tail mean, or NaN when alpha is not finite or outside (0, 1).

struct bootstrap_confidence_interval_result

Lower and upper bounds of a percentile bootstrap interval.

Public Members

double lower

Lower percentile bound.

double upper

Upper percentile bound.

struct bootstrap_summary_result

Bootstrap summaries for the mean, deviation and ratio statistics.

Public Members

bootstrap_summary_statistic mean

Summary of the mean statistic.

bootstrap_summary_statistic sample_standard_deviation

Summary of the sample deviation statistic.

bootstrap_summary_statistic sharpe_ratio

Summary of the Sharpe-like ratio.

bootstrap_summary_statistic sortino_ratio

Summary of the Sortino-like ratio.

struct bootstrap_summary_statistic

Summary of one bootstrapped statistic.

Public Members

double value

Statistic observed on the NaN-cleaned input sample.

double standard_error

Sample deviation of the bootstrap draws.

bootstrap_confidence_interval_result confidence_interval

Percentile interval of the bootstrap draws.

struct custom_stress_scenario

User-defined stress transformation.

Each stressed return is (return + return_shift) * return_multiplier. Names must be non-empty, distinct and must not reuse a predefined name.

Public Members

std::string name

Scenario name shown in results.

double return_shift = {0.0}

Additive shift applied first.

double return_multiplier = {1.0}

Multiplier applied after the shift.

struct simulation_parameters

Annualized drift and volatility estimated from periodic returns.

Public Members

double drift

Annualized arithmetic mean return.

double volatility

Annualized sample standard deviation.

struct simulation_terminal_percentiles

Terminal-value percentiles using linear interpolation.

Public Members

double percentile_1

1st percentile.

double percentile_5

5th percentile.

double percentile_25

25th percentile.

double percentile_50

50th percentile (the median).

double percentile_75

75th percentile.

double percentile_95

95th percentile.

double percentile_99

99th percentile.

struct simulation_terminal_statistics

Summary statistics of the terminal path values.

Public Members

double mean

Arithmetic mean of terminal values.

double sample_standard_deviation

Sample deviation (n - 1); NaN for one path.

double median

Median terminal value.

simulation_terminal_percentiles percentiles

Terminal-value percentiles.

struct stress_scenario_result

Outcome of one stress scenario.

Public Members

std::string name

Scenario name.

dataframe stressed_returns

Stressed returns on the input index and column.

double cumulative_return

Compounded stressed return.

double max_drawdown

Deepest drawdown as a negative fraction.

double volatility

Annualized volatility of the stressed returns.

double final_value

1 + cumulative_return.

enum class fincore::bootstrap_statistic

Statistic computed for each bootstrap resample.

Values:

enumerator mean

Arithmetic mean of the drawn sample.

enumerator sample_standard_deviation

Sample deviation (n - 1); NaN for one value.

enumerator median

Median, averaging the two middle values.

enumerator sharpe_ratio

Mean / sample deviation, scaled by sqrt(252).

enumerator sortino_ratio

Mean / downside deviation, scaled by sqrt(252).

enumerator minimum

Smallest drawn value.

enumerator maximum

Largest drawn value.

enum class fincore::predefined_stress_scenario

Predefined stress transformations.

Values:

enumerator crash

Sets the middle return to -0.20 (crash).

enumerator spike

Sets the middle return to 0.10 (spike).

enumerator volatility_crush

Halves every return (vol_crush).

enumerator volatility_spike

Doubles every return (vol_spike).

simulation_result fincore::analyze_simulation_paths(const dataframe &paths)

Analyzes a path frame into a simulation result.

The frame must have a non-empty index and only double columns, with each column matching the index length. Each column’s last value is its terminal value and must be finite. The result owns a copy of the paths plus the terminal statistics and distribution.

Parameters:

paths – Path frame whose columns are simulation paths.

Throws:

input_error – If the frame is structurally invalid, a terminal value is not finite, or allocation fails.

Returns:

A result owning the copied paths, terminal statistics and terminal distribution.

double fincore::annualize(double value, std::string_view period = "daily")

Annualizes a volatility-like value with the source’s period factors.

Recognized periods are case-insensitive daily (252), weekly (52), monthly (12) and yearly (1); any other value uses the daily factor. The result is value * sqrt(factor).

Parameters:
  • value – Volatility-like value to scale.

  • period – Period label; defaults to daily.

Returns:

The scaled value.

dataframe fincore::antithetic_variates(const dataframe &paths)

Appends antithetic mirror paths to a path frame.

Every double column is retained, and a companion column named antithetic_<name> is added with values 2 * c - x, where c is the first value of the first double column. The input index and column order are preserved, and mirror columns follow the source columns.

Parameters:

paths – Path frame with only double columns.

Throws:

input_error – If the frame has no rows or no double columns, contains a non-double column, has mismatched column lengths, a derived name collides with an input name, or a derived name is unrepresentable.

Returns:

A frame containing the original columns followed by their mirrors.

bootstrap_confidence_interval_result fincore::bootstrap_confidence_interval(const dataframe &returns, std::size_t resample_count = 10000U, double alpha = 0.05, bootstrap_statistic statistic = bootstrap_statistic::mean, std::optional<std::uint64_t> seed = std::nullopt, std::string_view column = returns_column)

Computes a percentile bootstrap confidence interval.

The interval spans the alpha / 2 and 1 - alpha / 2 linearly interpolated percentiles of the generated statistic distribution. Validation of alpha happens before any resampling.

Parameters:
  • returns – Frame containing the selected return column.

  • resample_count – Number of bootstrap draws.

  • alpha – Significance level in (0, 1); defaults to 0.05.

  • statistic – Statistic computed for every draw.

  • seed – Optional deterministic seed.

  • column – Double return column to resample.

Throws:

input_error – If alpha is not finite or outside (0, 1), the distribution rejects the input, or generated statistics are not finite.

Returns:

The lower and upper percentile bounds.

dataframe fincore::bootstrap_distribution(const dataframe &returns, std::size_t resample_count = 10000U, bootstrap_statistic statistic = bootstrap_statistic::mean, std::optional<std::uint64_t> seed = std::nullopt, std::string_view column = returns_column)

Generates a bootstrap distribution of one statistic.

NaN returns are removed before resampling and infinities are rejected. Each of the resample_count draws samples the retained population with replacement at its original size. A supplied seed makes the native 64-bit Mersenne Twister stream repeatable; omitting it seeds from std::random_device. Native draws are not guaranteed to match any other RNG implementation.

Parameters:
  • returns – Frame containing the selected return column.

  • resample_count – Number of bootstrap draws; 0 yields an empty frame after input validation.

  • statistic – Statistic computed for every draw.

  • seed – Optional deterministic seed.

  • column – Double return column to resample.

Throws:

input_error – If the column or statistic is invalid, no non-NaN return remains, a retained value is infinite, or the output size is unrepresentable.

Returns:

A frame with an ordinal index and one bootstrap double column.

dataframe fincore::bootstrap_distribution(const dataframe &returns, std::span<const std::vector<std::size_t>> draw_indices, bootstrap_statistic statistic = bootstrap_statistic::mean, std::string_view column = returns_column)

Generates a bootstrap distribution from explicit draw indices.

Draws address the NaN-cleaned positional input population. Every draw must contain exactly that population’s size and only in-range indices. No random engine is used. Empty draw_indices yields an empty distribution after the input and statistic are validated.

Parameters:
  • returns – Frame containing the selected return column.

  • draw_indices – Resampling index sets, one per output row.

  • statistic – Statistic computed for every draw.

  • column – Double return column to resample.

Throws:

input_error – If the column or statistic is invalid, no non-NaN return remains, a retained value is infinite, a draw length or index is invalid, or the output size is unrepresentable.

Returns:

A frame with an ordinal index and one bootstrap double column.

bootstrap_summary_result fincore::bootstrap_summary(const dataframe &returns, std::size_t resample_count = 10000U, double alpha = 0.05, std::optional<std::uint64_t> seed = std::nullopt, std::string_view column = returns_column)

Computes bootstrap summaries for four statistics.

Each statistic is summarized in a separate native run with the supplied seed and reports its observed value, the sample deviation of its draws, and the percentile interval at alpha / 2 and 1 - alpha / 2. Validation of alpha and the resample count happens before input access.

Parameters:
  • returns – Frame containing the selected return column.

  • resample_count – Number of draws per statistic; must be at least 2.

  • alpha – Significance level in (0, 1); defaults to 0.05.

  • seed – Optional deterministic seed.

  • column – Double return column to resample.

Throws:

input_error – If alpha is invalid, resample_count is less than 2, the input is malformed, or an observed or generated summary value is not finite.

Returns:

Summaries for the mean, sample deviation, Sharpe and Sortino statistics.

simulation_parameters fincore::estimate_simulation_parameters(const dataframe &returns, std::size_t periods_per_year = 252, std::string_view column = returns_column)

Estimates annualized drift and volatility from a return column.

NaN values are dropped and infinities are rejected; at least two retained returns are required. Drift is the retained mean multiplied by periods_per_year; volatility is the sample standard deviation (n - 1) multiplied by sqrt(periods_per_year). Timestamp order is not used.

Parameters:
  • returns – Frame containing the selected double return column.

  • periods_per_year – Positive number of periods per year.

  • column – Double return column name.

Throws:

input_error – If the column is missing or not double, periods_per_year is zero, fewer than two finite returns remain, a value is infinite, or arithmetic overflows.

Returns:

Annualized drift and sample volatility.

dataframe fincore::gbm_from_returns(const dataframe &returns, std::size_t horizon = 252U, std::size_t path_count = 1000U, std::size_t periods_per_year = 252U, std::optional<std::uint64_t> seed = std::nullopt, std::string_view column = returns_column)

Simulates cumulative-return paths calibrated from historical returns.

Annualized drift and volatility are estimated from the selected return column (NaN values dropped), prices are simulated from 1.0 over horizon periods at periods_per_year, the initial price row is removed, and each path stores price_t - 1. Columns are named path_<n>.

Parameters:
  • returns – Frame containing the selected return column.

  • horizon – Number of periods to simulate.

  • path_count – Number of paths; must be positive.

  • periods_per_year – Positive periods per year used for calibration and step sizing.

  • seed – Optional deterministic seed; omitted seeds are not reproducible.

  • column – Double return column used for calibration.

Throws:

input_error – If calibration or path generation rejects the input, or the requested names or output shape is unrepresentable.

Returns:

A frame whose index excludes the initial row and whose path columns hold cumulative returns.

dataframe fincore::generate_correlation_breakdown(std::span<const std::string_view> asset_names, double target_correlation = 1.0)

Builds a square correlation breakdown matrix.

The matrix has one row and one double column per asset, in the supplied order, with an ordinal index. Diagonal entries are 1.0 and every off-diagonal entry is target_correlation, which is retained even when it lies outside [-1, 1]. Empty asset_names yields an empty frame.

Parameters:
  • asset_names – Ordered asset names; entries must be non-empty, distinct and free of embedded NULs.

  • target_correlation – Finite off-diagonal correlation value.

Throws:

input_error – If a name is empty, duplicated or contains an embedded NUL, the target is not finite, or the matrix is unrepresentable.

Returns:

A square frame with one double column per asset.

dataframe fincore::geometric_brownian_motion(double initial_value, double annualized_drift, double annualized_volatility, double total_years, double time_step, std::size_t path_count, std::optional<std::uint64_t> seed = std::nullopt)

Simulates geometric Brownian motion price paths.

Each path starts at initial_value and evolves for trunc(total_years / time_step) steps using S[k + 1] = S[k] * exp((drift - volatility^2 / 2) * time_step + volatility * sqrt(time_step) * Z) with standard normal draws Z. The output index is ordinal and paths are named path_<n>; the first row is the initial value.

Parameters:
  • initial_value – Starting price of every path.

  • annualized_drift – Annualized drift of the process.

  • annualized_volatility – Annualized volatility of the process.

  • total_years – Total horizon in years.

  • time_step – Time between observations in years; must be positive.

  • path_count – Number of paths; must be positive.

  • seed – Optional deterministic seed; omitted seeds are not reproducible.

Throws:

input_error – If an input is not finite, time_step is not positive, total_years is negative, path_count is zero, or the requested index, names or output shape is unrepresentable.

Returns:

A frame with one initial row plus one row per truncated step, and one double column per path.

dataframe fincore::latin_hypercube_sampling(std::size_t sample_count, std::size_t dimension_count, std::optional<std::uint64_t> seed = std::nullopt)

Samples a Latin hypercube design.

Every output column is a permutation of sample_count equal-probability strata in [0, 1), with one uniformly distributed point per stratum. The index is ordinal and columns are named lhs_dimension_<n>. Zero counts produce the corresponding empty shape. A supplied seed makes the native 64-bit Mersenne Twister stream repeatable; omitted seeds are not reproducible and native draws make no cross-implementation RNG claim.

Parameters:
  • sample_count – Number of rows to generate.

  • dimension_count – Number of independent dimensions (columns).

  • seed – Optional deterministic seed.

Throws:

input_error – If the requested index, names, stratum count or output shape is unrepresentable, or a sampled point is not finite.

Returns:

A frame with an ordinal index and one double column per dimension.

dataframe fincore::scenario_table(std::span<const stress_scenario_result> results)

Builds a tabular summary of stress scenario results.

The table has an ordinal index and one row per result, with the columns scenario, cumulative_return, max_drawdown, volatility and final_value. Empty input yields a typed empty table.

Parameters:

results – Scenario results to tabulate.

Returns:

A frame with one row per result.

simulation_result fincore::simulate_monte_carlo(const dataframe &returns, std::size_t path_count = 1000U, std::size_t horizon = 252U, bool antithetic = false, std::optional<std::uint64_t> seed = std::nullopt, std::string_view column = returns_column)

Simulates a calibrated Monte Carlo path set from historical returns.

Annualized drift and volatility are estimated from the selected return column (NaN values dropped), geometric Brownian motion is simulated from 1.0 for horizon steps at 252 periods per year, and the resulting price paths are converted to cumulative returns. When antithetic is true, antithetic_variates is applied to those paths, so the stored path count is doubled.

Parameters:
  • returns – Frame containing the return column.

  • path_count – Number of base paths.

  • horizon – Number of simulated periods.

  • antithetic – Whether to append antithetic mirror paths.

  • seed – Optional deterministic seed; omitted seeds are not reproducible.

  • column – Double return column used for calibration.

Throws:

input_error – If calibration or path generation rejects the input.

Returns:

A simulation result owning the path frame and terminal statistics.

simulation_result fincore::simulate_monte_carlo_from_parameters(double annualized_drift, double annualized_volatility, double initial_value = 1.0, std::size_t path_count = 1000U, std::size_t horizon = 252U, std::optional<std::uint64_t> seed = std::nullopt)

Simulates price paths directly from annualized parameters.

Annualized drift and volatility are converted to per-step values (drift / 252 and volatility / sqrt(252)), and horizon periods are simulated with a 1 / 252 year step. The returned paths start at initial_value and include the initial row.

Parameters:
  • annualized_drift – Annualized drift of the price process.

  • annualized_volatility – Annualized volatility of the price process.

  • initial_value – Starting price of every path.

  • path_count – Number of paths.

  • horizon – Number of simulated periods.

  • seed – Optional deterministic seed; omitted seeds are not reproducible.

Throws:

input_error – If an input is non-finite, path_count is zero, or the requested index, names or output shape are unrepresentable.

Returns:

A simulation result owning the price paths and terminal statistics.

std::vector<stress_scenario_result> fincore::stress_test(const dataframe &returns, std::string_view column = returns_column)

Runs the four predefined stress scenarios in source order.

Applies crash, spike, vol_crush and vol_spike in that order.

Parameters:
  • returns – Frame containing the return column.

  • column – Double return column to stress.

Throws:

input_error – If the column is missing, not double, empty or contains NaN.

Returns:

One result per predefined scenario, in source order.

std::vector<stress_scenario_result> fincore::stress_test(const dataframe &returns, std::span<const predefined_stress_scenario> scenarios, std::span<const custom_stress_scenario> custom_scenarios = {}, std::string_view column = returns_column)

Runs selected predefined and custom stress scenarios.

Predefined results appear first in caller order, followed by custom results in caller order. NaN returns are rejected, while infinite returns are retained.

Parameters:
  • returns – Frame containing the return column.

  • scenarios – Predefined scenarios to apply; duplicates are rejected.

  • custom_scenarios – Custom scenarios to apply; names must be non-empty, distinct and not one of crash, spike, vol_crush or vol_spike.

  • column – Double return column to stress.

Throws:

input_error – If return validation fails, a predefined scenario is unknown or duplicated, or a custom scenario has an empty, reserved or duplicate name or non-finite parameters.

Returns:

One result per requested scenario, in the order described above.

std::vector<double> fincore::validate_returns(std::span<const double> returns)

Validates and copies a return series.

The source rejects empty input and NaN values; the native contract also rejects infinities so every returned value is finite. Contract failures raise fincore_error with a structured validation category/code.

Parameters:

returns – Return values to validate.

Throws:

fincore_error – If returns is empty, contains NaN, or contains infinity.

Returns:

A validated copy of returns.

std::vector<double> fincore::validate_returns(const dataframe &returns, std::string_view column = returns_column)

Validates and copies the named return column of a frame.

Parameters:
  • returns – Frame containing the selected double column.

  • column – Name of the double return column; must not be empty.

Throws:

fincore_error – If column is empty or missing, the column is not a double column, or the selected values are empty, NaN or infinite.

Returns:

A validated copy of the selected column.

constexpr std::string_view fincore::antithetic_path_column_prefix{"antithetic_"}

Prefix of generated antithetic mirror columns.

constexpr std::string_view fincore::bootstrap_statistic_column = {"bootstrap"}

Name of the generated bootstrap statistic column.

constexpr std::string_view fincore::gbm_path_column_prefix = {"path_"}

Prefix of generated geometric-Brownian-motion path columns.

constexpr std::string_view fincore::latin_hypercube_dimension_column_prefix{"lhs_dimension_"}

Prefix of Latin-hypercube dimension columns.

constexpr std::string_view fincore::stress_cumulative_return_column{"cumulative_return"}

Name of the cumulative-return column in a scenario table.

constexpr std::string_view fincore::stress_final_value_column = {"final_value"}

Name of the final-value column in a scenario table.

constexpr std::string_view fincore::stress_max_drawdown_column = {"max_drawdown"}

Name of the maximum-drawdown column in a scenario table.

constexpr std::string_view fincore::stress_scenario_name_column = {"scenario"}

Name of the scenario-name column in a scenario table.

constexpr std::string_view fincore::stress_volatility_column = {"volatility"}

Name of the volatility column in a scenario table.