ores.trading.bond_instrument

Table of Contents

One row per bond trade, reshaped from the wide legacy table per the bond relational model deliverable (doc/agile/versions/v0/sprint_25/data-oriented-trading-model/design_bond_relational_model.org). The row carries the instrument's identity only: trade_id, the trade that identifies both the trade and its instrument, trade_type_code over the ten bond codes, party_id, and issue_id, the NOT NULL foreign key to the bond issue row that holds every term of the bond. The economics moved to the issue (one row per ISIN, shared by every instrument of it), so an amendment to a term touches the issue row once, not every open instrument (finding B11 of the deliverable). A product change is a cancel and a rebook: the service closes the row's validity and inserts a new instrument row with its fact rows; trade_type_code never changes in place (review answer 2).

The ten codes, in seed order (trading_trade_types_populate.sql): Bond, ForwardBond, BondFuture, BondOption, BondRepo, BondTRS, BondPosition, CallableBond, ConvertibleBond, Ascot. The trade_type_code check is the in-list coverage check over the ten codes below, which is the schema's statement of the closed set; the real foreign key to ores_trading_trade_types_tbl is PR 4's (defect 7). For Bond, ForwardBond, CallableBond, ConvertibleBond and BondPosition the issue is the bond itself; for BondRepo the issue is the collateral the financing runs against; for BondOption, BondFuture, BondTRS and Ascot the issue is the bond the product is written on (review answers 3 and 4).

1. Flags

2. Natural keys

3. Columns

3.1. trade_type_code

Trade type code (soft FK to ores_trading_trade_types_tbl).

One of the ten bond codes; the coverage check below lists all of them. Never amended in place: a product change closes this row and rebooks a new one.

std::string("Bond")

3.2. party_id

Party that owns this instrument.

Set from session variable app.current_party_id.

3.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 instrument carries no identity of its own: this column is the key, and the trade it names gives the instrument its scope.

3.4. trade_activity_id

The activity that wrote this version.

3.5. issue_id

UUID of the bond issue this instrument trades (FK to ores_trading_bond_issues_tbl).

A bond product row states its issue: a row with no issue is not a bond product. The one exception is a bond position on a basket, which holds several bonds and states them as instrument_underlying rows, so it names no single issue. The issue holds the bond's terms; the instrument row reads them through a temporal join on this key, so an amended term reads as of the instrument row's own validity.

ctx.generate_uuid()

3.6. position_identifier

The name a bond position gives its basket, as BondBasketData/Identifier states it.

3.7. notional

The trade's own amount for its bond, which ORE holds on bondData and we keep on the bond instrument.

std::make_optional(ores::utility::decimal::decimal::from_string("1000000").value())

4. Foreign keys

4.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();
    ores::trading::repository::trade_repository().write(party_ctx, anchor);
    v.identity.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.identity.trade_activity_id = activity.id;
}

4.3. issue_id

The issue whose terms this instrument's bond carries.

:cascade_orphan: asks the delete rule to take the issue with the instrument when the instrument was the last row to name it. The issue is shared by security id, so an unconditional delete would take another instrument's terms with it, and no delete at all would leave an orphan.

5. Delete cascade

The rows a bond 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 issue-keyed rows are not here: the issue owns them, and its own cascade closes them when the last instrument of the issue is deleted.

5.1. bond_legs

5.2. bond_leg_amounts

5.3. bond_leg_rates

5.4. bond_leg_amortizations

5.5. instrument_schedules

5.6. instrument_schedule_dates

5.7. instrument_underlyings

5.8. instrument_options

5.9. instrument_option_premiums

5.10. instrument_option_exercise_fees

5.11. instrument_option_payment_dates

5.12. instrument_strikes

5.13. bond_forwards

5.14. bond_futures

5.15. bond_options

5.16. bond_repos

5.17. bond_trs

5.18. ascots

6. SQL

6.1. Flags

6.2. Indexes

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

6.3. Checks

expression
"issue_id" is not null or "trade_type_code" = 'BondPosition'

7. 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 issue_id stays flat in between, the vanilla_swap_instrument shape probed for this entity in wave 1.2. The SQL schema, DB entity and column lists are unaffected. The entity templates consume these annotations; domain and repository profiles regenerate correctly.

7.1. Flags

7.2. Repository

7.3. Domain includes

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

7.4. Conventions

7.5. Table display

column header
identity.trade_id ID
identity.trade_type_code Type
issue_id Issue ID
identity.version Version
audit.recorded_at Recorded At

7.6. Custom repository methods

7.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"

8. See also

Emacs 29.3 (Org mode 9.6.15)