ores.analytics.quant
Table of Contents
2. Summary
Dependency-light quantitative math library. One consumer is the Cross Rates Matrix (CRM): a two-phase engine that builds and validates a driver/derived spanning-tree topology over currency pairs (throwing a readable error at build time if the input admits more than one path between two currencies), then serves continuously-updating derived rates with staleness propagation. Deliberately has no database, messaging, or refdata coupling – everything domain-specific (currency codes, spot days, short-term rates) is supplied by the caller as plain parameters, so the library is consumable standalone and fully unit-testable in isolation.
A second, unrelated area covers stochastic price processes: pluggable
i_stochastic_process implementations (geometric and arithmetic Gaussian
mixture engines, an Ornstein-Uhlenbeck mean-reverting engine), a
process_factory that builds one from a process-type string plus raw
parameters, and process_parameter_validation as the single source of
truth for "are these parameters good?" shared between server-side
construction and any UI that wants a friendly error before submitting.
This code originated in ores.synthetic, which mixed the maths with
synthetic-data-generation orchestration; it moved here so the maths is
reusable (e.g. by a future calibration service) and independently
unit-testable without pulling in a service binary.
A third family answers a different question: when does the next tick arrive.
domain::i_arrival_process has one method, next_gap() in seconds, and three
implementations: periodic_arrival_process (equal gaps), poisson_arrival_process
(exponential gaps) and hawkes_arrival_process (self-exciting gaps). The same
factory builds them with make_arrival_process, and tick_clock turns one into
the stop-responsive loop every producer runs on. The arrival rules live beside
the others in domain::validate_arrival_process_parameters.
3. Inputs
domain::ccy_pair_input– raw currency-pair quotes (base/quote codes, driver flag) fed toservice::topology_builder::build.- Pivot currency code and the list of required "major" currencies.
domain::driver_quote– a single tick on a driver edge, fed toservice::rate_engine::update.domain::staleness_policy– caller-supplied max age for a derived rate to be considered fresh.- Raw process parameters (means, stdevs, weights, initial_price) plus a
process_typestring ("geometric" / "arithmetic" / "ornstein_uhlenbeck"), fed toservice::process_factory::make_processanddomain::validate_process_parameters.
4. Outputs
domain::crm_topology– the immutable, validated spanning tree.domain::topology_build_error– thrown with onetopology_errorper offending pair when the input cannot form a valid tree.domain::derived_rate– a rate (direct or triangulated) with itsrate_status(fresh/stale/unavailable) and the oldest contributing driver's timestamp.domain::i_stochastic_process– the pure interface every price process implements (next()advances one step,current()reads without advancing).domain::process_parameter_validation_result– ok/error-message pair from validating raw process parameters before construction.domain::i_arrival_process– the pure interface an arrival process implements (next_gap()draws the wait to the next arrival, in seconds).domain::validate_arrival_process_parameters– the arrival rules (mode, positive rate, branching ratio below one, positive decay), returning the same ok/error-message pair as the value-process rules.
5. Entry points
service::topology_builder::build(pairs, pivot_code, required_majors)service::rate_engine::update(driver_quote)service::rate_engine::rate(base_code, quote_code)/rates(pairs)service::recenter(source_engine, aggregation_code, policy, now)– point-in-time star-shaping around an aggregation currency for risk, preserving every current rate's value.service::process_factory::make_process(process_type, means, stdevs, weights, initial_price, seed)– builds agaussian_mixture_model_process(geometric/multiplicative),arithmetic_gaussian_mixture_model_process(additive/Bachelier), orornstein_uhlenbeck_process(mean-reverting) depending onprocess_type; unrecognised values fall back to geometric.domain::validate_process_parameters(process_type, means, stdevs, weights, initial_price)
6. Boundary
Quant holds its own types and knows no stored spelling. Its engines take enums
and small value types, such as domain::date_period and math::fx_quote_convention,
so its tests need no other component. The component that owns a record owns its
mapping into quant and out of it: reference data reads its codes in
ores.refdata.core/mapping/quant_codes.hpp and its FX option conventions in
ores.refdata.core/mapping/fx_option_convention_mapping.hpp, and market data maps
its observations into engine inputs and the results back. A code is read once, when
a row is loaded, so no engine loop sees a string.
7. Dependencies
Boost::graph/Boost::boost– topology construction (boost::disjoint_setsfor incremental cycle detection, BFS for parent/path assignment).immer(immer::atom<rate_snapshot>,immer::vector<vertex_state>) – lock-free, structural-sharing immutable snapshots for the runtime rate engine.- Deliberately not linked:
ores.database,ores.service, NATS,ores.refdata,ores.logging(the stochastic-process code carries no logging dependency either – callers that need diagnostics log around the call).
8. Build dependencies
What the shared profile cannot know, declared here rather than hand-edited into the generated build file. Each block is spliced into its generated file after the profile's baseline.
9. See also
- ores.analytics — component group overview.
- ores.analytics.api — protocol types and domain entities.
- ores.analytics.core — business logic, persistence, and NATS handlers.
- ores.analytics.service — NATS service entrypoint.
- CRM graph topology and spanning tree
- Driver and derived rates
- CRM risk: recentering and artefacts
- Story: Extract stochastic process math from ores.synthetic into ores.analytics.quant
- Story: Cross-rates matrix (CRM)
- Task: Model the driver/derived spanning-tree topology
- Task: Implement triangulation/derivation
- Task: Implement risk recentering
find_package(Immer CONFIG REQUIRED)
find_package(magic_enum CONFIG REQUIRED)
find_package(Eigen3 CONFIG REQUIRED)
target_link_libraries(${lib_target_name}
PUBLIC
Boost::boost
Boost::graph
PRIVATE
immer
magic_enum::magic_enum
PUBLIC
# Eigen types appear in public headers (the process params
# structs); on a shared build a PRIVATE link would hide its
# include directories from consumers.
Eigen3::Eigen)
target_link_libraries(${tests_target_name}
PRIVATE
${lib_target_name}
Catch2::Catch2
Threads::Threads)
