Ignore:
Timestamp:
09/16/26 23:37:15 (13 days ago)
Author:
Stefan <trsunovstefan@…>
Branches:
main
Children:
8b447ef
Parents:
df05838
Message:

add reserved_quantity and modify the phases, add v_03.png and v_03.xml for P1

File:
1 edited

Legend:

Unmodified
Added
Removed
  • docs/P3-UseCaseModel/UseCase0005.md

    rdf05838 r9577c79  
    66
    77A Trader sells part or all of a holding at the current market price. Cost basis is preserved so realised P/L can be reconstructed from the ledger.
     8
     9## Reserve, then settle
     10
     11The crypto being sold is **reserved** (`holdings.reserved_quantity`) before it
     12is actually removed from the position, so the check a second sell order makes
     13is always against what is truly still free (`quantity - reserved_quantity`),
     14not against the raw `quantity`, which would also count crypto already
     15promised to this order. Because only market orders are implemented, an order
     16settles in the same database transaction it is placed in, so reserve and
     17settle below are two statements inside one commit rather than two separate
     18ones — the existing all-or-nothing guarantee (see
     19[PrototypeImplementation](../P4-Prototype/PrototypeImplementation.md)) is
     20kept. They stay logically distinct so that a future limit-order matcher —
     21where an order really would sit `open` for a while before a *later*
     22transaction settles it — needs only a second transaction where today there is
     23one, not a schema change.
    824
    925## Scenario
    … …  
    1834   BEGIN;
    1935
     36   -- (a) record intent — no trade has happened yet.
    2037   INSERT INTO project.orders
    21        (user_id, market_id, side, type, status, quantity, price, executed_at)
     38       (user_id, market_id, side, type, status, quantity, price)
    2239   VALUES
    23        ($user_id, $market_id, 'sell', 'market', 'executed', $qty, $price, now())
     40       ($user_id, $market_id, 'sell', 'market', 'open', $qty, $price)
    2441   RETURNING id;   -- $order_id
    2542
    26    SELECT quantity, avg_price
     43   -- (b) lock the holding and check what is actually free to sell.
     44   SELECT quantity, reserved_quantity, avg_price
    2745     FROM project.holdings
    2846    WHERE user_id = $user_id AND crypto_id = $crypto_id
    2947    FOR UPDATE;
    30    -- abort if row missing or quantity < $qty
     48   -- available := quantity - reserved_quantity
     49   -- abort if row missing or available < $qty
    3150   ```
    32 6. If the holding check passes, system reduces the holding, credits cash and debits invested, and appends a ledger and a market trade:
     51
     526. If the check passes, system reserves the crypto, then — since this is a market order — settles it immediately, all inside the same transaction:
    3353
    3454   ```sql
     55   -- (c) reserve: committed to this order, not yet removed from the position.
    3556   UPDATE project.holdings
    36       SET quantity   = quantity - $qty,
    37           updated_at = now()
     57      SET reserved_quantity = reserved_quantity + $qty,
     58          updated_at        = now()
     59    WHERE user_id = $user_id AND crypto_id = $crypto_id;
     60
     61   -- (d) settle: release the reservation and remove the asset in one step.
     62   UPDATE project.holdings
     63      SET quantity          = quantity - $qty,
     64          reserved_quantity = reserved_quantity - $qty,
     65          updated_at        = now()
    3866    WHERE user_id = $user_id AND crypto_id = $crypto_id;
    3967
    … …  
    5482       ($market_id, now(), $price, $qty, 'sell', 'user');
    5583
     84   -- (e) settle the order itself — it has now actually been filled.
     85   UPDATE project.orders
     86      SET status = 'executed', executed_at = now()
     87    WHERE id = $order_id;
     88
    5689   COMMIT;
    5790   ```
     91
    58927. System confirms: `Order executed: sell 0.5000 ETH @ 3520.000000 (notional 1760.0000 USD)`.
    5993
    6094### Alternate flow 5a — insufficient holding
    6195
    62 If the `SELECT ... FOR UPDATE` returns no row, or the held quantity is smaller than the sell quantity, the entire transaction rolls back and system shows "Insufficient holding: trying to sell X, hold Y."
     96If the holding row is missing, or `quantity - reserved_quantity < $qty`, the
     97entire transaction rolls back — including the `open` order from step 5, which
     98was never committed — and system shows:
     99`"Insufficient holding: trying to sell X, available Y (of Z held, W reserved)."`
     100
     101### Worked example — the case this fixes
     102
     103Alice holds 2 BTC, `reserved_quantity = 0`, and places `sell 0.5 BTC`:
     104
     105| | quantity | reserved_quantity | available |
     106|---|---|---|---|
     107| before | 2.0000 | 0.0000 | 2.0000 |
     108| after step (c) — reserved | 2.0000 | 0.5000 | 1.5000 |
     109| after step (d) — settled | 1.5000 | 0.0000 | 1.5000 |
     110
     111If a second sell for more than 1.5 BTC is placed concurrently, its own
     112`SELECT … FOR UPDATE` in step 5b blocks until the first transaction commits,
     113then sees the reduced `quantity` and correctly reports insufficient holding —
     114proven under real concurrency in
     115[UseCase0005Implementation](../P4-Prototype/UseCase0005Implementation.md).
    63116
    64117### Realised P/L (post-scenario)
Note: See TracChangeset for help on using the changeset viewer.