ores.trading.trade_state
Table of Contents
A trade's lifecycle state: where it stands in the trade_status machine
(draft, live, expired or cancelled). It is a component of the trade
anchor, keyed by the trade id, and versions on its own timeline: a
lifecycle event cuts a new version of the state and leaves the booking
alone.
The status is not the caller's to supply. The activity names the event, reference data maps the event to a transition, and the insert trigger takes the transition only from the state it starts from. An activity that maps to no transition versions the state and leaves the status where it was.
The state copies the anchor's party, pinned to the anchor, because row-level security needs the party on every row.
1. Flags
2. Columns
2.1. trade_id
The trade this state belongs to.
ctx.generate_uuid()
2.2. trade_activity_id
The activity that wrote this version.
2.3. status_id
The state the trade occupies in the trade_status machine.
The generated sample leaves it nil: the insert trigger sets it from the activity's transition.
boost::uuids::uuid{}
3. SQL
3.1. Flags
3.2. Status transition
The guard is the wide trade's, keyed by the trade id. A status other than draft also checks the booking: an actual trade cannot go live while its booking names a virtual book.
v_transition record; v_prior_status_id uuid; v_activity_type_code text;
The transition is the one the activity's type names, read through the activity that wrote this version.
{
auto amendment = ores::trading::generators::generate_synthetic_trade_activity(ctx);
amendment.party_id = *party_ctx.party_id();
amendment.activity_type_code = "amendment";
ores::trading::repository::trade_activity_repository().write(crud_ctx, amendment);
v.trade_activity_id = amendment.id;
}
The booking case: the machine has not started, so the transition must be one that starts it.
select activity_type_code into v_activity_type_code from ores_trading_trade_activities_tbl where tenant_id = NEW.tenant_id and id = NEW.trade_activity_id; v_transition := ores_trading_resolve_trade_transition_fn(v_activity_type_code); if not v_transition.has_transition then raise exception 'Activity % cannot book a trade: it names no transition to start the machine.', v_activity_type_code using errcode = '23514'; end if; if v_transition.from_state_id is not null then raise exception 'Activity % cannot book a trade: transition % leaves state %, but a new trade has no state to leave.', v_activity_type_code, v_transition.transition_name, v_transition.from_state_id using errcode = '23514'; end if; NEW.status_id = v_transition.to_state_id; if ores_trading_trade_booked_virtual_fn(NEW.tenant_id, NEW.trade_id) and not ores_trading_status_may_be_virtual_fn(NEW.tenant_id, NEW.trade_id, NEW.status_id) then raise exception 'Invalid activity: %. An actual trade in a virtual book can only be a draft.', v_activity_type_code using errcode = '23514'; end if;
The amendment case. The prior row is locked by the version select, so the status read here cannot race another writer.
select status_id into v_prior_status_id from "ores_trading_trade_states_tbl" where tenant_id = NEW.tenant_id and trade_id = NEW.trade_id and valid_to = ores_utility_infinity_timestamp_fn(); select activity_type_code into v_activity_type_code from ores_trading_trade_activities_tbl where tenant_id = NEW.tenant_id and id = NEW.trade_activity_id; v_transition := ores_trading_resolve_trade_transition_fn(v_activity_type_code); if not v_transition.has_transition then NEW.status_id = v_prior_status_id; else if v_transition.from_state_id is null then raise exception 'Activity % can only book a trade: transition % starts the machine, but this trade is already at %.', v_activity_type_code, v_transition.transition_name, v_prior_status_id using errcode = '23514'; end if; if v_prior_status_id is distinct from v_transition.from_state_id then raise exception 'Activity % is not legal here: transition % must be taken from %, but the trade is at %.', v_activity_type_code, v_transition.transition_name, v_transition.from_state_id, v_prior_status_id using errcode = '23514'; end if; NEW.status_id = v_transition.to_state_id; end if; if ores_trading_trade_booked_virtual_fn(NEW.tenant_id, NEW.trade_id) and not ores_trading_status_may_be_virtual_fn(NEW.tenant_id, NEW.trade_id, NEW.status_id) then raise exception 'Invalid activity: %. An actual trade in a virtual book can only be a draft; book it into a real book first.', v_activity_type_code using errcode = '23514'; end if;
3.3. Indexes
| name | columns | unique | current_only | where_extra |
|---|---|---|---|---|
| status | tenant_id, status_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"
{
const auto party_id = *party_ctx.party_id();
auto anchor = ores::trading::generators::generate_synthetic_trade(ctx);
anchor.party_id = 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. status_id
skip_check because the status is set by the transition, not by the
caller.
5. C++
5.1. Flags
5.2. Repository
5.3. Domain includes
#include <string> #include <boost/uuid/uuid.hpp>
5.4. Conventions
5.5. Table display
| column | header |
|---|---|
| trade_id | Trade Id |
| trade_activity_id | Activity |
| status_id | Status |
| version | Version |
6. See also
- ores.trading — component group overview.
- ores.trading.trade — the anchor this state belongs to.