| [ef1c1c7] | 1 | # Prototype Implementation
|
|---|
| 2 |
|
|---|
| 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.
|
|---|
| 8 |
|
|---|
| 9 | ## Implemented use-cases
|
|---|
| 10 |
|
|---|
| 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
|
|---|
| 18 |
|
|---|
| 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/).
|
|---|
| 22 |
|
|---|
| 23 | - How to build, configure, run and test the prototype: [BuildInstructions](BuildInstructions.md)
|
|---|
| 24 | - AI usage for this phase: [PrototypeImplementationAIUsage](PrototypeImplementationAIUsage.md)
|
|---|
| 25 |
|
|---|
| 26 | ## Technology and architecture
|
|---|
| 27 |
|
|---|
| 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.
|
|---|
| 37 |
|
|---|
| 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 | — |
|
|---|
| 50 |
|
|---|
| 51 | ## No identifiers to remember
|
|---|
| 52 |
|
|---|
| 53 | The user never has to type or remember an id, a code or a symbol:
|
|---|
| 54 |
|
|---|
| 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.
|
|---|
| 66 |
|
|---|
| 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.
|
|---|
| 69 |
|
|---|
| 70 | ## What the prototype demonstrates about the database design
|
|---|
| 71 |
|
|---|
| 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 |
|
|---|
| 94 | ## Known limitations
|
|---|
| 95 |
|
|---|
| 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).
|
|---|