Portfolio

Positions, capacity, exposure and portfolio model inputs.

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 portfolio_inputs

Detached optional input tables for portfolio-domain workflows.

Each supplied frame is owned by this object; materialize() returns another independent copy. Only supplied fields are returned, under returns, positions, transactions and benchmark_returns respectively. A supplied empty frame is retained.

Public Functions

inline portfolio_inputs(std::optional<dataframe> returns = std::nullopt, std::optional<dataframe> positions = std::nullopt, std::optional<dataframe> transactions = std::nullopt, std::optional<dataframe> benchmark_returns = std::nullopt)

Constructs a container from optional input frames.

Parameters:
  • returns – Optional return frame stored under returns.

  • positions – Optional position frame stored under positions.

  • transactions – Optional transaction frame stored under transactions.

  • benchmark_returns – Optional benchmark frame stored under benchmark_returns.

inline std::map<std::string, dataframe> materialize() const

Returns an independent copy of each supplied input table.

Keys are added only for fields that were supplied; a supplied empty frame is retained. Repeated calls produce independent frames.

Returns:

Map from input name to an owned copy of the frame.

struct capacity_assessment

Value-owned results of assess_liquidity().

Public Members

dataframe liquidation_days

Days to liquidate per non-cash asset, indexed by position dates after the rolling-volume warm-up.

std::vector<ticker_liquidity> ticker_maximums

Maximum days to liquidate and allocation percentage per symbol, sorted by symbol.

std::vector<transaction_liquidity> low_liquidity_transactions

Maximum bar-consumption percentage per symbol in the scan window, sorted by symbol.

struct capacity_config

Parameters controlling assess_liquidity().

Public Members

double max_bar_consumption = 0.2

Maximum fraction of a bar’s dollar volume assumed executable per day; must be in (0, 1].

double capital_base = 1'000'000.0

Capital in currency units used to scale position allocations; must be positive.

std::size_t mean_volume_window = 5U

Number of preceding bars in the rolling mean dollar volume; must be at least 1.

std::optional<std::size_t> last_n_days

Optional trailing-day scan limit; when set, must be at least 1.

struct daily_txn_bar

One aggregated symbol/day trade bar.

Public Members

timestamp date

Trade date.

std::string symbol

Traded symbol.

double amount

Sum of absolute trade amounts for the day.

double price

Bar price on that date.

double volume

Bar share volume on that date.

struct exposure_bundle

Long, short, gross and net exposure frames sharing one index.

Public Members

dataframe long_exposure

Long exposure values; units follow the producing API.

dataframe short_exposure

Short exposure values; sign and units follow the producing API.

dataframe gross_exposure

Gross exposure values; units follow the producing API.

dataframe net_exposure

Net exposure (long minus short); empty for APIs that do not populate it.

struct long_short_aggregate

Per-statistic results for all, long and short round trips.

Public Members

std::vector<std::string> statistic_names

Statistic names in input order.

std::vector<double> all_trades

Statistic values over all round trips.

std::vector<double> long_trades

Statistic values over long round trips; NaN when none exist.

std::vector<double> short_trades

Statistic values over short round trips; NaN when none exist.

struct position_record

One stacked position observation.

Public Members

timestamp date

Position timestamp.

std::string symbol

Position column name.

double value

Position value; may be NaN.

struct ranked_position

Symbol and value pair ranked by get_top_long_short_abs().

Public Members

std::string symbol

Position column name.

double value

Extremum over the column’s rows.

struct round_trip_stat_table

Rectangular statistic table with row and column labels.

Public Members

std::vector<std::string> row_labels

Labels for the value rows.

std::vector<std::string> column_labels

Labels for the value columns.

std::vector<std::vector<double>> values

Row-major statistic values.

struct round_trip_statistic

Named statistic applied by agg_all_long_short().

Public Members

std::string name

Output statistic name.

std::function<double(std::span<const double>)> compute

Callable statistic; must be set before aggregation.

struct round_trip_stats

Round-trip tables and per-symbol return statistics.

The first four tables contain rows for the non-empty All trades, short and long groups in that order.

Public Members

round_trip_stat_table pnl

Profit/loss statistics.

round_trip_stat_table summary

Trade counts and profitability.

round_trip_stat_table duration

Holding-duration statistics in days, plus trades per day and month.

round_trip_stat_table returns

Round-trip return statistics.

std::vector<std::string> symbol_labels

Symbols in ascending order.

std::vector<std::string> symbol_stat_labels

Fixed labels for the eight per-symbol return statistics.

std::vector<std::vector<double>> symbol_values

One row per symbol statistic, one value per symbol in symbol_labels.

struct round_trip_transaction

Transaction record used to build round-trip inputs and closing trades.

Public Members

timestamp date = 0

UTC nanosecond timestamp.

std::string symbol

Traded symbol.

double amount = 0.0

Signed quantity.

double price = 0.0

Execution price per unit.

struct sector_assignment

Symbol-to-sector assignment used by get_sector_exposures().

Public Members

std::string symbol

Position column name.

std::string sector

Output sector column name.

struct sector_definition

Numeric identifier and output label for an exposure bucket.

Public Members

double id

Numeric bucket identifier; sector exposures compare it with panel cells.

std::string name

Output column label.

struct ticker_liquidity

Per-symbol maximum liquidation time.

Public Members

std::string symbol

Position column name.

double days_to_liquidate

Maximum days in the scan; NaN if none.

double position_allocation_pct

Position allocation on that date as a percentage (100 * fraction).

struct top_positions

Ranked long, short and absolute position lists.

Public Members

std::vector<ranked_position> long_positions

Largest positive column extrema, in descending order.

std::vector<ranked_position> short_positions

Most negative column extrema, in ascending order.

std::vector<ranked_position> absolute_positions

Largest absolute column extrema, in descending order.

struct trade_record

One trade used by the liquidity helpers.

Public Members

timestamp date

Trade timestamp; bar helpers match it exactly.

std::string symbol

Traded symbol.

double amount

Signed quantity; absolute value is used.

struct transaction_liquidity

Per-symbol worst transaction bar consumption.

Public Members

std::string symbol

Traded symbol.

timestamp date

Date of maximum consumption.

double max_percent_bar_consumed

Traded amount as a percentage of bar volume.

enum class fincore::turnover_denominator

Divisor convention used by get_turnover().

Values:

enumerator actual_gross_book

Average of the previous and current gross book, excluding cash; the first row uses half of the current gross book.

enumerator portfolio_value

Row sum of all numeric position columns, including cash.

std::vector<round_trip_transaction> fincore::add_closing_transactions(const dataframe &positions, std::span<const round_trip_transaction> transactions)

Appends one closing transaction per still-open position.

The net amount per symbol is computed from transactions. For each non-cash double column of positions with an absolute net amount of at least 1e-6, a closing transaction is appended at the final position timestamp plus one second. Its amount is the opposite of the net amount and its price is the final position value divided by the net amount. Zero-amount transactions are then removed from the combined result.

Parameters:
  • positions – Frame with at least one row and a double cash column; its other double columns are treated as positions.

  • transactions – Existing transactions, preserved in input order.

Throws:

input_error – If positions is empty or has no double cash column.

Returns:

Input transactions followed by generated closing transactions, with zero-amount entries removed.

dataframe fincore::adjust_returns_for_slippage(const dataframe &returns, const dataframe &positions, const dataframe &transactions, double slippage_bps, std::string_view returns_column_name = returns_column, std::string_view amount_column_name = transaction_amount_column, std::string_view price_column_name = transaction_price_column)

Adjusts daily returns for slippage on traded notional.

Every UTC day in the sorted union of return, position and transaction days is processed. A day with both a return and a portfolio value supplies pnl = return * portfolio_value; a cost of traded notional * slippage_bps / 10000 is subtracted from that PnL, and the day’s return is rescaled by the adjusted-to-original PnL ratio. Days without a usable return/PnL pair and days with zero PnL yield 0.0.

Parameters:
  • returns – Frame with a double returns_column_name column whose UTC days are strictly increasing and unique.

  • positions – Wide position frame whose double columns are summed per row to obtain portfolio value; its UTC days must be strictly increasing and unique.

  • transactions – Transaction frame consumed by get_txn_vol().

  • slippage_bps – Slippage in basis points of traded notional.

  • returns_column_name – Input and output return column name.

  • amount_column_name – Signed-quantity column in transactions.

  • price_column_name – Execution-price column in transactions.

Throws:

input_error – If a column is invalid, a return or position day is duplicated or out of order, or a required transaction column is invalid.

Returns:

Frame indexed by the sorted union of UTC days with adjusted decimal returns under returns_column_name.

long_short_aggregate fincore::agg_all_long_short(const dataframe &round_trips, std::string_view column, std::span<const round_trip_statistic> statistics)

Applies caller-supplied statistics to all, long and short round trips.

Each statistic is evaluated over every value of column in row order, then over the subset selected by the required long boolean column. A statistic whose subset is empty yields NaN for that subset.

Parameters:
  • round_trips – Frame containing a double value column and a bool long column.

  • column – Value column to aggregate.

  • statistics – Statistics to apply, in output order.

Throws:

input_error – If column is not a valid double column, a column length differs from the frame index length, or a statistic has no callable compute.

Returns:

Statistic names and per-statistic values for all, long and short trades.

dataframe fincore::apply_sector_mappings_to_round_trips(const dataframe &round_trips, const std::map<std::string, std::string> &sector_mappings)

Copies round trips and adds a sector column from symbol mappings.

The output keeps the input index, every double column, and every string column except an existing sector; columns of other types are not copied. When a string symbol column is present, a new sector column is appended using sector_mappings; symbols absent from the map get an empty string. Without a symbol column no sector column is added.

Parameters:
  • round_trips – Round-trip frame to copy.

  • sector_mappings – Symbol-to-sector mapping.

Returns:

Copied frame with the mapped sector column when possible.

dataframe fincore::apply_slippage_penalty(const dataframe &returns, const dataframe &transactions_with_bar_data, double simulate_starting_capital, double backtest_starting_capital, double impact = 0.1, std::string_view returns_column_name = returns_column, std::string_view amount_column_name = transaction_amount_column, std::string_view price_column_name = transaction_price_column, std::string_view volume_column_name = transaction_bar_volume_column)

Applies a quadratic market-impact penalty to daily returns.

Trades are rescaled from backtest to simulation capital by simulate_starting_capital / backtest_starting_capital. For each trade, shares = |scaled amount|, dollars = price * shares and the penalty is (shares / volume)^2 * impact * dollars; penalties are summed by UTC day. Return days are compounded into wealth, and each daily penalty is divided by the simulated portfolio value and subtracted from that day’s return.

Parameters:
  • returns – Frame with a double returns_column_name column whose UTC days are strictly increasing and unique.

  • transactions_with_bar_data – Frame with double amount, price and volume columns of matching lengths.

  • simulate_starting_capital – Starting capital of the simulation.

  • backtest_starting_capital – Starting capital of the backtest; must be nonzero.

  • impact – Quadratic impact coefficient.

  • returns_column_name – Input and output return column name.

  • amount_column_name – Signed-quantity column.

  • price_column_name – Execution-price column.

  • volume_column_name – Bar share-volume column.

Throws:

input_error – If backtest_starting_capital is zero, a column name is empty or duplicated, a required column is absent or not double, its length differs from the frame index length, or return days are duplicated or out of order.

Returns:

Frame indexed by UTC return days with adjusted decimal returns under returns_column_name.

capacity_assessment fincore::assess_liquidity(const dataframe &positions, std::span<const trade_record> transactions, const dataframe &prices, const dataframe &volumes, const capacity_config &config = {})

Assesses position and transaction liquidity.

Combines days_to_liquidate_positions(), get_max_days_to_liquidate_by_ticker() and get_low_liquidity_transactions() using one validated configuration.

Parameters:
  • positions – Wide position frame with a double column per asset; a cash column is excluded when present.

  • transactions – Trades used for the low-liquidity scan.

  • prices – Bar price frame with the same index as positions and a double column per position asset.

  • volumes – Bar share-volume frame with the same index as positions and a double column per position asset.

  • config – Liquidity parameters, validated before any computation.

Throws:

input_error – If config is invalid, an input index differs, a transaction amount is non-finite, a trade date is absent from the price index, or a position double column is malformed. The price and volume frames must also provide a double column for every position asset.

Returns:

Value-owned liquidation frame and per-symbol liquidity records.

exposure_bundle fincore::compute_cap_exposures(const dataframe &positions, const dataframe &caps, std::string_view cash_column_name = cash_column)

Returns market-cap bucket exposure fractions.

Buckets use inclusive bounds in the units of the caps panel: Micro covers 50M-300M, Small 300M-2B, Mid 2B-10B, Large 10B-200B and Mega 200B and above. Values below 50M match no bucket, and a value on a shared boundary can match adjacent buckets. Exposure fractions are computed as in compute_sector_exposures().

Parameters:
  • positions – Wide position frame.

  • caps – Wide market-cap panel with the same index as positions and a double column for every non-cash asset.

  • cash_column_name – Name of the cash column excluded when present.

Throws:

input_error – If the frames do not share an identical index, cash_column_name is empty, no double position column exists, or a position column length differs from the index length.

Returns:

Bundle of long, short, gross and net exposure frames with Micro, Small, Mid, Large and Mega columns.

exposure_bundle fincore::compute_sector_exposures(const dataframe &positions, const dataframe &sectors, std::span<const sector_definition> definitions, std::string_view cash_column_name = cash_column)

Returns sector long, short, gross and net exposure fractions.

Assets are matched to definitions by comparing each sectors cell with sector_definition::id. For each row the bundle reports the sector’s share of total long, total short (as a negative fraction) and total gross non-cash positions; net exposure is long minus short. A sector without matched positions contributes zero.

Parameters:
  • positions – Wide position frame.

  • sectors – Wide sector-id panel with the same index as positions and a double column for every non-cash asset.

  • definitions – Sector identifiers and output column names.

  • cash_column_name – Name of the cash column excluded when present.

Throws:

input_error – If the frames do not share an identical index, cash_column_name is empty, no double position column exists, or a position column length differs from the index length.

Returns:

Bundle of long, short, gross and net exposure frames with one column per definition.

dataframe fincore::compute_style_factor_exposures(const dataframe &positions, const dataframe &risk_factors, std::string_view cash_column_name = cash_column)

Returns the weighted style-factor exposure per timestamp.

For each row the exposure is sum(position * factor) / sum(|position|) over non-cash asset columns. NaN positions are skipped entirely; a NaN factor contributes zero to the numerator while its position still counts in the denominator. A zero denominator or non-finite result yields 0.0.

Parameters:
  • positions – Wide position frame.

  • risk_factors – Wide factor panel with the same index as positions and a double column for every non-cash position asset.

  • cash_column_name – Name of the cash column excluded when present.

Throws:

input_error – If the frames do not share an identical index, cash_column_name is empty, no double position column exists, or a position column length differs from the index length.

Returns:

Frame with an exposure double column aligned to the positions index.

exposure_bundle fincore::compute_volume_exposures(const dataframe &shares_held, const dataframe &volumes, double percentile = 0.5)

Returns position-to-volume exposure percentages.

For each row and common share/volume column, shares / volume is computed. Positive shares feed the long set, negative shares feed the short set as positive magnitudes, and absolute fractions feed the gross set. The reported value is 100 times the linearly interpolated quantile at percentile of each set, or 0.0 when the set is empty. Only common columns contribute to the quantiles; a nonzero share value in any share column requires a finite, positive matching volume.

Parameters:
  • shares_held – Wide share-count frame.

  • volumes – Wide share-volume frame with the same index as shares_held.

  • percentile – Quantile in [0, 1] applied within each row.

Throws:

input_error – If the frames do not share an identical index, percentile is outside [0, 1], or a nonzero share value has no finite positive matching volume.

Returns:

Bundle whose long_exposure, short_exposure and gross_exposure frames hold an exposure column; net_exposure is empty.

std::vector<daily_txn_bar> fincore::daily_txns_with_bar_data(std::span<const trade_record> transactions, const dataframe &prices, const dataframe &volumes)

Returns daily aggregated trade bars joined to price and volume panels.

Trades are summed by exact (symbol, date) using absolute amounts; dates are not normalized. Each transaction date must exist in the price index, and each traded symbol must have both price and volume columns. The result is sorted by symbol and then date.

Parameters:
  • transactions – Trade records.

  • prices – Bar price panel.

  • volumes – Bar share-volume panel with the same index as prices.

Throws:

input_error – If an amount is not finite, a trade date is absent from the price index, a traded symbol lacks a price or volume column, or the price and volume indices differ.

Returns:

One bar per symbol/day with absolute amount, price and volume.

dataframe fincore::days_to_liquidate_positions(const dataframe &positions, const dataframe &prices, const dataframe &volumes, double max_bar_consumption = 0.2, double capital_base = 1000000.0, std::size_t mean_volume_window = 5U, std::string_view cash_column_name = cash_column)

Returns days needed to liquidate each position asset.

Position allocations use the row total of all double columns. Daily dollar volume is the mean of price * volume over the preceding mean_volume_window bars, so the first window rows are warm-up and are omitted. Each value is allocation * capital_base / (max_bar_consumption * mean dollar volume); a NaN or zero mean dollar volume yields NaN for that row. The named cash column is excluded when present but is not required.

Parameters:
  • positions – Wide position frame with a double column per asset.

  • prices – Bar price frame with the same index as positions and a double column for every non-cash asset.

  • volumes – Bar share-volume frame with the same index as positions and a double column for every non-cash asset.

  • max_bar_consumption – Maximum fraction of a bar’s dollar volume per day; must be in (0, 1].

  • capital_base – Capital in currency units used to scale positions; must be positive.

  • mean_volume_window – Number of preceding bars in the dollar-volume mean; must be at least 1.

  • cash_column_name – Cash column excluded from the output.

Throws:

input_error – If a numeric parameter is out of range, the frames do not share the same index, a position double column is malformed, or cash_column_name is empty or contains an embedded NUL.

Returns:

Frame indexed by position dates after warm-up with one days column per non-cash asset; empty when there are no complete windows.

dataframe fincore::extract_round_trips(const dataframe &transactions, std::chrono::nanoseconds max_delta = default_round_trip_max_delta, std::string_view symbol_column_name = transaction_symbol_column, std::string_view amount_column_name = transaction_amount_column, std::string_view price_column_name = transaction_price_column)

Extracts FIFO round trips from transaction rows.

Transactions are first consolidated as in groupby_consecutive_transactions(). Each block is matched first-in, first-out against open lots of the opposite direction; a block may both close existing lots and open a remainder. Blocks with a negative price are skipped. The output index is the sequential round-trip number starting at 0.

Output columns are pnl, open_dt, close_dt, long, rt_returns, symbol and duration (elapsed nanoseconds stored as a timestamp). long is true for a round trip closed by a sell; rt_returns is pnl divided by matched opening notional, or 0 when that notional is zero.

Parameters:
  • transactions – Frame with a string symbol column and double amount and price columns.

  • max_delta – Maximum allowed gap when consolidating transactions.

  • symbol_column_name – String symbol column.

  • amount_column_name – Double signed-quantity column.

  • price_column_name – Double execution-price column.

Throws:

input_error – If a field name or required column is invalid, or an elapsed duration cannot be represented as a timestamp.

Returns:

Round-trip frame with a sequential index and the documented columns.

round_trip_stats fincore::gen_round_trip_stats(const dataframe &round_trips, std::string_view pnl_column_name = round_trip_pnl_column, std::string_view returns_column_name = round_trip_return_column, std::string_view long_column_name = round_trip_long_column, std::string_view symbol_column_name = transaction_symbol_column, std::string_view open_column_name = round_trip_open_datetime_column, std::string_view close_column_name = round_trip_close_datetime_column)

Computes round-trip performance tables and per-symbol statistics.

Durations are derived as close - open in days. Each table contains rows for the non-empty All trades, short and long groups in that order, with fixed English column labels. Per-symbol values use the statistics listed in symbol_stat_labels, one column per symbol. An empty frame yields default-constructed, empty statistics.

Parameters:
  • round_trips – Frame containing the required pnl, return, long, symbol, open and close columns.

  • pnl_column_name – Double profit/loss column.

  • returns_column_name – Double round-trip return column.

  • long_column_name – Bool long/short flag column.

  • symbol_column_name – String symbol column.

  • open_column_name – Timestamp opening column.

  • close_column_name – Timestamp closing column.

Throws:

input_error – If a column name contains an embedded NUL, a required double column is invalid, or a required column length differs from the frame index length.

Returns:

Round-trip statistic tables and per-symbol statistics.

std::pair<std::vector<double>, std::vector<double>> fincore::get_long_short_notional(const dataframe &positions, std::string_view cash_column_name = cash_column)

Returns per-timestamp long and short notional values.

Each row sums positive non-cash position values into the first vector and absolute negative values into the second. NaN cells are skipped. The named cash column is excluded when present but is not required.

Parameters:
  • positions – Wide position frame.

  • cash_column_name – Name of the cash column to exclude.

Throws:

input_error – If cash_column_name is empty or contains an embedded NUL, no double column exists, or a double column length differs from the index length.

Returns:

Pair of long and short vectors, each aligned to the positions index.

dataframe fincore::get_long_short_pos(const dataframe &positions, std::string_view cash_column_name = cash_column)

Returns gross long and short position values per timestamp.

Positive non-cash position values are summed into long and negative values are summed into short as positive magnitudes. NaN cells are skipped. The named cash column is excluded when present but is not required.

Parameters:
  • positions – Wide position frame.

  • cash_column_name – Name of the cash column to exclude.

Throws:

input_error – If cash_column_name is empty, no double column exists, or a double column length differs from the index length.

Returns:

Frame with long and short double columns aligned to the positions index.

std::vector<transaction_liquidity> fincore::get_low_liquidity_transactions(std::span<const trade_record> transactions, const dataframe &prices, const dataframe &volumes, std::optional<std::size_t> last_n_days = std::nullopt)

Returns the worst bar consumption per symbol for recent trades.

Trades are summed by exact (symbol, date) using absolute amounts. When last_n_days is set, only dates strictly after newest - last_n_days days are scanned, where newest is the latest trade date. For each symbol the date with the largest amount / volume * 100 is retained, and results are sorted by symbol.

Parameters:
  • transactions – Trade records.

  • prices – Price panel whose index defines valid trade dates.

  • volumes – Bar share-volume panel with the same index as prices and a volume column for every scanned symbol.

  • last_n_days – Optional number of trailing days to scan.

Throws:

input_error – If an amount is not finite, a trade date is absent from the price index, or the price and volume indices differ.

Returns:

One record per symbol with a scanned trade; empty when there are no transactions.

std::vector<ticker_liquidity> fincore::get_max_days_to_liquidate_by_ticker(const dataframe &positions, const dataframe &prices, const dataframe &volumes, double max_bar_consumption = 0.2, double capital_base = 1000000.0, std::size_t mean_volume_window = 5U, std::optional<std::size_t> last_n_days = std::nullopt, std::string_view cash_column_name = cash_column)

Returns the maximum liquidation time per position symbol.

Uses days_to_liquidate_positions() with the supplied parameters. When last_n_days is set, only metric rows at or after newest - last_n_days days are considered. Each record reports the maximum days value and the position allocation on that date as a percentage (100 * fraction). Symbols with no valid value keep NaN fields. Results are sorted by symbol.

Parameters:
  • positions – Wide position frame with a double column per asset.

  • prices – Bar price frame with the same index as positions and a double column for every non-cash asset.

  • volumes – Bar share-volume frame with the same index as positions and a double column for every non-cash asset.

  • max_bar_consumption – Maximum fraction of a bar’s dollar volume per day; must be in (0, 1].

  • capital_base – Capital in currency units used to scale positions; must be positive.

  • mean_volume_window – Number of preceding bars in the dollar-volume mean; must be at least 1.

  • last_n_days – Optional number of trailing days to scan.

  • cash_column_name – Cash column excluded from the output.

Throws:

input_error – If days_to_liquidate_positions() rejects a parameter, an index, or a position column.

Returns:

One record per non-cash asset, sorted by symbol.

dataframe fincore::get_max_median_position_concentration(const dataframe &positions, std::string_view cash_column_name = cash_column)

Returns row-wise long and short position concentration measures.

Allocation fractions are computed as in get_percent_alloc() over all double columns. For each row, max_long is the largest positive non-cash allocation, median_long the median positive allocation, median_short the median negative allocation and max_short the most negative allocation. A side without allocations yields NaN.

Parameters:
  • positions – Wide position frame with a required double cash column.

  • cash_column_name – Name of the cash column excluded from the measures.

Throws:

input_error – If the cash name is empty, the cash column is absent or not double, no double column exists, or a double column length differs from the index length.

Returns:

Frame with max_long, median_long, median_short and max_short columns.

dataframe fincore::get_percent_alloc(const dataframe &positions)

Returns each numeric position column as a fraction of its row total.

The output has the same index and one double column per input double column, in input order. NaN cells are excluded from the row total and remain NaN in the output; a zero row total yields NaN. Values are fractions, not percentages.

Parameters:

positions – Wide position frame with at least one double column whose values match the frame index length.

Throws:

input_error – If no double column exists or a double column length differs from the index length.

Returns:

Frame of row-wise position fractions.

dataframe fincore::get_sector_exposures(const dataframe &positions, std::span<const sector_assignment> assignments, std::string_view cash_column_name = cash_column)

Returns raw sector exposures by summing assigned positions.

One column is produced per sector named in assignments, in lexicographic sector order, followed by the cash column copied from positions. Only assignments whose symbol names an existing non-cash double column contribute; unassigned assets are omitted. Values are summed raw positions, not fractions.

Parameters:
  • positions – Wide position frame with a required double cash column.

  • assignments – Symbol-to-sector assignments.

  • cash_column_name – Name of the cash column copied to the output.

Throws:

input_error – If cash_column_name is empty, no double position column exists, or a double column length differs from the index length.

Returns:

Frame indexed like positions with one column per sector plus cash.

top_positions fincore::get_top_long_short_abs(const dataframe &positions, std::size_t top = 10U, std::string_view cash_column_name = cash_column)

Returns the top long, short and absolute positions by symbol.

Each non-cash double column contributes its maximum value to the long list when positive, its minimum value to the short list when negative, and its maximum absolute value to the absolute list. Lists are sorted by value (longs and absolutes descending, shorts ascending) and truncated to top entries. NaN rows are ignored; symbols without a qualifying value are omitted.

Parameters:
  • positions – Wide position frame.

  • top – Maximum number of entries in each returned list.

  • cash_column_name – Name of the cash column to exclude.

Throws:

input_error – If cash_column_name is empty or contains an embedded NUL, no double column exists, or a double column length differs from the index length.

Returns:

Ranked long, short and absolute position lists.

dataframe fincore::get_turnover(const dataframe &positions, const dataframe &transactions, turnover_denominator denominator = turnover_denominator::actual_gross_book, std::string_view cash_column_name = cash_column, std::string_view amount_column_name = transaction_amount_column, std::string_view price_column_name = transaction_price_column)

Returns daily turnover using the selected denominator.

The output index is the sorted union of position days and transaction days, both normalized to UTC midnight. Transaction notional comes from get_txn_vol(), and position days must be strictly increasing and unique. actual_gross_book divides by the average of the previous and current gross book (excluding cash), using half the current gross book on the first row. portfolio_value divides by the row sum of all double position columns, including cash. Non-finite ratios are returned as 0.0, and values are ratios rather than percentages.

Parameters:
  • positions – Wide position frame with at least one double column.

  • transactions – Transaction frame consumed by get_txn_vol().

  • denominator – Denominator convention.

  • cash_column_name – Cash column excluded from actual_gross_book; unused by portfolio_value.

  • amount_column_name – Signed-quantity column in transactions.

  • price_column_name – Execution-price column in transactions.

Throws:

input_error – If the denominator value is unknown, a column name is invalid, the cash column is missing or not double for actual_gross_book, position days are duplicated or out of order, or a required transaction column is invalid.

Returns:

Frame indexed by UTC day with a turnover double column.

dataframe fincore::get_txn_vol(const dataframe &transactions, std::string_view amount_column_name = transaction_amount_column, std::string_view price_column_name = transaction_price_column)

Returns daily transaction notional and share totals.

Rows are grouped by UTC calendar day, normalized to midnight and sorted ascending. txn_volume sums |amount| * price and txn_shares sums |amount|; contributions that evaluate to NaN are skipped, and infinite values are preserved.

Parameters:
  • transactions – Frame with double amount and price columns.

  • amount_column_name – Signed-quantity column.

  • price_column_name – Execution-price column.

Throws:

input_error – If a column name is empty or duplicated, a required column is absent or not double, or its length differs from the frame index length.

Returns:

Frame indexed by UTC day with txn_volume and txn_shares double columns.

dataframe fincore::gross_lev(const dataframe &positions, std::string_view cash_column_name = cash_column)

Returns gross leverage per timestamp.

Gross leverage is the sum of absolute non-cash position values divided by the row total of all double columns, including cash. NaN cells are skipped; a zero denominator or a non-finite ratio yields NaN.

Parameters:
  • positions – Wide position frame with a required double cash column.

  • cash_column_name – Name of the cash column excluded from the numerator.

Throws:

input_error – If the cash name is empty, the cash column is absent or not double, no double column exists, or a double column length differs from the index length.

Returns:

Frame with a gross_lev double column.

dataframe fincore::groupby_consecutive_transactions(const dataframe &transactions, std::chrono::nanoseconds max_delta = default_round_trip_max_delta, std::string_view symbol_column_name = transaction_symbol_column, std::string_view amount_column_name = transaction_amount_column, std::string_view price_column_name = transaction_price_column)

Groups consecutive same-direction transactions into price/amount blocks.

Rows are ordered by symbol, timestamp and original position, then adjacent rows of the same symbol with amounts both positive or both non-positive are merged while the gap to the previous row does not exceed max_delta. A block’s amount is the sum of its amounts, its price is the amount-weighted average price, and its timestamp is the first row’s timestamp. A negative max_delta disables merging.

Parameters:
  • transactions – Frame with a string symbol column and double amount and price columns.

  • max_delta – Maximum allowed gap between merged transactions.

  • symbol_column_name – String symbol column.

  • amount_column_name – Double signed-quantity column.

  • price_column_name – Double execution-price column.

Throws:

input_error – If a field name is empty or duplicated, a required column is absent or of the wrong type, or a column length differs from the frame index length.

Returns:

Frame indexed by each block’s first timestamp with amount, symbol and price columns under the supplied names.

dataframe fincore::make_transaction_frame(std::span<const portfolio_transaction> transactions)

Builds an owning transaction frame from validated transactions.

Each input is validated with map_transaction(). The frame index holds the transaction dates; columns are the string symbol and the double amount and price columns. Input order and duplicate dates are preserved.

Parameters:

transactions – Transactions to convert.

Throws:

fincore_error – If any transaction fails validation.

Returns:

Frame with symbol, amount and price columns indexed by date.

portfolio_transaction fincore::map_transaction(const portfolio_transaction &transaction)

Validates and returns a copy of a portfolio transaction.

A valid transaction has a non-empty symbol and finite amount and price; other fields are copied unchanged.

Parameters:

transaction – Transaction to validate.

Throws:

fincore_error – If the symbol is empty or the amount or price is not finite.

Returns:

Copy of transaction.

std::vector<position_record> fincore::stack_positions(const dataframe &positions, bool pos_in_dollars = true, std::string_view cash_column_name = cash_column)

Returns positions in long form, one record per row and non-cash column.

Records follow input row order and then input column order. NaN values are retained, and the named cash column is excluded when present. The pos_in_dollars flag is accepted for API compatibility but does not transform values in this implementation.

Parameters:
  • positions – Wide position frame.

  • pos_in_dollars – Retained for API compatibility; values pass through unchanged.

  • cash_column_name – Name of the cash column to exclude.

Throws:

input_error – If cash_column_name is empty or contains an embedded NUL, no double column exists, or a double column length differs from the index length.

Returns:

Stacked position records.

constexpr std::string_view fincore::cash_column = {"cash"}

Default name of the cash column in wide position frames.

constexpr std::chrono::nanoseconds fincore::default_round_trip_max_delta{std::chrono::hours{8}}

Default maximum gap (8 hours) between transactions merged into one block.

constexpr std::string_view fincore::round_trip_close_datetime_column = {"close_dt"}

Round-trip closing timestamp column.

constexpr std::string_view fincore::round_trip_duration_column = {"duration"}

Round-trip elapsed-nanoseconds column, stored as a timestamp.

constexpr std::string_view fincore::round_trip_long_column = {"long"}

Round-trip long/short flag column.

constexpr std::string_view fincore::round_trip_open_datetime_column = {"open_dt"}

Round-trip opening timestamp column.

constexpr std::string_view fincore::round_trip_pnl_column = {"pnl"}

Round-trip profit/loss column.

constexpr std::string_view fincore::round_trip_return_column = {"rt_returns"}

Round-trip return column.

constexpr std::string_view fincore::transaction_amount_column = {"amount"}

Default signed-quantity column in transaction frames.

constexpr std::string_view fincore::transaction_bar_volume_column = {"volume"}

Default bar share-volume column used by apply_slippage_penalty().

constexpr std::string_view fincore::transaction_price_column = {"price"}

Default execution-price column in transaction frames.

constexpr std::string_view fincore::transaction_symbol_column = {"symbol"}

Default symbol column name in transaction and round-trip frames.