ores.trading.trade

Table of Contents

The immutable anchor of a trade. It holds only the facts that are fixed for the trade's life: the firm's legal entity (party), the counterparty, the trade type, and the three closed classifications — counterparty scope, booking nature and entry channel. Everything that changes lives in component tables keyed by the trade, each on its own timeline.

A row is written once and never changed. A change of party or counterparty is a cancel and rebook: a new trade, linked to the old one. Because the row never changes, the component tables reference it with database foreign keys instead of trigger checks against temporal rows.

The anchor is built beside the wide trade table, which it replaces in the last task of the story; until then it is named trade. It has no audit tail: the audit record of the act that booked it carries that.

1. Flags

:generate_touch_function:: the trade is a composite parent. A write to any of its parts bumps the trade's own version in the same transaction, so the trade's windows partition time into spans in which its parts are stable, and a read assembles the trade from the timeline with no link table.

The parts are the trade's own anchors (trade_state, trade_booking, trade_identifier, trade_party_role, trade_portfolio, trade_additional_field, structure_member) and the bond rows keyed by the same trade: bond_instrument and the leg rows under it. Each of those declares :bump_parent_version: true on its trade key. See the "Temporal composite entity versioning" architecture doc.

2. Columns

2.1. id

The trade id: the firm's own identifier of the trade.

ctx.generate_uuid()

2.2. party_id

The firm's legal entity that is party to the trade.

ctx.generate_uuid()

2.3. counterparty_id

The party the firm faces. Absent only on an intra-entity trade, which moves risk between two books of one legal entity and faces nobody.

std::nullopt

2.4. trade_type

ORE trade type code (e.g. Swap, FxForward, CapFloor).

std::string("Swap")

2.5. counterparty_scope

Who the firm faces: external, inter_entity or intra_entity.

ores::trading::domain::counterparty_scope::intra_entity

2.6. booking_nature

Whether anything happened to cause the booking: actual, test or hypothetical.

ores::trading::domain::booking_nature::test

2.7. entry_channel

How the trade reached the firm's books: manual, stp, ecn or allocation.

ores::trading::domain::entry_channel::manual

2.8. origin

Which system first wrote the trade down: ux, api, ore_import or synthetic. It is provenance and not economics, so it stays out of the economic digest: the same trade imported twice is one trade.

ores::trading::domain::origin::api

2.9. origin_reference

The reference the origin implies: the ORE trade id for an import, the generation config id for a synthetic trade, and nothing for a person at a screen.

std::string(faker::string::numeric(10))

2.10. external_version

The version the customer's records match. It moves when the economics the trade holds change, and never for an internal reason: a null amend, an operational authorisation or any other change the customer did not agree to leaves it alone.

It is not the version every entity carries. That one is the audit trail and moves on every recorded change; this one is the commercial record. See Trade Versioning.

0

2.11. draft_reason_code

Why a draft is not live, when the trade is a draft: a code from ores.trading.draft_reason. It is null for a trade that went live when it was booked, because a concluded deal waits on nothing.

std::string("structure_incomplete")

2.12. economic_digest

The SHA-256 of the economics the trade holds, folded from its own economic fields and from every component's, in hex. Stored rather than recomputed on read, so a change is one comparison rather than a walk of the whole shape.

A null amend leaves it unchanged, which is what makes the external version rule enforceable at all. A change to a field the customer did not agree to leaves it unchanged too, because the digest covers declared economic fields and nothing else. See Exact Numbers and Economic Change.

std::string("")

3. Foreign keys

3.1. origin

3.2. party_id

The firm's legal entity. A party is temporal, so this stays a trigger check.

3.3. counterparty_id

The counterparty. Temporal, so a trigger check.

3.4. trade_type

The trade type catalogue is system reference data, so the check reads the system tenant.

3.5. counterparty_scope

A database foreign key: the lookup is immutable and has no tenant.

3.6. booking_nature

A database foreign key: the lookup is immutable and has no tenant.

3.7. entry_channel

A database foreign key: the lookup is immutable and has no tenant.

4. SQL

4.1. Flags

4.2. Checks

An external trade faces a counterparty; only an intra-entity trade may have none.

expression
"counterparty_scope" = 'intra_entity' or "counterparty_id" is not null

4.3. Indexes

The two unique keys are what a component pins its copied party and counterparty to: a database pin can only name a key, and the primary key (tenant_id, id) does not carry either column.

name columns unique current_only where_extra
party tenant_id, party_id false false  
counterparty tenant_id, counterparty_id false false  

5. C++

5.1. Flags

5.2. Repository

5.3. Domain includes

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

5.4. Conventions

5.5. Table display

column header
id Trade Id
trade_type Trade Type
counterparty_scope Counterparty Scope
booking_nature Booking Nature
entry_channel Entry Channel

6. See also

Emacs 29.3 (Org mode 9.6.15)