Changeset 9577c79 for docs/P3-UseCaseModel/UseCase0005.md
- Timestamp:
- 09/16/26 23:37:15 (13 days ago)
- Branches:
- main
- Children:
- 8b447ef
- Parents:
- df05838
- File:
-
- 1 edited
-
docs/P3-UseCaseModel/UseCase0005.md (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
docs/P3-UseCaseModel/UseCase0005.md
rdf05838 r9577c79 6 6 7 7 A 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 11 The crypto being sold is **reserved** (`holdings.reserved_quantity`) before it 12 is actually removed from the position, so the check a second sell order makes 13 is always against what is truly still free (`quantity - reserved_quantity`), 14 not against the raw `quantity`, which would also count crypto already 15 promised to this order. Because only market orders are implemented, an order 16 settles in the same database transaction it is placed in, so reserve and 17 settle below are two statements inside one commit rather than two separate 18 ones — the existing all-or-nothing guarantee (see 19 [PrototypeImplementation](../P4-Prototype/PrototypeImplementation.md)) is 20 kept. They stay logically distinct so that a future limit-order matcher — 21 where an order really would sit `open` for a while before a *later* 22 transaction settles it — needs only a second transaction where today there is 23 one, not a schema change. 8 24 9 25 ## Scenario … … 18 34 BEGIN; 19 35 36 -- (a) record intent — no trade has happened yet. 20 37 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) 22 39 VALUES 23 ($user_id, $market_id, 'sell', 'market', ' executed', $qty, $price, now())40 ($user_id, $market_id, 'sell', 'market', 'open', $qty, $price) 24 41 RETURNING id; -- $order_id 25 42 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 27 45 FROM project.holdings 28 46 WHERE user_id = $user_id AND crypto_id = $crypto_id 29 47 FOR UPDATE; 30 -- abort if row missing or quantity < $qty 48 -- available := quantity - reserved_quantity 49 -- abort if row missing or available < $qty 31 50 ``` 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 52 6. If the check passes, system reserves the crypto, then — since this is a market order — settles it immediately, all inside the same transaction: 33 53 34 54 ```sql 55 -- (c) reserve: committed to this order, not yet removed from the position. 35 56 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() 38 66 WHERE user_id = $user_id AND crypto_id = $crypto_id; 39 67 … … 54 82 ($market_id, now(), $price, $qty, 'sell', 'user'); 55 83 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 56 89 COMMIT; 57 90 ``` 91 58 92 7. System confirms: `Order executed: sell 0.5000 ETH @ 3520.000000 (notional 1760.0000 USD)`. 59 93 60 94 ### Alternate flow 5a — insufficient holding 61 95 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." 96 If the holding row is missing, or `quantity - reserved_quantity < $qty`, the 97 entire transaction rolls back — including the `open` order from step 5, which 98 was 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 103 Alice 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 111 If 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, 113 then sees the reduced `quantity` and correctly reports insufficient holding — 114 proven under real concurrency in 115 [UseCase0005Implementation](../P4-Prototype/UseCase0005Implementation.md). 63 116 64 117 ### Realised P/L (post-scenario)
Note:
See TracChangeset
for help on using the changeset viewer.
