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

Emacs 29.3 (Org mode 9.6.15)