ores.reporting.risk_report_config

Table of Contents

The ORE-level parameters for a risk report definition: base currency, observation model, analytics flags, XVA/VaR/SIMM settings and threading.

Each row is owned by exactly one report_definition (1:1, enforced by the unique index on report_definition_id). Portfolio and book scope live in separate temporal junction tables; an empty set in either junction means "all visible to the tenant".

Analytics flags use integer 0/1, not boolean, to match the project convention. npv and cashflow default to enabled; all others default off.

1. Flags

2. Columns

2.1. id

UUID uniquely identifying this configuration.

2.2. report_definition_id

The report definition this configuration belongs to. Unique per tenant on active records, so a definition has at most one live configuration.

ctx.generate_uuid()

2.3. base_currency

Reporting base currency (ISO 4217 code) that all results are converted to.

std::string(faker::finance::currencyCode())

2.4. observation_model

How the engine handles absent market data: disable, none, move or defer.

std::string("disable")

2.5. n_threads

Number of worker threads the risk engine may use.

faker::number::integer(1, 8)

2.6. market_data_type

Market data convention: live, eod or date.

std::string("eod")

2.7. market_data_date

As-of date (ISO 8601) used when market_data_type is date. An empty string is the not-set sentinel: the mapper writes NULL for it, which is what the CHECK requires while market_data_type stays eod.

std::string("")

2.8. npv_enabled

Enables the NPV analytic.

faker::number::integer(0, 1)

2.9. cashflow_enabled

Enables the cashflow analytic.

faker::number::integer(0, 1)

2.10. curves_enabled

Enables curve analytic output.

faker::number::integer(0, 1)

2.11. sensitivity_enabled

Enables sensitivity analytic output.

faker::number::integer(0, 1)

2.12. simulation_enabled

Enables Monte Carlo simulation output.

faker::number::integer(0, 1)

2.13. xva_enabled

Enables XVA analytics. Gates the xva_* settings below.

faker::number::integer(0, 1)

2.14. stress_enabled

Enables stress test analytics.

faker::number::integer(0, 1)

2.15. parametric_var_enabled

Enables parametric VaR. Gates the var_* settings below.

faker::number::integer(0, 1)

2.16. initial_margin_enabled

Enables initial margin (SIMM) calculation. Gates the simm_* settings.

faker::number::integer(0, 1)

2.17. pfe_enabled

Enables potential future exposure output.

faker::number::integer(0, 1)

2.18. xva_quantile

XVA confidence quantile. NULL means "not set".

faker::number::decimal(0.9, 0.99)

2.19. xva_cva_enabled

Enables the CVA adjustment within XVA.

faker::number::integer(0, 1)

2.20. xva_dva_enabled

Enables the DVA adjustment within XVA.

faker::number::integer(0, 1)

2.21. xva_fva_enabled

Enables the FVA adjustment within XVA.

faker::number::integer(0, 1)

2.22. xva_colva_enabled

Enables the ColVA adjustment within XVA.

faker::number::integer(0, 1)

2.23. xva_dim_enabled

Enables the DIM (dynamic initial margin) calculation within XVA.

faker::number::integer(0, 1)

2.24. xva_dim_quantile

DIM confidence quantile. NULL means "not set".

faker::number::decimal(0.9, 0.99)

2.25. xva_dim_horizon_calendar_days

DIM horizon in calendar days. NULL means "not set".

faker::number::integer(1, 30)

2.26. xva_dim_regression_order

Regression order for the DIM backtest, 1 to 3. NULL means "not set".

faker::number::integer(1, 3)

2.27. var_quantiles

Confidence quantiles for the parametric VaR grid. NULL means "not set".

The column stays in the schema but reaches no C++ layer. sqlgen cannot bind a Postgres array, so no entity member can carry it, and a member the mapper cannot persist is worse than no member at all. The field is currently unwired: nothing reads or writes it, in this model or in the hand-written code this model replaces. Remove :sql_only: and regenerate once sqlgen, or a replacement, can bind numeric[].

2.28. var_method

Parametric VaR method: delta, delta_gamma_normal or monte_carlo. NULL means "not set".

std::string("delta")

2.29. simm_version

SIMM methodology version. NULL means "not set".

std::string("2.6")

2.30. simm_calculation_currency

Currency the SIMM amount is expressed in. NULL means "not set".

std::string(faker::finance::currencyCode())

3. SQL

3.1. Flags

3.2. Checks

Domain constraints beyond the standard temporal, natural-key and nil-uuid checks the template emits on its own.

expression
"base_currency" <> ''
"observation_model" in ('disable', 'none', 'move', 'defer')
"n_threads" >= 1
"market_data_type" in ('live', 'eod', 'date')
(market_data_type = 'date') = (market_data_date is not null)
"npv_enabled" in (0, 1)
"cashflow_enabled" in (0, 1)
"curves_enabled" in (0, 1)
"sensitivity_enabled" in (0, 1)
"simulation_enabled" in (0, 1)
"xva_enabled" in (0, 1)
"stress_enabled" in (0, 1)
"parametric_var_enabled" in (0, 1)
"initial_margin_enabled" in (0, 1)
"pfe_enabled" in (0, 1)
"xva_cva_enabled" in (0, 1)
"xva_dva_enabled" in (0, 1)
"xva_fva_enabled" in (0, 1)
"xva_colva_enabled" in (0, 1)
"xva_dim_enabled" in (0, 1)
"xva_dim_regression_order" is null or "xva_dim_regression_order" between 1 and 3
"var_method" is null or "var_method" in ('delta', 'delta_gamma_normal', 'monte_carlo')

4. Foreign keys

4.1. report_definition_id

5. Insert trigger

5.1. Validations

column validation_function

6. C++

6.1. Flags

6.2. Repository

6.3. Domain includes

#include <boost/uuid/uuid.hpp>
#include <chrono>
#include <optional>
#include <string>

6.4. Entity includes

#include <optional>
#include <ostream>
#include <string>
#include "sqlgen/Timestamp.hpp"
#include <boost/uuid/uuid.hpp>

6.5. Conventions

6.6. Table display

column header
report_definition_id Definition
base_currency Base Currency
observation_model Observation Model
market_data_type Market Data
npv_enabled NPV
cashflow_enabled Cashflow
xva_enabled XVA
parametric_var_enabled Parametric VaR
initial_margin_enabled Initial Margin
modified_by Modified By
version Version

6.7. Paste blocks

6.7.1. Additional class-member declarations

  1. find_by_definition_id

    Reads the current configuration for a report definition. The execution handler needs the config before any CRUD surface exists for it, and the lookup keys on the natural key rather than on get.

    std::optional<domain::risk_report_config>
    find_by_definition_id(context ctx, const std::string& definition_id);
    
  2. resolve_book_ids

    Resolves the full set of book UUIDs in scope by calling ores_reporting_resolve_book_ids_for_config_fn, which checks explicit book scope, then portfolio scope with subtree expansion, then falls back to all tenant books. The fallback chain lives in SQL, so it cannot be templated.

    std::vector<std::string> resolve_book_ids(context ctx, const std::string& config_id);
    
  3. get_book_scope

    Returns the active book UUIDs explicitly scoped to a configuration. An empty vector means "all books within the selected portfolios".

    std::vector<std::string> get_book_scope(context ctx, const std::string& config_id);
    
  4. get_portfolio_scope

    Returns the active portfolio UUIDs explicitly scoped to a configuration. An empty vector means "all portfolios visible to the tenant".

    std::vector<std::string> get_portfolio_scope(context ctx, const std::string& config_id);
    

6.7.2. Additional out-of-class implementations

  1. scope_entities

    Lightweight row types for the two scope junction tables. Only the columns scope resolution needs are declared; the junction tables have no org model of their own.

    Must precede the methods that use them: paste blocks concatenate in sub-heading order.

    namespace {
    
    struct book_scope_entity {
        constexpr static const char* schema = "public";
        constexpr static const char* tablename = "ores_reporting_risk_report_config_books_tbl";
    
        std::string tenant_id;
        std::string risk_report_config_id;
        std::string book_id;
        std::optional<db_timestamp> valid_from = "9999-12-31 23:59:59";
        std::optional<db_timestamp> valid_to = "9999-12-31 23:59:59";
    };
    
    struct portfolio_scope_entity {
        constexpr static const char* schema = "public";
        constexpr static const char* tablename = "ores_reporting_risk_report_config_portfolios_tbl";
    
        std::string tenant_id;
        std::string risk_report_config_id;
        std::string portfolio_id;
        std::optional<db_timestamp> valid_from = "9999-12-31 23:59:59";
        std::optional<db_timestamp> valid_to = "9999-12-31 23:59:59";
    };
    
    }
    
  2. find_by_definition_id
    std::optional<domain::risk_report_config>
    risk_report_config_repository::find_by_definition_id(context ctx,
                                                         const std::string& definition_id) {
    
        BOOST_LOG_SEV(lg(), debug) << "Finding risk_report_config by definition_id: " << definition_id;
    
        static auto max(make_timestamp(MAX_TIMESTAMP, lg()));
        const auto tid = ctx.tenant_id().to_string();
        const auto query = sqlgen::read<std::vector<risk_report_config_entity>> |
                           where("tenant_id"_c == tid && "report_definition_id"_c == definition_id &&
                                 "valid_to"_c == max.value());
    
        auto results = execute_read_query<risk_report_config_entity, domain::risk_report_config>(
            ctx,
            query,
            [](const auto& entities) { return risk_report_config_mapper::map(entities); },
            lg(),
            "Finding risk_report_config by definition_id");
    
        if (results.empty())
            return std::nullopt;
        return results.front();
    }
    
  3. resolve_book_ids
    std::vector<std::string>
    risk_report_config_repository::resolve_book_ids(context ctx, const std::string& config_id) {
    
        BOOST_LOG_SEV(lg(), debug) << "Resolving book IDs for config: " << config_id;
    
        const auto tid = ctx.tenant_id().to_string();
        const std::string sql =
            "SELECT id::text FROM "
            "ores_reporting_resolve_book_ids_for_config_fn($1::uuid, $2::uuid) AS t(id)";
    
        return execute_parameterized_string_query(
            ctx, sql, {tid, config_id}, lg(), "Resolving book IDs for risk_report_config");
    }
    
  4. get_book_scope
    std::vector<std::string>
    risk_report_config_repository::get_book_scope(context ctx, const std::string& config_id) {
    
        BOOST_LOG_SEV(lg(), debug) << "Reading book scope for config: " << config_id;
    
        static auto max(make_timestamp(MAX_TIMESTAMP, lg()));
        const auto tid = ctx.tenant_id().to_string();
        const auto query = sqlgen::read<std::vector<book_scope_entity>> |
                           where("tenant_id"_c == tid && "risk_report_config_id"_c == config_id &&
                                 "valid_to"_c == max.value());
    
        auto rows = execute_read_query<book_scope_entity, book_scope_entity>(
            ctx, query, [](const auto& entities) { return entities; }, lg(), "Reading book scope");
    
        std::vector<std::string> book_ids;
        book_ids.reserve(rows.size());
        for (const auto& row : rows)
            book_ids.push_back(row.book_id);
    
        BOOST_LOG_SEV(lg(), debug) << "Found " << book_ids.size() << " book(s) in scope";
        return book_ids;
    }
    
  5. get_portfolio_scope
    std::vector<std::string>
    risk_report_config_repository::get_portfolio_scope(context ctx, const std::string& config_id) {
    
        BOOST_LOG_SEV(lg(), debug) << "Reading portfolio scope for config: " << config_id;
    
        static auto max(make_timestamp(MAX_TIMESTAMP, lg()));
        const auto tid = ctx.tenant_id().to_string();
        const auto query = sqlgen::read<std::vector<portfolio_scope_entity>> |
                           where("tenant_id"_c == tid && "risk_report_config_id"_c == config_id &&
                                 "valid_to"_c == max.value());
    
        auto rows = execute_read_query<portfolio_scope_entity, portfolio_scope_entity>(
            ctx, query, [](const auto& entities) { return entities; }, lg(), "Reading portfolio scope");
    
        std::vector<std::string> portfolio_ids;
        portfolio_ids.reserve(rows.size());
        for (const auto& row : rows)
            portfolio_ids.push_back(row.portfolio_id);
    
        BOOST_LOG_SEV(lg(), debug) << "Found " << portfolio_ids.size() << " portfolio(s) in scope";
        return portfolio_ids;
    }
    

7. Physical space

The messaging, eventing and history surface is deliberately not generated yet. Nothing writes a risk report config today: the only consumer is report_execution_handler, which reads one through find_by_definition_id and never saves. Generating the surface would add a NATS handler, a sub-registrar, a history provider registrar, an event registrar and a service that no composition point calls.

ores.reporting/core/src/messaging/registrar.cpp is a hand-written fan-out, not a per-entity registrar aggregator, so a generated registrar has nowhere to join. It builds each *_handler directly. Its siblings report_definition, report_instance, report_type and concurrency_policy already carry two orphaned families on main: the four *_registrar classes and the four *_history_provider_registrar classes. Nothing calls either family. The measurement and the repair are their own task; this model does not add a fifth orphan.

The *_event_registrar family is composed, so it is not part of the finding. ores.reporting/service/src/app/application.cpp calls all four register_*_event_mapping functions.

The data layer — domain, generator, entity, mapper, repository — plus the SQL schema is generated.

Address Enabled
ores.cpp.service false
ores.cpp.protocol false
ores.cpp.nats-handler false
ores.cpp.nats-sub-registrar false
ores.cpp.nats-event-registrar false
ores.cpp.nats-eventing false
ores.cpp.nats-event-cache false
ores.cpp.history-provider-registrar false
ores.cpp.presentation false
ores.cpp.eventing-integration-test false

8. See also

Emacs 29.3 (Org mode 9.6.15)