ores.trading.trade_booking

Table of Contents

A trade's booking: the book that holds it, the netting set it is filed in, the date it was agreed and the time it was executed. It is a component of the trade anchor, keyed by the trade id, and versions on its own timeline: a book move or a re-papering into another netting set cuts a new version of the booking and leaves the anchor alone.

The booking copies the anchor's party and counterparty. The copies are pinned to the anchor, so they cannot drift, and they are what the other pins and row-level security read: the book must belong to the trade's party, and the netting set to its counterparty and party.

A booking names a virtual book, one inside a sandbox, only while its trade is a pre-agreement draft, a test or a hypothetical. An actual trade going live needs a real book: the booking is written before the state that takes the trade live, in the same transaction.

1. Flags

2. Columns

2.1. trade_id

The trade this booking belongs to.

ctx.generate_uuid()

2.2. trade_activity_id

The activity that wrote this version.

2.3. book_id

The book that holds the trade.

ctx.generate_uuid()

2.4. netting_set_id

The netting set the trade is filed in.

std::nullopt

2.5. counterparty_identifier_id

The identifier the trade's source named its counterparty by.

An ORE document names a counterparty by an alias, and two aliases may name one counterparty, as CP and CPTY do in the ORE samples. The import keeps the identifier it resolved, so an export writes back the name the document used.

std::nullopt

2.6. netting_set_identifier_id

The identifier the trade's source named its netting set by, kept for the same reason as the counterparty's.

std::nullopt

2.7. trade_date

The date the trade was agreed. Absent when the source does not state it, as an ORE document does not.

std::chrono::year_month_day{std::chrono::year{2025}/1/15}

2.8. execution_timestamp

The time the trade was executed.

std::nullopt

3. SQL

3.1. Flags

3.2. Virtual books

A virtual book holds no position of the firm, so an actual trade may sit in one only while it is a draft. The check reads the trade's current state; a booking written before any state is allowed, because the state that follows checks the booking in turn.

if ores_trading_book_is_virtual_fn(NEW.tenant_id, NEW.book_id)
   and not ores_trading_trade_may_be_virtual_fn(NEW.tenant_id, NEW.trade_id) then
    raise exception 'Invalid book_id: %. A virtual book holds only drafts, tests and hypotheticals.',
        NEW.book_id
        using errcode = '23514';
end if;

-- The booking holds no party of its own. The party and the counterparty
-- are the deal's, so the book, the netting set and the counterparty
-- identifier are checked against the trade the booking names rather than
-- against a copy that could drift from it.
if not exists (
    select 1 from ores_refdata_books_tbl b
    join ores_trading_trades_tbl t
      on t.tenant_id = b.tenant_id and t.party_id = b.party_id
    where b.tenant_id = NEW.tenant_id and b.id = NEW.book_id
      and b.valid_to = ores_utility_infinity_timestamp_fn()
      and t.id = NEW.trade_id
      and t.valid_to = ores_utility_infinity_timestamp_fn()
) then
    raise exception 'Invalid book_id: %. The book must belong to the trade''s party.', NEW.book_id
        using errcode = '23514';
end if;

if NEW.netting_set_id is not null and not exists (
    select 1 from ores_refdata_netting_sets_tbl s
    join ores_trading_trades_tbl t
      on t.tenant_id = s.tenant_id
     and t.party_id = s.party_id
     and t.counterparty_id = s.counterparty_id
    where s.tenant_id = NEW.tenant_id and s.id = NEW.netting_set_id
      and s.valid_to = ores_utility_infinity_timestamp_fn()
      and t.id = NEW.trade_id
      and t.valid_to = ores_utility_infinity_timestamp_fn()
) then
    raise exception 'Invalid netting_set_id: %. The set must be the trade''s counterparty''s and party''s.', NEW.netting_set_id
        using errcode = '23514';
end if;

if NEW.counterparty_identifier_id is not null and not exists (
    select 1 from ores_refdata_counterparty_identifiers_tbl ci
    join ores_trading_trades_tbl t
      on t.tenant_id = ci.tenant_id and t.counterparty_id = ci.counterparty_id
    where ci.tenant_id = NEW.tenant_id and ci.id = NEW.counterparty_identifier_id
      and ci.valid_to = ores_utility_infinity_timestamp_fn()
      and t.id = NEW.trade_id
      and t.valid_to = ores_utility_infinity_timestamp_fn()
) then
    raise exception 'Invalid counterparty_identifier_id: %. The identifier must be the trade''s counterparty''s.', NEW.counterparty_identifier_id
        using errcode = '23514';
end if;

if NEW.netting_set_identifier_id is not null and not exists (
    select 1 from ores_refdata_netting_set_identifiers_tbl si
    where si.tenant_id = NEW.tenant_id
      and si.id = NEW.netting_set_identifier_id
      and si.netting_set_id = NEW.netting_set_id
      and si.valid_to = ores_utility_infinity_timestamp_fn()
) then
    raise exception 'Invalid netting_set_identifier_id: %. The identifier must be the netting set''s.', NEW.netting_set_identifier_id
        using errcode = '23514';
end if;

3.3. Indexes

name columns unique current_only where_extra
book tenant_id, book_id false true  

4. Foreign keys

4.1. trade_id

A database foreign key: the anchor is immutable.

#include "ores.trading.api/generators/trade_generator.hpp"
#include "ores.trading.core/repository/trade_repository.hpp"
#include <optional>
{
    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;
}

4.3. book_id

The book. Temporal, so a trigger check. The seed books into a book of the session party, which the book pin requires.

#include "ores.refdata.api/generators/book_generator.hpp"
#include "ores.refdata.api/generators/currency_generator.hpp"
#include "ores.refdata.api/generators/portfolio_generator.hpp"
#include "ores.refdata.core/repository/book_repository.hpp"
#include "ores.refdata.core/repository/currency_repository.hpp"
#include "ores.refdata.core/repository/portfolio_repository.hpp"
{
    namespace gen = ores::refdata::generators;
    namespace repo = ores::refdata::repository;

    auto currency = gen::generate_synthetic_currency(ctx);
    repo::currency_repository().write(party_ctx, {currency});

    auto portfolio = gen::generate_synthetic_portfolio(ctx);
    portfolio.party_id = *party_ctx.party_id();
    portfolio.change_reason_code = "system.test";
    repo::portfolio_repository().write(party_ctx, portfolio);

    auto book = gen::generate_synthetic_book(ctx);
    book.party_id = *party_ctx.party_id();
    book.parent_portfolio_id = portfolio.id;
    book.functional_currency = currency.iso_code;
    book.change_reason_code = "system.test";
    repo::book_repository().write(party_ctx, book);

    v.book_id = book.id;
}

4.4. netting_set_id

The netting set. Temporal, so a trigger check.

4.5. counterparty_identifier_id

The counterparty identifier. Temporal, so a trigger check.

4.6. netting_set_identifier_id

The netting set identifier. Temporal, so a trigger check.

5. C++

5.1. Flags

5.2. Repository

5.3. Domain includes

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

5.4. Conventions

5.5. Table display

column header
trade_id Trade Id
book_id Book
trade_date Trade Date
version Version

6. See also

Emacs 29.3 (Org mode 9.6.15)