| 1 | # Build Instructions
|
|---|
| 2 |
|
|---|
| 3 | How to compile, configure, run and test the EduBerza prototype.
|
|---|
| 4 | Linked from [PrototypeImplementation](PrototypeImplementation.md).
|
|---|
| 5 |
|
|---|
| 6 | ## Development environment description
|
|---|
| 7 |
|
|---|
| 8 | | Tool | Version tested | Needed for |
|
|---|
| 9 | |-------------|-----------------------|---------------------------------------------------------------|
|
|---|
| 10 | | Go | 1.26 (1.25+ works) | Building `server/` (the CLI) and `bots/` (the market bot). |
|
|---|
| 11 | | PostgreSQL | 16 | The database. Docker image or the faculty server. |
|
|---|
| 12 | | Docker | any recent | Optional — brings up a local PostgreSQL in one command. |
|
|---|
| 13 | | `psql` | any | Optional — running the SQL scripts by hand. |
|
|---|
| 14 | | Java | 21 (8+ works) | Optional — only to open/edit the ER diagram in TerraER. |
|
|---|
| 15 | | DBeaver | any recent | Optional — only to export `relational_schema.jpg`. |
|
|---|
| 16 |
|
|---|
| 17 | Nothing else has to be installed. The only third-party Go dependency
|
|---|
| 18 | (`github.com/lib/pq`, the PostgreSQL driver) is fetched automatically by
|
|---|
| 19 | `go build` from `go.mod`/`go.sum`.
|
|---|
| 20 |
|
|---|
| 21 | ## Build instructions
|
|---|
| 22 |
|
|---|
| 23 | All commands run from the repository root.
|
|---|
| 24 |
|
|---|
| 25 | ### 1. Configure the database connection
|
|---|
| 26 |
|
|---|
| 27 | ```sh
|
|---|
| 28 | cp .env.example .env
|
|---|
| 29 | ```
|
|---|
| 30 |
|
|---|
| 31 | The defaults in `.env.example` match the bundled Docker setup. To use the
|
|---|
| 32 | faculty database instead, either edit `.env` or pass the values as real
|
|---|
| 33 | environment variables — those take precedence over the file:
|
|---|
| 34 |
|
|---|
| 35 | ```sh
|
|---|
| 36 | DBHOST=... DBPORT=5432 DBUSER=... DBPASSWORD=... DBNAME=... ./eduberza
|
|---|
| 37 | ```
|
|---|
| 38 |
|
|---|
| 39 | `.env` is deliberately not committed (see `.gitignore`) because it holds a
|
|---|
| 40 | password.
|
|---|
| 41 |
|
|---|
| 42 | ### 2. Start PostgreSQL
|
|---|
| 43 |
|
|---|
| 44 | ```sh
|
|---|
| 45 | docker compose up -d
|
|---|
| 46 | ```
|
|---|
| 47 |
|
|---|
| 48 | Skip this step if you are pointing at the faculty database.
|
|---|
| 49 |
|
|---|
| 50 | ### 3. Build
|
|---|
| 51 |
|
|---|
| 52 | ```sh
|
|---|
| 53 | go build -o eduberza ./server
|
|---|
| 54 | ```
|
|---|
| 55 |
|
|---|
| 56 | ### 4. Create the schema and load the sample data
|
|---|
| 57 |
|
|---|
| 58 | ```sh
|
|---|
| 59 | ./eduberza -init
|
|---|
| 60 | ```
|
|---|
| 61 |
|
|---|
| 62 | This runs `server/db/schema_creation.sql` and then `server/db/data_load.sql`.
|
|---|
| 63 | Both are **compiled into the binary** (`go:embed`), so `-init` works regardless
|
|---|
| 64 | of which directory you launch it from. It is destructive and idempotent — it
|
|---|
| 65 | drops and recreates the whole `project` schema, so it is also the reset button
|
|---|
| 66 | if a demo goes wrong. To reload only the data, keeping the schema:
|
|---|
| 67 |
|
|---|
| 68 | ```sh
|
|---|
| 69 | ./eduberza -load-data
|
|---|
| 70 | ```
|
|---|
| 71 |
|
|---|
| 72 | The equivalent with `psql`, if you prefer to watch the statements run:
|
|---|
| 73 |
|
|---|
| 74 | ```sh
|
|---|
| 75 | psql "postgresql://$DBUSER:$DBPASSWORD@$DBHOST:$DBPORT/$DBNAME" \
|
|---|
| 76 | -f server/db/schema_creation.sql
|
|---|
| 77 | psql "postgresql://$DBUSER:$DBPASSWORD@$DBHOST:$DBPORT/$DBNAME" \
|
|---|
| 78 | -f server/db/data_load.sql
|
|---|
| 79 | ```
|
|---|
| 80 |
|
|---|
| 81 | ### 5. Run the prototype
|
|---|
| 82 |
|
|---|
| 83 | ```sh
|
|---|
| 84 | ./eduberza
|
|---|
| 85 | ```
|
|---|
| 86 |
|
|---|
| 87 | Seed accounts — all with the password `test123`:
|
|---|
| 88 |
|
|---|
| 89 | | Username | Starting state |
|
|---|
| 90 | |-----------|-------------------------------------------------------|
|
|---|
| 91 | | `alice` | 8250.00 USD cash, holds 0.5 ETH — best demo account |
|
|---|
| 92 | | `bob` | 5000.00 USD cash, no positions |
|
|---|
| 93 | | `charlie` | 2500.00 USD cash, no positions |
|
|---|
| 94 |
|
|---|
| 95 | ### 6. Optional — run the market simulation bot
|
|---|
| 96 |
|
|---|
| 97 | In a second terminal:
|
|---|
| 98 |
|
|---|
| 99 | ```sh
|
|---|
| 100 | go run ./bots
|
|---|
| 101 | ```
|
|---|
| 102 |
|
|---|
| 103 | The bot walks the price of every active market, inserts a row into
|
|---|
| 104 | `market_trades` on each tick and upserts the current 1-minute candle. Prices in
|
|---|
| 105 | the CLI change while it runs, because the current price is always read from the
|
|---|
| 106 | most recent trade (`v_latest_prices`), never from a stored column.
|
|---|
| 107 |
|
|---|
| 108 | ### 7. Optional — richer data for the P6 reports
|
|---|
| 109 |
|
|---|
| 110 | `data_load.sql` only seeds a few minutes of trade history, which is not enough
|
|---|
| 111 | for the [top traders](../P6-AdvancedReports/AdvancedReports.md#top-traders-by-realized-performance)
|
|---|
| 112 | or [market performance](../P6-AdvancedReports/AdvancedReports.md#market-performance-leaderboard)
|
|---|
| 113 | reports (menu `[10]`/`[11]`) to show more than a single period. To see them do
|
|---|
| 114 | something more interesting, load five quarters of synthetic history on top:
|
|---|
| 115 |
|
|---|
| 116 | ```sh
|
|---|
| 117 | psql "postgresql://$DBUSER:$DBPASSWORD@$DBHOST:$DBPORT/$DBNAME" \
|
|---|
| 118 | -f server/db/reports_demo_data.sql
|
|---|
| 119 | ```
|
|---|
| 120 |
|
|---|
| 121 | It is deliberately not part of `-init`/`-load-data` — see the header of
|
|---|
| 122 | [`reports_demo_data.sql`](../../server/db/reports_demo_data.sql) for why — so
|
|---|
| 123 | running it never changes the balances the smoke test below checks.
|
|---|
| 124 |
|
|---|
| 125 | ## Testing instructions
|
|---|
| 126 |
|
|---|
| 127 | ### Mini-guide to the application
|
|---|
| 128 |
|
|---|
| 129 | The CLI has two menus. Before logging in: **Register**, **Login**,
|
|---|
| 130 | **Browse markets**. After logging in: **View balance**, **Deposit virtual
|
|---|
| 131 | funds**, **Browse markets**, **Place market BUY order**, **Place market SELL
|
|---|
| 132 | order**, **View portfolio**, **View transaction history**, **Manage watchlist**,
|
|---|
| 133 | **Logout**, and two [P6](../P6-AdvancedReports/AdvancedReports.md) reports:
|
|---|
| 134 | **Report: top traders** and **Report: market performance**.
|
|---|
| 135 |
|
|---|
| 136 | You never have to remember an identifier. Markets are always printed as a
|
|---|
| 137 | numbered list with their current price before you are asked which one you want,
|
|---|
| 138 | and assets are referred to by symbol (`BTC`, `ETH`, …), never by database id.
|
|---|
| 139 |
|
|---|
| 140 | ### End-to-end smoke test
|
|---|
| 141 |
|
|---|
| 142 | Verified on 2026-09-16 against PostgreSQL 16 with freshly loaded sample data.
|
|---|
| 143 | Expected values are exact.
|
|---|
| 144 |
|
|---|
| 145 | 1. `./eduberza -init` — prints `Database initialised.`
|
|---|
| 146 | 2. `./eduberza`, then `[2] Login` → `alice` / `test123` → `Login successful.`
|
|---|
| 147 | 3. `[6] View portfolio` → one row: `ETH 0.5000` reserved 0.0000, available
|
|---|
| 148 | 0.5000, at avg 3500.000000, current 3520.000000, value 1760.0000,
|
|---|
| 149 | unrealised P/L `+10.0000`. Cash available 8250.0000, net worth 10010.0000.
|
|---|
| 150 | 4. `[4] Place market BUY order` → `BTC` → `0.01` →
|
|---|
| 151 | `Order executed: buy 0.0100 BTC @ 67140.000000 (notional 671.4000 USD)`.
|
|---|
| 152 | 5. `[6] View portfolio` → now BTC *and* ETH, total value 2431.4000, cash
|
|---|
| 153 | 7578.6000 (= 8250.00 − 671.40), net worth still 10010.0000.
|
|---|
| 154 | 6. `[5] Place market SELL order` → `ETH` → `0.5` →
|
|---|
| 155 | `Order executed: sell 0.5000 ETH @ 3520.000000 (notional 1760.0000 USD)`.
|
|---|
| 156 | 7. `[7] View transaction history` → deposit, buy, buy, sell, newest first.
|
|---|
| 157 | 8. `[8] Manage watchlist` → `[1] List items` → alice's `Favorites` contains
|
|---|
| 158 | BTC, ETH, SOL with live prices.
|
|---|
| 159 | 9. `[9] Logout`, then `[0] Exit`.
|
|---|
| 160 |
|
|---|
| 161 | ### Testing the failure paths
|
|---|
| 162 |
|
|---|
| 163 | These matter more than the happy path, because they are what proves the
|
|---|
| 164 | transactions actually roll back:
|
|---|
| 165 |
|
|---|
| 166 | - **Insufficient funds:** log in as `charlie` (2500 USD) and try to buy `1` BTC.
|
|---|
| 167 | Expect `Insufficient funds: need 67140.0000, have 2500.0000` and *no* change
|
|---|
| 168 | to any table — no order row, no ledger entry, no holding.
|
|---|
| 169 | - **Insufficient holding:** as `bob` (no positions), try to sell `1` ETH.
|
|---|
| 170 | Expect `Insufficient holding: trying to sell 1.0000, available 0.0000 (of
|
|---|
| 171 | 0.0000 held, 0.0000 reserved)`.
|
|---|
| 172 | - **Two sell orders racing for the same crypto:** give `alice` a 2 BTC holding
|
|---|
| 173 | and start two `eduberza` processes at once, each selling `1.5` BTC (together
|
|---|
| 174 | 3 BTC, more than she has). Expect exactly one `Order executed`, and the
|
|---|
| 175 | other `Insufficient holding` reading the post-commit quantity — see
|
|---|
| 176 | [UseCase0005Implementation](UseCase0005Implementation.md) for the exact
|
|---|
| 177 | transcript. This is the concurrency guarantee that
|
|---|
| 178 | `holdings.reserved_quantity` and the `SELECT ... FOR UPDATE` lock together
|
|---|
| 179 | provide.
|
|---|
| 180 | - **Duplicate registration:** register with username `alice`. Expect
|
|---|
| 181 | `Username or email already taken.`
|
|---|
| 182 | - **Wrong password:** log in as `alice` with any wrong password. Expect
|
|---|
| 183 | `Invalid credentials.` — and note the same message for an unknown username, so
|
|---|
| 184 | the prototype does not leak which accounts exist.
|
|---|
| 185 |
|
|---|
| 186 | ### For the public presentation
|
|---|
| 187 |
|
|---|
| 188 | Demo with `alice` (already has a position, so the portfolio screen is not
|
|---|
| 189 | empty), and register a brand-new account live to show UC0001. Run the bot in a
|
|---|
| 190 | background terminal so the prices visibly move between two portfolio refreshes.
|
|---|
| 191 |
|
|---|
| 192 | ## Editing the ER diagram
|
|---|
| 193 |
|
|---|
| 194 | TerraER is a third-party tool and is deliberately **not** committed to this
|
|---|
| 195 | repository. Download the teacher's build from
|
|---|
| 196 | <https://bazi.finki.ukim.mk/resources/Software/> and run it:
|
|---|
| 197 |
|
|---|
| 198 | ```sh
|
|---|
| 199 | java -jar TerraER3.11.jar # then File → Open → docs/ERModel_v01.xml
|
|---|
| 200 | ```
|
|---|
| 201 |
|
|---|
| 202 | Save new versions as `ERModel_v02.xml`, `ERModel_v03.xml`, … and export a
|
|---|
| 203 | matching PNG for each. TerraER does not add the extension itself — type
|
|---|
| 204 | `.xml` explicitly or the file will not reopen.
|
|---|
| 205 |
|
|---|
| 206 | ## Up-to-date source code
|
|---|
| 207 |
|
|---|
| 208 | The repository is pushed to the FINKI DEVELOP git server; see the Repositories
|
|---|
| 209 | section in EPRMS for the clone URL and credentials.
|
|---|
| 210 |
|
|---|
| 211 | ### About the source code
|
|---|
| 212 |
|
|---|
| 213 | - All source needed to run the prototype is in this repository: the CLI
|
|---|
| 214 | (`server/`), the market bot (`bots/`), the DDL script and the sample-data
|
|---|
| 215 | script (`server/db/`).
|
|---|
| 216 | - Third-party Go libraries are **not** vendored — `go build` downloads
|
|---|
| 217 | `github.com/lib/pq` using the pinned versions in `go.mod` and `go.sum`.
|
|---|
| 218 | - Third-party executables are **not** committed. `.gitignore` excludes `*.jar`;
|
|---|
| 219 | TerraER is downloaded from the URL above.
|
|---|
| 220 | - No third-party images, styles or frameworks are used, and there are no
|
|---|
| 221 | images in the prototype at all — the interface is text.
|
|---|