Changeset ef1c1c7 for docs/P4-Prototype/PrototypeImplementation.md
- Timestamp:
- 09/24/26 17:43:19 (5 days ago)
- Branches:
- main
- Children:
- 0cee8ec
- Parents:
- a531b45
- File:
-
- 1 edited
-
docs/P4-Prototype/PrototypeImplementation.md (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
docs/P4-Prototype/PrototypeImplementation.md
ra531b45 ref1c1c7 1 = Prototype Implementation = 1 # Prototype Implementation 2 2 3 The prototype is a Go command-line application in 4 [https://github.com/StefanTrsunov/bp/tree/main/server server/] that works against the `project` 5 schema in PostgreSQL. It implements all seven use cases from 6 [https://github.com/StefanTrsunov/bp/blob/main/docs/P3-UseCaseModel/UseCaseModel.md UseCaseModel] 7 – the rubric requires at least three – with every database access shown as real, executed SQL. 8 An auxiliary program in [https://github.com/StefanTrsunov/bp/tree/main/bots bots/] simulates a 9 live market so prices move while the prototype is running. 3 EduBerza's P4 prototype is a Go command-line program (in [`server/`](../../server/)). It works 4 against the `project` schema in PostgreSQL. It implements all seven use cases from 5 [UseCaseModel](../P3-UseCaseModel/UseCaseModel.md); the course asks for at least three. Every 6 database access is real SQL that was executed and tested. A second program, the market bot in 7 [`bots/`](../../bots/), simulates a live market so prices move while the prototype runs. 10 8 11 Build, configure, run and test instructions: 12 [https://github.com/StefanTrsunov/bp/blob/main/docs/P4-Prototype/BuildInstructions.md BuildInstructions]. 9 ## Implemented use-cases 13 10 14 All pages listed below, together with the screenshots of each run, are kept in the project's 15 GitHub repository, [https://github.com/StefanTrsunov/bp StefanTrsunov/bp], under 16 `docs/P4-Prototype/`. 11 - [UseCase0001Implementation](UseCase0001Implementation.md) — Register new account 12 - [UseCase0002Implementation](UseCase0002Implementation.md) — Log in 13 - [UseCase0003Implementation](UseCase0003Implementation.md) — Deposit virtual funds 14 - [UseCase0004Implementation](UseCase0004Implementation.md) — Place market BUY order 15 - [UseCase0005Implementation](UseCase0005Implementation.md) — Place market SELL order 16 - [UseCase0006Implementation](UseCase0006Implementation.md) — View portfolio and transaction history 17 - [UseCase0007Implementation](UseCase0007Implementation.md) — Manage watchlist 17 18 18 == Implemented use-cases == 19 Each page follows its P3 use case step by step. It adds the exact SQL the Go code runs in that 20 step and a screenshot of the step from a real run against the database. Screenshots are in 21 [`screenshots/`](screenshots/). 19 22 20 ||=Page=||=Use-case=||=Source=|| 21 ||[https://github.com/StefanTrsunov/bp/blob/main/docs/P4-Prototype/UseCase0001Implementation.md UseCase0001Implementation]||Register a new account||[https://github.com/StefanTrsunov/bp/blob/main/server/auth.go server/auth.go]|| 22 ||[https://github.com/StefanTrsunov/bp/blob/main/docs/P4-Prototype/UseCase0002Implementation.md UseCase0002Implementation]||Log in||[https://github.com/StefanTrsunov/bp/blob/main/server/auth.go server/auth.go]|| 23 ||[https://github.com/StefanTrsunov/bp/blob/main/docs/P4-Prototype/UseCase0003Implementation.md UseCase0003Implementation]||Deposit virtual funds||[https://github.com/StefanTrsunov/bp/blob/main/server/account.go server/account.go]|| 24 ||[https://github.com/StefanTrsunov/bp/blob/main/docs/P4-Prototype/UseCase0004Implementation.md UseCase0004Implementation]||Place market BUY order||[https://github.com/StefanTrsunov/bp/blob/main/server/trade.go server/trade.go]|| 25 ||[https://github.com/StefanTrsunov/bp/blob/main/docs/P4-Prototype/UseCase0005Implementation.md UseCase0005Implementation]||Place market SELL order||[https://github.com/StefanTrsunov/bp/blob/main/server/trade.go server/trade.go]|| 26 ||[https://github.com/StefanTrsunov/bp/blob/main/docs/P4-Prototype/UseCase0006Implementation.md UseCase0006Implementation]||View portfolio and history||[https://github.com/StefanTrsunov/bp/blob/main/server/portfolio.go server/portfolio.go]|| 27 ||[https://github.com/StefanTrsunov/bp/blob/main/docs/P4-Prototype/UseCase0007Implementation.md UseCase0007Implementation]||Manage watchlist||[https://github.com/StefanTrsunov/bp/blob/main/server/watchlist.go server/watchlist.go]|| 23 - How to build, configure, run and test the prototype: [BuildInstructions](BuildInstructions.md) 24 - AI usage for this phase: [PrototypeImplementationAIUsage](PrototypeImplementationAIUsage.md) 28 25 29 Each page mirrors its P3 use-case page and adds the actual SQL emitted by the Go code plus a 30 screenshot of the corresponding run against the live database. The screenshots are committed 31 alongside the pages, in 32 [https://github.com/StefanTrsunov/bp/tree/main/docs/P4-Prototype/screenshots docs/P4-Prototype/screenshots/]. 26 ## Technology and architecture 33 27 34 == What the prototype demonstrates about the database design == 28 - **Language:** Go (module `bp_project`, `go 1.25` in [`go.mod`](../../go.mod)). The only 29 third-party library is the PostgreSQL driver `github.com/lib/pq`. 30 - **Database:** PostgreSQL. Every table, view and function is in the `project` schema. The DDL 31 is [`schema_creation.sql`](../../server/db/schema_creation.sql) and the sample data is 32 [`data_load.sql`](../../server/db/data_load.sql). Both scripts are compiled into the binary 33 and run by `./eduberza -init`. 34 - **Interface:** plain text menus on standard input and output. There are no web server, 35 frameworks, images or styles. 36 - **Structure:** one source file per area of the application. 35 37 36 * '''The current price is never stored as a column.''' It is always the price of the most recent 37 row in `market_trades`, read through the `v_latest_prices` view. Both the user's own fills and 38 the bot's simulated trades feed the same table, so there is exactly one definition of "the 39 price". 40 * '''Money movements are transactional.''' Buying touches five tables – `orders`, `users`, 41 `holdings`, `transactions`, `market_trades` – inside one transaction. A failed balance check 42 rolls the whole thing back: after a rejected purchase there is no order row, no ledger entry 43 and no holding. This is verified in the failure-path tests in 44 [https://github.com/StefanTrsunov/bp/blob/main/docs/P4-Prototype/BuildInstructions.md BuildInstructions]. 45 * '''Constraints do real work.''' `UNIQUE (user_id, crypto_id)` on `holdings` is what makes the 46 `INSERT … ON CONFLICT DO UPDATE` upsert possible, so the weighted-average entry price is 47 recomputed by the database in one statement instead of by a read-modify-write in application 48 code. `CHECK (reserved_quantity >= 0 AND reserved_quantity <= quantity)` is the same idea 49 applied to the sell path: an inconsistent reservation is impossible at the database level, not 50 just something `trade.go` is careful about. 51 * '''Selling reserves before it removes.''' A sell order locks the holding row, reserves the 52 quantity being sold, then settles by removing it — see 53 [UseCase0005Implementation](UseCase0005Implementation.md). Two sell orders placed at the same 54 instant for more than the available quantity are serialised correctly by `SELECT ... FOR 55 UPDATE`, not just by luck of everything happening in one CLI process; this is demonstrated 56 there with two concurrent processes. 57 * '''No identifiers are ever typed.''' Markets are listed with their prices before any choice is 58 made, and everything else is selected by symbol. 38 | File | Responsibility | Use cases | 39 |------|----------------|-----------| 40 | [`server/main.go`](../../server/main.go) | Flags `-init` / `-load-data`, then starts the menu loop | — | 41 | [`server/cli.go`](../../server/cli.go) | The two menus (before and after login), input reading | all | 42 | [`server/db/db.go`](../../server/db/db.go) | Connection from `.env` / environment variables, embedded SQL scripts | — | 43 | [`server/auth.go`](../../server/auth.go) | Register, log in (SHA-256 password hash) | UC0001, UC0002 | 44 | [`server/account.go`](../../server/account.go) | Balance, deposit, transaction history | UC0003, UC0006 | 45 | [`server/market.go`](../../server/market.go) | Market list, choosing a market or a holding by number, latest price | UC0004, UC0005 | 46 | [`server/trade.go`](../../server/trade.go) | Market buy and sell orders, each in one transaction | UC0004, UC0005 | 47 | [`server/portfolio.go`](../../server/portfolio.go) | Portfolio with current value and unrealised P/L | UC0006 | 48 | [`server/watchlist.go`](../../server/watchlist.go) | List, add and remove watchlist items | UC0007 | 49 | [`bots/main.go`](../../bots/main.go) | Market bot: random-walk price ticks into `market_trades`, 1-minute candles | — | 59 50 60 == Known limitations == 51 ## No identifiers to remember 61 52 62 Deliberately out of scope for a first prototype, and the natural content of the later phases:53 The user never has to type or remember an id, a code or a symbol: 63 54 64 * Only `market` orders execute. `limit` is accepted by the schema (`orders.type`) but the 65 matching logic is not implemented. 66 * Passwords are SHA-256 without a salt. Adequate to demonstrate that the password itself is 67 never stored; not adequate for real use. A proper password hash belongs in P9 (security). 68 * Money is handled as `float64` in Go while the database columns are `numeric`. All arithmetic 69 that must be exact – the weighted average – is done in SQL for that reason, but the Go side 70 would need a decimal type for real use. 71 * There is no connection pooling configuration and no explicit isolation level; both are P8 72 topics. 73 * Reservation only ever lives inside one transaction, because only market orders (which settle 74 immediately) exist. A real limit-order matcher would leave `holdings.reserved_quantity` set 75 and `orders.status = 'open'` between two separate commits, and would need a way to cancel an 76 order to release the reservation — neither is implemented, since nothing in the prototype 77 produces an order that stays open. 55 - Every menu is numbered, and the user answers with the number of an option. 56 - **Buying:** all active markets are listed with their latest price, numbered `1…n`. The user 57 enters the market's number at `Market #:` (`ChooseMarket` in `market.go`). 58 - **Selling:** only the cryptos the user actually holds are listed, each with the quantity held 59 and the quantity still free to sell. The user enters the holding's number at `Holding #:` 60 (`ChooseHolding`). A user who holds nothing free to sell gets 61 `you hold no crypto that is free to sell` and is never asked to choose. 62 - **Watchlist:** *Add* lists the cryptos that are not on the watchlist yet. *Remove* lists the 63 ones that are on it. Both are numbered, and the user enters the number. 64 - A number outside the list is refused with `Invalid choice, enter a number from 1 to N.` and 65 nothing is changed. 78 66 79 == AI usage == 67 The only things the user types are their own data: username, e-mail, full name, password, the 68 amount to deposit, the quantity to buy or sell, and the date range of the two P6 reports. 80 69 81 AI was used in this phase and is logged in full, per the course rule for P1 onward. 70 ## What the prototype demonstrates about the database design 82 71 83 * '''Phase log:''' 84 [https://github.com/StefanTrsunov/bp/blob/main/docs/P4-Prototype/PrototypeImplementationAIUsage.md PrototypeImplementationAIUsage.md] 85 – service used, the bugs found and fixed, the test evidence, and what I decided myself. 86 * '''Full conversation transcript:''' 87 [https://github.com/StefanTrsunov/bp/blob/main/docs/P1-ConceptualModel/ERModelAIUsage.md ERModelAIUsage.md] 88 – the same conversation produced the P1–P4 artefacts, so the complete prompt/response log is 89 kept in one place. Direct links: 90 [https://github.com/StefanTrsunov/bp/blob/main/docs/P1-ConceptualModel/ERModelAIUsage.md#session-1--2026-04-21 Session 1 – 2026-04-21], 91 [https://github.com/StefanTrsunov/bp/blob/main/docs/P1-ConceptualModel/ERModelAIUsage.md#session-2--2026-08-06--2026-08-07 Session 2 – 2026-08-06/07], 92 [https://github.com/StefanTrsunov/bp/blob/main/docs/P1-ConceptualModel/ERModelAIUsage.md#session-3--2026-09-16 Session 3 – 2026-09-16]. 72 - **The current price is never stored as a column.** It is always the price of the most recent 73 row in `market_trades`, read through the `v_latest_prices` view. The user's own fills and the 74 bot's simulated trades go into the same table, so there is only one definition of "the 75 price". 76 - **Money movements are transactional.** A buy touches five tables (`orders`, `users`, 77 `holdings`, `transactions`, `market_trades`) inside one transaction. If the balance check 78 fails, the whole transaction is rolled back: after a rejected purchase there is no order row, 79 no ledger entry and no holding. The failure-path tests in 80 [BuildInstructions](BuildInstructions.md) check this. 81 - **Constraints do real work.** `UNIQUE (user_id, crypto_id)` on `holdings` is what makes the 82 `INSERT … ON CONFLICT DO UPDATE` upsert possible, so the database recomputes the 83 weighted-average entry price in one statement, instead of the application reading, changing 84 and writing the row. `CHECK (reserved_quantity >= 0 AND reserved_quantity <= quantity)` does 85 the same for the sell path: the database itself makes an inconsistent reservation 86 impossible, and it does not rely only on `trade.go` being careful. 87 - **Selling reserves before it removes.** A sell order locks the holding row with 88 `SELECT … FOR UPDATE`, reserves the quantity being sold, then settles by removing it (see 89 [UseCase0005Implementation](UseCase0005Implementation.md)). Two sell orders for more than the 90 free quantity, placed at the same moment from two separate processes, are serialised by the 91 row lock. Exactly one of them succeeds. This was tested with two concurrent processes in 92 session 3 (see [PrototypeImplementationAIUsage](PrototypeImplementationAIUsage.md)). 93 93 94 '''Service:''' Claude Code (Anthropic), https://claude.com/claude-code – Claude subscription, 95 model Claude Opus 4.7 (1M context) in sessions 1–2, Claude Sonnet 5 in session 3. 94 ## Known limitations 96 95 97 '''In short:''' session 1 rewrote the existing Chi/HTTP backend as the CLI prototype covering 98 UC0001–UC0007 and added the market bot. Session 2 was a review pass I asked for, which found and 99 fixed three bugs – a path-resolution bug that made the documented build instructions fail, an 100 infinite loop at end of input, and an error check in the wrong order that misreported database 101 failures as "Insufficient holding" – and replaced the read-modify-write holding update with a 102 single `INSERT … ON CONFLICT DO UPDATE`. Session 3 added `holdings.reserved_quantity` and changed 103 `trade.go`'s sell path to reserve crypto before removing it, closing a gap where two sell orders 104 could be granted the same units; see 105 [PrototypeImplementationAIUsage](PrototypeImplementationAIUsage.md#session-3--2026-09-16). 96 These were left out on purpose for a first prototype. They belong to the later phases: 97 98 - Only `market` orders execute. The schema accepts `limit` (`orders.type`), but there is no 99 matching logic for it. 100 - Passwords are hashed with SHA-256 and no salt. That shows the password itself is never 101 stored, but it is not good enough for real use. A proper password hash belongs in P9 102 (security). 103 - Money is `float64` in Go, while the database columns are `numeric`. For that reason, all 104 arithmetic that must be exact (the weighted average) is done in SQL. Real use would need a 105 decimal type on the Go side too. 106 - The prototype sets no connection pool and no explicit isolation level. Both are P8 topics. 107 - A reservation only exists inside one transaction, because the prototype only has market 108 orders, and they settle immediately. A real limit-order matcher would leave 109 `holdings.reserved_quantity` set and `orders.status = 'open'` between two separate commits. 110 It would also need a way to cancel an order and release the reservation. Neither is 111 implemented, because nothing in the prototype creates an order that stays open. 112 113 ## History of changes 114 115 The code started as my own Go backend: HTTP handlers, a draft schema, and a `db.go` that 116 recreated the tables on every start. It changed as follows. The AI's share of each change is 117 logged in [PrototypeImplementationAIUsage](PrototypeImplementationAIUsage.md). 118 119 | Date | Change | Origin | 120 |------|--------|--------| 121 | 2026-04-21 | My HTTP backend rewritten as the CLI prototype covering UC0001–UC0007. The schema errors in my draft were corrected. The market bot was added. | My code and decisions (CLI instead of web, drop the frontend, keep a simulator); rewrite by AI (session 1) | 122 | 2026-08-06/07 | Three bugs fixed: path resolution of `.env` and the SQL scripts, an endless loop at end of input, and an error check in the wrong order on the sell path. The holding update became one `INSERT … ON CONFLICT DO UPDATE`. | I asked for a code review; fixes by AI (session 2) | 123 | 2026-09-16 | `holdings.reserved_quantity` added. The sell path now reserves, then settles. Orders go from `open` to `executed`. | The edge case was mine; implementation by AI (session 3) | 124 | 2026-09-24 | Every choice is picked from a numbered list: markets by number, a sell lists only the user's holdings, the watchlist lists the cryptos. All screenshots were retaken, one per step. | I asked for a check against the P4 rules; implementation by AI (session 4) | 125 126 **Service:** Claude Code (Anthropic), Claude subscription. Session 1 used Claude Opus 4.7 (1M 127 context), session 2 Claude Opus 5 (1M context), session 3 Claude Sonnet 5, and session 4 128 Claude Opus 5.5 (1M context).
Note:
See TracChangeset
for help on using the changeset viewer.
