ores.trading.credit_instrument

Table of Contents

One row per credit trade, the family's own header. It holds what every product in the family states the same way: the trade it belongs to, the trade type code, the party, the activity that wrote the version, the reference entity, the instrument's start and maturity dates and its description. It holds no product economics.

The header is the family's coverage: the trade_type_code check that the routed-instrument template generates from ores.trading.trade_type_catalogue lists exactly the six codes the importer writes, and the two codes with no importer, CreditDefaultSwapOption and CBO, stay out until a mapper writes them. It is also the family's owner: the delete cascade section below names every table the family writes, so the delete rule closes the legs, the basket and the facts with the header and the family does not outlive it.

The products' own fields stay in their fact tables, keyed by the same trade and joined to this row by the trade id. A fact table carries no party_id and no trade_type_code: the header is the family's party boundary and its routing boundary.

The dates are the family's common dates, the one set of names for the start and the maturity of every credit product. A risk participation agreement's protection start and end are those dates.

1. Flags

2. Columns

2.1. trade_type_code

Trade type code (soft FK to ores_trading_trade_types_tbl).

One of the family's codes; the coverage check the routed-instrument template generates from the trade-type catalogue lists all of them.

std::string("CreditDefaultSwap")

2.2. party_id

Party that owns this instrument. The family's party boundary: the fact and child rows carry no party of their own.

Set from session variable app.current_party_id.

2.3. trade_id

The trade this instrument belongs to, and the instrument's own key.

The trade id identifies both the trade and its instrument, so the header carries no identity of its own: this column is the key, and the trade it names gives the instrument its scope. Every fact and child row of the family names the same trade.

2.4. trade_activity_id

The activity that wrote this version.

2.5. reference_entity_id_type

What kind of identifier reference_entity_id states: the legal entity's LEI, or an identifier the system's own data uses.

A reference is typed rather than guessed, because a credit curve named ACME in an engine document and an LEI are different things and a column that holds either without saying which cannot be joined to anything.

The closed set is LEI and Internal. It is a check on this column rather than a catalogue table, because the two kinds of identifier are a property of how a reference is stated and not a list the tenant curates.

std::string("LEI")

2.6. reference_entity_id

The identifier of the reference entity the risk is on, read with reference_entity_id_type.

Every credit product states one. When the type is LEI the value is the legal entity's LEI and the soft reference below applies; when it is Internal the value is the identifier the source states, such as an engine document's credit curve or issuer id, and nothing is claimed about a legal entity.

Soft FK to the DQ lei_entity dataset when the type is LEI: the entity belongs to that dataset rather than to a trading table, so the reference is recorded rather than copied. A later change tightens it into a real foreign key once a legal entity lives in refdata.

Resolving an internal identifier into an LEI replaces both this value and its type together, so a trade states one identifier and never two.

std::string("ORESLEI0000000000001")

2.7. start_date

The instrument's start date. A risk participation agreement's protection start is this column.

Null when the product states none.

ISO 8601 date string (YYYY-MM-DD).

2.8. maturity_date

The instrument's maturity date. A risk participation agreement's protection end is this column.

Null with start_date, and for the same reason.

2.9. description

Optional free-text description.

3. Foreign keys

3.1. trade_id

The trade that carries this instrument. An instrument is written beside its trade, so a row cannot exist without one.

#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();
    anchor.trade_type = v.identity.trade_type_code;
    ores::trading::repository::trade_repository().write(party_ctx, anchor);
    v.identity.trade_id = anchor.id;
}

3.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.identity.trade_activity_id = activity.id;
}

4. Delete cascade

The rows a credit instrument owns. Each is keyed by the instrument's own trade id, so the delete rule closes them all with one statement per table and the family does not outlive its header.

The legs, the strikes and the schedules are the shared shapes the bond pilot and the rates family built; the credit family writes to them rather than widening them.

4.1. credit_basket_constituents

4.2. swap_legs

4.3. swap_leg_amounts

4.4. swap_leg_rates

4.5. swap_leg_amortizations

4.6. swap_leg_indexings

4.7. instrument_barriers

4.8. instrument_barrier_levels

4.9. instrument_strikes

4.10. instrument_schedules

4.11. instrument_schedule_dates

4.12. instrument_options

4.13. instrument_option_premiums

4.14. instrument_option_exercise_fees

4.15. instrument_option_payment_dates

4.16. instrument_option_exercise_prices

4.17. credit_default_swap_instruments

4.18. index_credit_default_swap_instruments

4.19. index_credit_default_swap_option_instruments

4.20. synthetic_cdo_instruments

4.21. credit_linked_swap_instruments

4.22. risk_participation_agreement_instruments

5. SQL

5.1. Flags

5.2. Checks

expression
"reference_entity_id" <> ''
"reference_entity_id_type" in ('LEI', 'Internal')
"reference_entity_id_type" <> 'LEI' or char_length("reference_entity_id") = 20
"maturity_date" is null or "start_date" is null or "maturity_date" >= "start_date"

5.3. Indexes

name columns unique current_only where_extra
party tenant_id, party_id false true  
trade_type tenant_id, trade_type_code false true  

5.4. Pinned keys

The header copies the anchor's party and trade type, so the insert trigger checks the copies against the anchor's current row and refuses a row that disagrees. The anchor is a versioned table, so a database foreign key cannot name it and the pin is a trigger check, as the trade_id key is. The fact and child tables carry neither column: they reach both through this row (design decision D3), so the header is the only row of the family that can disagree.

The insert trigger stamps party_id from the session, and row-level security hides another party's anchor, so in practice the pin refuses a disagreeing trade_type_code; the party column is held to the anchor as well, so a path that bypasses the stamp cannot drift from it.

The anchor's model states that its party and trade type are fixed for the trade's life and that a change is a cancel and rebook. The anchor's write path does not enforce it, so an amendment that changed the trade type would leave the header unchecked. That gap belongs to the trade model, not to this family, and it is open.

name columns table target_columns error_message
anchor trade_id, party_id, trade_type_code ores_trading_trades_tbl id, party_id, trade_type Invalid trade_id: %. The party and trade type must be the trade's own.

The tenant isolation and the party isolation policies are hand-written in trading_rls_policies_create.sql, as bond_instruments' are: the header carries party_id, so it needs the restrictive party policy beside the tenant policy, and the generated :rls_tenant_isolation: form states the tenant policy alone.

6. C++

The C++ domain class nests the scaffolding columns and the columns marked :group: identity below into an instrument_identity identity; member (see the ores.trading.instrument_identity field-group model), the implicit audit columns fold into an ores::dq::domain::audit_record audit; member (see the ores.dq.audit_record field-group model), and the dates and the description stay flat in between. The SQL schema, DB entity and column lists are unaffected. The entity templates consume these annotations; domain and repository profiles regenerate correctly.

6.1. Flags

6.2. Repository

6.3. Domain includes

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

6.4. Conventions

6.5. Table display

column header
identity.trade_id ID
identity.trade_type_code Type
reference_entity_id_type Reference Type
reference_entity_id Reference
start_date Start Date
maturity_date Maturity Date
description Description
identity.version Version
audit.recorded_at Recorded At

6.6. Custom repository methods

6.7. Write observation

The trade's economic digest observes every write of this component. The observer's specialisation must be visible where the repository writes, so the repository includes it.

#include "ores.trading.core/repository/trade_write_observation.hpp"

7. See also

Emacs 29.3 (Org mode 9.6.15)