source: docs/P3-UseCaseModel/UseCase0005.md@ 9577c79

main
Last change on this file since 9577c79 was 9577c79, checked in by Stefan <trsunovstefan@…>, 13 days ago

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

  • Property mode set to 100644
File size: 4.8 KB
RevLine 
[d8ce4e2]1# Use-case 0005 — Place market SELL order
2
3**Initiating actor:** Trader
4
5**Other actors:** Market Simulator (indirect — supplies the current price).
6
7A 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
[9577c79]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.
24
[d8ce4e2]25## Scenario
26
271. Trader chooses "Place market SELL order".
282. System lists markets (same SQL as UC0004 step 2).
293. Trader enters market symbol and quantity.
304. System resolves the market and looks up the latest price (same SQL as UC0004 step 4).
315. System opens a transaction:
32
33 ```sql
34 BEGIN;
35
[9577c79]36 -- (a) record intent — no trade has happened yet.
[d8ce4e2]37 INSERT INTO project.orders
[9577c79]38 (user_id, market_id, side, type, status, quantity, price)
[d8ce4e2]39 VALUES
[9577c79]40 ($user_id, $market_id, 'sell', 'market', 'open', $qty, $price)
[d8ce4e2]41 RETURNING id; -- $order_id
42
[9577c79]43 -- (b) lock the holding and check what is actually free to sell.
44 SELECT quantity, reserved_quantity, avg_price
[d8ce4e2]45 FROM project.holdings
46 WHERE user_id = $user_id AND crypto_id = $crypto_id
47 FOR UPDATE;
[9577c79]48 -- available := quantity - reserved_quantity
49 -- abort if row missing or available < $qty
[d8ce4e2]50 ```
[9577c79]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:
[d8ce4e2]53
54 ```sql
[9577c79]55 -- (c) reserve: committed to this order, not yet removed from the position.
[d8ce4e2]56 UPDATE project.holdings
[9577c79]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()
[d8ce4e2]66 WHERE user_id = $user_id AND crypto_id = $crypto_id;
67
68 UPDATE project.users
69 SET available_balance = available_balance + $notional,
70 invested_balance = GREATEST(invested_balance - ($avg_price * $qty), 0),
71 updated_at = now()
72 WHERE id = $user_id;
73
74 INSERT INTO project.transactions
75 (user_id, type, amount, currency, related_order, description)
76 VALUES
77 ($user_id, 'sell', $notional, 'USD', $order_id, 'Market sell ...');
78
79 INSERT INTO project.market_trades
80 (market_id, executed_at, price, quantity, side, source)
81 VALUES
82 ($market_id, now(), $price, $qty, 'sell', 'user');
83
[9577c79]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
[d8ce4e2]89 COMMIT;
90 ```
[9577c79]91
[d8ce4e2]927. System confirms: `Order executed: sell 0.5000 ETH @ 3520.000000 (notional 1760.0000 USD)`.
93
94### Alternate flow 5a — insufficient holding
95
[9577c79]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).
[d8ce4e2]116
117### Realised P/L (post-scenario)
118
119The realised P/L for a sell is `$notional - ($avg_price * $qty)`. It is not persisted explicitly but can be computed from the ledger and the holding at sell time.
Note: See TracBrowser for help on using the repository browser.