ores.trading.credit_basket_constituent

Table of Contents

One row per name a credit basket states, keyed to the trade and the name's ordinal within the document.

The ORE schema states a basket as an unbounded list of Name elements, each carrying an issuer, a credit curve, an optional notional, an optional weight and an optional currency, and a few more that this table does not hold yet. The list order is the document's order and the ordinal preserves it, so the export re-emits the names as the document held them.

An index credit default swap and a synthetic CDO state a basket. A product that states none writes no rows.

The notional is a money value, so the column holds an exact numeric. The weight is a continuous quantity, so the column holds a binary float. The document may state neither, and then the column is null rather than a zero the document never wrote.

The trade row carries the party. The name rows are family-owned and ride the trade's scope.

1. Flags

2. Natural keys

3. Columns

3.1. trade_id

The trade whose credit basket document states this name.

3.2. trade_activity_id

The activity that wrote this version.

3.3. sequence_number

Ordinal of this name within the document's list, counting from one.

The schema declares the list unbounded and a document's order is the order it stated, so the ordinal is what identifies a row and preserves that order.

3.4. issuer_id

The issuer this name stands for.

The ORE document states it in the Name/IssuerId element; the column refuses an empty issuer.

3.5. qualifier

The qualifier of the name, when the document states one. Null otherwise.

3.6. credit_curve_id

The credit curve of the name, from the CreditCurveId element. Null when the document names the curve another way.

3.7. notional

The notional this name carries in the basket, when the document states one.

3.8. weight

The relative weight of this name in the basket, when the document states one. A document that states no weight leaves the column null, and the reverse mapper writes no Weight element back.

3.9. currency

The currency of the name's notional, when the document states one.

4. Foreign keys

4.1. trade_id

The trade this row belongs to. An instrument is keyed by its trade, so a fact or child row names the trade its document states and the insert trigger checks it there.

#include "ores.trading.api/generators/trade_generator.hpp"
#include "ores.trading.core/repository/trade_repository.hpp"
{
    auto anchor = ores::trading::generators::generate_synthetic_trade(ctx);
    anchor.party_id = *party_ctx.party_id();
    ores::trading::repository::trade_repository().write(party_ctx, anchor);
    v.trade_id = anchor.id;
}

4.2. trade_activity_id

The activity that wrote this version. A database foreign key: the activity is immutable.

#include "ores.trading.api/generators/trade_activity_generator.hpp"
#include "ores.trading.core/repository/trade_activity_repository.hpp"
{
    auto activity = ores::trading::generators::generate_synthetic_trade_activity(ctx);
    activity.party_id = *party_ctx.party_id();
    ores::trading::repository::trade_activity_repository().write(party_ctx, activity);
    v.trade_activity_id = activity.id;
}

5. SQL

5.1. Flags

5.2. Checks

expression
"sequence_number" > 0
"issuer_id" <> ''

6. C++

6.1. Flags

6.2. Repository

6.3. Domain includes

#include <optional>
#include <string>
#include <boost/uuid/uuid.hpp>
#include "ores.utility/decimal/decimal.hpp"

6.4. Conventions

6.5. Table display

column header
sequence_number Sequence
issuer_id Issuer
notional Notional
weight Weight
modified_by Modified By
version Version

6.6. Custom repository methods

The trade read path fetches the names of many credit baskets at once when it assembles the export payload, and no generated read covers that: read_latest(context, ids) filters on the child's own key and read_latest_by_trade_id reads one instrument at a time. The batch read below is one statement over the instrument set, which is what the generated stack cannot express.

6.6.1. read_by_instruments_batch

Reads every current name of the given instruments, ordered by instrument and ordinal, in one statement. An empty input reads nothing.

std::vector<domain::credit_basket_constituent>
read_by_instruments_batch(context ctx, const std::vector<std::string>& trade_ids);
std::vector<domain::credit_basket_constituent>
credit_basket_constituent_repository::read_by_instruments_batch(
    context ctx, const std::vector<std::string>& trade_ids) {
    if (trade_ids.empty())
        return {};
    static constexpr auto max = db_timestamp::max();
    const auto tid = ctx.tenant_id().to_string();
    const auto query = query_builder::read<std::vector<credit_basket_constituent_entity>> |
                       query_builder::where("tenant_id"_c == tid && "trade_id"_c.in(trade_ids) &&
                             "valid_to"_c == max) |
                       query_builder::order_by("trade_id"_c, "sequence_number"_c);
    return query_builder::execute_read_query<credit_basket_constituent_entity,
                              domain::credit_basket_constituent>(
        ctx,
        query,
        [](const auto& entities) { return credit_basket_constituent_mapper::map(entities); },
        lg(),
        "Reading credit basket constituents for multiple instruments.");
}

7. See also

Emacs 29.3 (Org mode 9.6.15)