source: docs/P4-Prototype/UseCase0004Implementation.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: 3.8 KB
Line 
1# Use-case 0004 Implementation — Buy
2
3**Initiating actor:** Trader. **Source file:** `server/trade.go`, function `PlaceOrder(s, "buy")`.
4
5## Scenario (implemented)
6
71. **User** chooses `[4] Place market BUY order`.
82. **System** lists markets with their latest price (same SQL as UC0006 — via the `v_latest_prices` view).
9
10
113. **User** enters `BTC`.
124. **System** resolves the market and fetches the latest price:
13
14 ```sql
15 SELECT m.id, c.id, c.symbol, m.quote_currency
16 FROM markets m
17 JOIN crypto c ON c.id = m.crypto_id
18 WHERE upper(c.symbol) = upper($1) AND m.is_active;
19
20 SELECT price FROM v_latest_prices WHERE market_id = $2;
21 ```
22
235. **User** enters quantity `0.01`.
246. **System** executes a single database transaction — *all or nothing*:
25
26 ```sql
27 BEGIN;
28
29 -- (a) record the order as 'open' — no trade has happened yet
30 INSERT INTO orders
31 (user_id, market_id, side, type, status, quantity, price)
32 VALUES
33 ($1, $2, 'buy', 'market', 'open', $3, $4)
34 RETURNING id;
35
36 -- (b) lock and check the user balance
37 SELECT available_balance FROM users WHERE id = $1 FOR UPDATE;
38
39 -- (c) move cash from available to invested. A buy never reserves crypto
40 -- the way a sell does (see UC0005) — it only ever adds to the
41 -- position, so there is nothing to commit on the holdings side
42 -- before settling.
43 UPDATE users
44 SET available_balance = available_balance - $notional,
45 invested_balance = invested_balance + $notional,
46 updated_at = now()
47 WHERE id = $1;
48
49 -- (d) upsert the holding, recomputing the weighted-average entry price
50 -- in one statement. Every SET expression sees the pre-update row, so
51 -- holdings.quantity below is still the old quantity.
52 -- reserved_quantity is untouched by a buy and defaults to 0.
53 INSERT INTO holdings (user_id, crypto_id, quantity, avg_price, updated_at)
54 VALUES ($1, $c, $3, $4, now())
55 ON CONFLICT (user_id, crypto_id) DO UPDATE
56 SET avg_price = (holdings.quantity * holdings.avg_price
57 + EXCLUDED.quantity * EXCLUDED.avg_price)
58 / (holdings.quantity + EXCLUDED.quantity),
59 quantity = holdings.quantity + EXCLUDED.quantity,
60 updated_at = now();
61
62 -- (e) ledger entry
63 INSERT INTO transactions
64 (user_id, type, amount, currency, related_order, description)
65 VALUES
66 ($1, 'buy', -$notional, 'USD', $orderId, 'Market buy ...');
67
68 -- (f) record the resulting market trade
69 INSERT INTO market_trades
70 (market_id, executed_at, price, quantity, side, source)
71 VALUES
72 ($2, now(), $4, $3, 'buy', 'user');
73
74 -- (g) settle the order itself — it has now actually been filled
75 UPDATE orders SET status = 'executed', executed_at = now() WHERE id = $orderId;
76
77 COMMIT;
78 ```
79
807. **System** prints: `Order executed: buy 0.0100 BTC @ 67140.000000 (notional 671.4000 USD)`.
81
82 ![Market list, then a filled BUY order](screenshots/uc0004_buy.png)
83
84## Verified run (from actual prototype execution)
85
86Re-run 2026-09-16 against PostgreSQL 16 (`bp_database` on `localhost:5433`) with freshly loaded seed data:
87
88- **Before:** alice.available_balance = 8250.00, portfolio = { ETH: 0.5000, reserved 0.0000 }.
89- **Command:** `buy 0.01 BTC`.
90- **After:** alice.available_balance = 7578.60 (= 8250 − 671.40), portfolio = { BTC: 0.0100 @ 67140 (reserved 0.0000), ETH: 0.5000 @ 3500 (reserved 0.0000) }, net worth = 10010.00 USD (the +10 is the ETH unrealised P/L from the price moving from 3500 → 3520). A buy never sets `reserved_quantity`, so it reads 0 on every row here.
91
92## Failure path — insufficient funds
93
94If `available_balance < notional`, the `defer tx.Rollback()` in `server/trade.go` reverts every statement above and the user sees:
95
96```
97Insufficient funds: need X, have Y
98```
Note: See TracBrowser for help on using the repository browser.