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/P4-Prototype/PrototypeImplementationAIUsage.md

    rdf05838 r9577c79  
    3939## Summary of AI involvement
    4040
    41 | | Session 1 — 2026-04-21 | Session 2 — 2026-08-06/07 |
    42 |---|---|---|
    43 | **What I brought** | My existing Go backend (Chi HTTP handlers) and a half-finished frontend | The CLI prototype as it stood after session 1 |
    44 | **What the AI did** | Rewrote the backend as a CLI covering UC0001–UC0007, wrote the market bot | Reviewed the code, found and fixed three bugs, improved the holding upsert |
    45 | **What I decided** | To delete the frontend, to build a CLI rather than a web app, to keep the market simulator | To ask for a code review pass rather than documentation alone |
     41| | Session 1 — 2026-04-21 | Session 2 — 2026-08-06/07 | Session 3 — 2026-09-16 |
     42|---|---|---|---|
     43| **What I brought** | My existing Go backend (Chi HTTP handlers) and a half-finished frontend | The CLI prototype as it stood after session 1 | A design review: the sell path had no way to reserve crypto committed to an order |
     44| **What the AI did** | Rewrote the backend as a CLI covering UC0001–UC0007, wrote the market bot | Reviewed the code, found and fixed three bugs, improved the holding upsert | Added `holdings.reserved_quantity`, changed the sell path to reserve-then-settle, gave `Orders.status` a real lifecycle, tested it live including under real concurrency |
     45| **What I decided** | To delete the frontend, to build a CLI rather than a web app, to keep the market simulator | To ask for a code review pass rather than documentation alone | To keep reserve+settle in one transaction rather than split it across two, since there is no cancel-order use case to recover a stuck reservation |
    4646
    4747The prototype was built in session 1 and worked. What session 2 added was a
    … …  
    116116> presentation. You will be asked how the buy transaction works, and the answer
    117117> has to be yours.
     118
     119### Session 3 — 2026-09-16
     120
     121Prompted by a design review I did myself, logged in full in
     122[ERModelAIUsage](../P1-ConceptualModel/ERModelAIUsage.md#session-3--2026-09-16).
     123The report: a user who owns 2 BTC and places a sell order for 0.5 BTC has that
     124crypto immediately removed from `quantity`, but nothing in the model recorded
     125that a *pending* order had already committed part of a position before it
     126settled — `holdings` had `quantity` and `avg_price` only, no equivalent of the
     127`available_balance`/`invested_balance` split already used for cash.
     128
     129**Bug fixed**
     130
     131`server/trade.go`'s sell path checked `held < qty` directly against
     132`holdings.quantity`. This happened to be safe against concurrent double-sells
     133only because the whole operation — order, holding check, holding update,
     134balance update, ledger, trade — runs inside one transaction with a
     135`SELECT ... FOR UPDATE` lock. It was not safe against the actual scenario
     136described: nothing distinguished "owned" from "owned, but already promised to
     137this order," which matters the moment an order can legitimately sit `open`
     138across more than one transaction — exactly what a limit-order matcher would
     139need, and what `orders.status` already implied was coming.
     140
     141**What changed**
     142
     1431. `holdings.reserved_quantity numeric(20,4) NOT NULL DEFAULT 0 CHECK
     144   (reserved_quantity >= 0 AND reserved_quantity <= quantity)` added to
     145   `schema_creation.sql`.
     1462. `v_portfolio` gained `reserved_quantity` and derived `available_quantity`.
     1473. `trade.go`'s sell path now: locks the holding, computes
     148   `available := quantity - reserved_quantity`, rejects if `available < qty`
     149   (previously: rejects if `quantity < qty`), reserves
     150   (`reserved_quantity += qty`), then settles (`quantity -= qty;
     151   reserved_quantity -= qty`) — two statements instead of one, kept inside the
     152   same transaction rather than split into two commits, which would risk an
     153   order stuck `open` with reserved crypto and no cancel command to free it.
     1544. Both `PlaceOrder` branches now insert the order as `status='open'` and
     155   `UPDATE ... SET status='executed', executed_at=now()` at the end, instead
     156   of inserting `'executed'` directly — `Orders.status` is now a real
     157   lifecycle rather than a label written once.
     1585. `portfolio.go` gained `Reserved`/`Available` columns, reading
     159   `v_portfolio.reserved_quantity`/`available_quantity`, so the new field is
     160   something a Trader can actually see.
     1616. The error message on the sell path changed from
     162   `"Insufficient holding: trying to sell X, hold Y"` to
     163   `"Insufficient holding: trying to sell X, available Y (of Z held, W
     164   reserved)"`, since "how much you hold" is no longer the only number that
     165   matters.
     166
     167**Test evidence**
     168
     169Verified against PostgreSQL 16 (`bp_database`, `localhost:5433`):
     170
     171```
     172$ eduberza sell 0.5 BTC  (Alice: 2.0000 BTC held, 0.0000 reserved)
     173Order executed: sell 0.5000 BTC @ 67140.000000 (notional 33570.0000 USD)
     174# holdings.quantity: 2.0000 -> 1.5000, reserved_quantity: 0.0000 (unchanged net of reserve+release)
     175
     176$ eduberza sell 1 ETH   (Bob: no holdings row at all)
     177Insufficient holding: trying to sell 1.0000, available 0.0000 (of 0.0000 held, 0.0000 reserved)
     178
     179# Two concurrent processes, Alice at 1.5 BTC / 0 reserved, each selling 1.0 BTC:
     180=== process A === Insufficient holding: trying to sell 1.0000, available 0.5000 (of 0.5000 held, 0.0000 reserved)
     181=== process B === Order executed: sell 1.0000 BTC @ 67140.000000 (notional 67140.0000 USD)
     182# final holding: quantity 0.5000, reserved_quantity 0.0000 — exactly one sell went through
     183
     184# Reserve visible mid-transaction, in one psql session (BEGIN; ...; COMMIT;):
     185before:            quantity 2.0000, reserved_quantity 0.0000, available 2.0000
     186after reserve:     quantity 2.0000, reserved_quantity 0.5000, available 1.5000
     187after settle:      quantity 1.5000, reserved_quantity 0.0000, available 1.5000
     188
     189# The CHECK constraint holds even without going through trade.go:
     190UPDATE holdings SET reserved_quantity = quantity + 1 WHERE ...;
     191ERROR:  new row for relation "holdings" violates check constraint "holdings_check"
     192```
     193
     194Full transcripts are on
     195[UseCase0005Implementation](UseCase0005Implementation.md). The rejected-order
     196rollback guarantee from session 2 was re-checked too: after both the
     197insufficient-funds and insufficient-holding failure paths, the affected user
     198still has zero new rows in `orders`.
     199
     200**What I decided:** to keep reserve and settle inside a single transaction —
     201splitting them into two, so an order genuinely sits `open` and reserved
     202between two commits, is what a real limit-order matcher will eventually need,
     203but building that now would add a way for an order to get stuck without also
     204building a way to cancel it, which is out of scope for this review. `go build
     205./...` was run after every change; all seven use cases were re-exercised
     206manually.
Note: See TracChangeset for help on using the changeset viewer.