| [ef1c1c7] | 1 | # Entity-Relationship Model v.04
|
|---|
| [d8ce4e2] | 2 |
|
|---|
| 3 | ## Diagram
|
|---|
| 4 |
|
|---|
| [ef1c1c7] | 5 | 
|
|---|
| [d8ce4e2] | 6 |
|
|---|
| 7 | Notation: Chen. Rectangles are entity sets, diamonds are relationships, ellipses
|
|---|
| 8 | are attributes, underlined ellipses are primary keys, the dashed ellipse is a
|
|---|
| 9 | derived attribute. A double line between an entity set and a relationship marks
|
|---|
| 10 | **total participation** (every instance of that entity set must participate); a
|
|---|
| 11 | single line marks partial participation.
|
|---|
| 12 |
|
|---|
| 13 | Two deliberate modeling decisions worth stating up front:
|
|---|
| 14 |
|
|---|
| 15 | - **No foreign keys appear in the diagram.** Connections between entity sets are
|
|---|
| 16 | expressed as relationships, per the notation. Foreign-key columns appear only
|
|---|
| 17 | in the relational model in [RelationalDesign](../P2-RelationalDesign/RelationalDesign.md).
|
|---|
| 18 | - **`Holds` and `Contains` are relationships, not entity sets.** Both are M:N and
|
|---|
| 19 | both carry their own attributes, which is exactly what a Chen relationship is
|
|---|
| 20 | for. They become tables (`holdings`, `watchlist_items`) only in P2.
|
|---|
| 21 |
|
|---|
| 22 | ## Data requirements
|
|---|
| 23 |
|
|---|
| [9577c79] | 24 | Each entity set is given as a short rationale for why it exists as its own set,
|
|---|
| 25 | its keys, and its attributes as a table. Each relationship is given as its
|
|---|
| 26 | cardinality and participation, a short rationale, and — where it carries data —
|
|---|
| 27 | an attribute table.
|
|---|
| 28 |
|
|---|
| [d8ce4e2] | 29 | ### Entity sets
|
|---|
| 30 |
|
|---|
| 31 | #### Users
|
|---|
| 32 | Registered participants of the platform. Every action in the simulation is
|
|---|
| 33 | attributed to a user, and the two balance attributes are what makes the
|
|---|
| [9577c79] | 34 | simulation work: cash that is free to trade is tracked separately from cash
|
|---|
| 35 | that is currently committed to open positions, so the platform can refuse a
|
|---|
| 36 | purchase without having to recompute the whole portfolio first.
|
|---|
| 37 |
|
|---|
| 38 | **Keys:** candidates `{id}`, `{username}`, `{email}`; primary key **`id`**. A
|
|---|
| 39 | surrogate UUID was chosen because it is opaque and stable — `username` and
|
|---|
| 40 | `email` are both things a user may legitimately want to change later, and
|
|---|
| 41 | every relationship in the diagram points at `Users`, so a mutable key would
|
|---|
| 42 | propagate changes across the whole database.
|
|---|
| 43 |
|
|---|
| 44 | | Attribute | Type | Constraints |
|
|---|
| 45 | |---|---|---|
|
|---|
| 46 | | `id` | UUID | PK, required |
|
|---|
| 47 | | `username` | text(50) | required, unique |
|
|---|
| 48 | | `email` | text(255) | required, unique, contains `@` |
|
|---|
| 49 | | `full_name` | text(200) | optional |
|
|---|
| 50 | | `password_hash` | text(255) | required — never the password itself; the prototype stores a SHA-256 hex digest |
|
|---|
| 51 | | `available_balance` | numeric(18,4) | required, default 0, ≥ 0 |
|
|---|
| 52 | | `invested_balance` | numeric(18,4) | required, default 0, ≥ 0 |
|
|---|
| [ef1c1c7] | 53 | | `reserved_balance` | numeric(18,4) | required, default 0, ≥ 0 — cash set aside for the user's open buy orders (added in v04, after P7) |
|
|---|
| [9577c79] | 54 | | `created_at` | timestamptz | required, defaults to now |
|
|---|
| 55 | | `updated_at` | timestamptz | optional (null until first change) |
|
|---|
| [d8ce4e2] | 56 |
|
|---|
| 57 | #### Cryptos
|
|---|
| 58 | The catalog of crypto assets the platform knows about. Kept separate from
|
|---|
| 59 | `Markets` because an asset exists independently of the pairs it is traded in —
|
|---|
| 60 | the same asset can be quoted against several currencies, and a user's holding is
|
|---|
| 61 | in the *asset*, not in a particular pair.
|
|---|
| 62 |
|
|---|
| [9577c79] | 63 | **Keys:** candidates `{id}`, `{symbol}`; primary key **`id`**, for the same
|
|---|
| 64 | reason as in `Users`. `symbol` is kept as a unique natural key because that is
|
|---|
| 65 | what users type and see.
|
|---|
| 66 |
|
|---|
| 67 | | Attribute | Type | Constraints |
|
|---|
| 68 | |---|---|---|
|
|---|
| 69 | | `id` | UUID | PK, required |
|
|---|
| 70 | | `symbol` | text(20) | required, unique (e.g. `BTC`) |
|
|---|
| 71 | | `name` | text(255) | required (e.g. `Bitcoin`) |
|
|---|
| 72 | | `created_at` | timestamptz | required, defaults to now |
|
|---|
| [d8ce4e2] | 73 |
|
|---|
| 74 | #### Markets
|
|---|
| 75 | A tradeable pair: one crypto asset quoted in one currency, e.g. BTC/USD. This is
|
|---|
| 76 | where prices live, and it is the thing an order is placed *on*. Modeled as its
|
|---|
| 77 | own entity set rather than an attribute of `Cryptos` because a market has its own
|
|---|
| 78 | lifecycle — it can be deactivated without deleting the asset — and because
|
|---|
| 79 | trades, candles and orders all reference the pair, not the asset.
|
|---|
| 80 |
|
|---|
| [9577c79] | 81 | **Keys:** candidates `{id}`, `{crypto_id, quote_currency}` — that pair is
|
|---|
| 82 | unique by definition, since a given asset can only be quoted once per
|
|---|
| 83 | currency; primary key **`id`**, so that the many entity sets referencing a
|
|---|
| 84 | market carry one narrow column instead of a composite key.
|
|---|
| 85 |
|
|---|
| 86 | | Attribute | Type | Constraints |
|
|---|
| 87 | |---|---|---|
|
|---|
| 88 | | `id` | UUID | PK, required |
|
|---|
| 89 | | `quote_currency` | text(3) | required, default `USD` |
|
|---|
| 90 | | `is_active` | boolean | required, default true — inactive markets are hidden from the trading menus but keep their history |
|
|---|
| 91 | | `created_at` | timestamptz | required, defaults to now |
|
|---|
| [d8ce4e2] | 92 |
|
|---|
| 93 | #### Orders
|
|---|
| 94 | A user's instruction to buy or sell on a market. Needed as a separate entity set
|
|---|
| 95 | because an order is a record of *intent* that outlives its execution: it keeps
|
|---|
| 96 | the requested quantity and price even after it has been filled, which is what
|
|---|
| 97 | makes the ledger auditable.
|
|---|
| 98 |
|
|---|
| [ef1c1c7] | 99 | Placing an order is what triggers a **reservation** of whatever it commits:
|
|---|
| 100 | the crypto being sold (`Holds.reserved_quantity`, below) on a sell, and the
|
|---|
| 101 | cash (`Users.reserved_balance`) on a buy. Since v04 (after P7) an order can
|
|---|
| 102 | wait in the order book and be filled in parts, so `status` is a real
|
|---|
| 103 | lifecycle driven by `filled_quantity`: `open` (nothing filled yet),
|
|---|
| 104 | `partially_filled`, `executed` (completely filled), or `cancelled`, which
|
|---|
| 105 | releases what is still reserved. See
|
|---|
| [9577c79] | 106 | [UseCase0005](../P3-UseCaseModel/UseCase0005.md) for the reserve-then-settle
|
|---|
| [ef1c1c7] | 107 | sequence and
|
|---|
| 108 | [AdvancedDatabaseDevelopment](../P7-AdvancedDatabaseDevelopment/AdvancedDatabaseDevelopment.md)
|
|---|
| 109 | for the rules that keep it consistent.
|
|---|
| [9577c79] | 110 |
|
|---|
| 111 | **Keys:** candidate `{id}` only — there is no natural key, since the same user
|
|---|
| 112 | can place two identical orders on the same market in the same second, and both
|
|---|
| 113 | are legitimately distinct; primary key **`id`**.
|
|---|
| 114 |
|
|---|
| 115 | | Attribute | Type | Constraints |
|
|---|
| 116 | |---|---|---|
|
|---|
| 117 | | `id` | UUID | PK, required |
|
|---|
| 118 | | `side` | text | required, `buy` or `sell` |
|
|---|
| [ef1c1c7] | 119 | | `type` | text | required, `market` or `limit` (both executed since P7) |
|
|---|
| 120 | | `status` | text | required, `open`, `partially_filled`, `executed` or `cancelled` |
|
|---|
| [9577c79] | 121 | | `quantity` | numeric(20,4) | required, > 0 |
|
|---|
| [ef1c1c7] | 122 | | `filled_quantity` | numeric(20,4) | required, default 0, between 0 and `quantity` — how much has been traded; remaining = `quantity − filled_quantity` (added in v04, after P7) |
|
|---|
| 123 | | `price` | numeric(18,6) | the limit price; for a market order, the market price when it was placed |
|
|---|
| [9577c79] | 124 | | `placed_at` | timestamptz | required, defaults to now |
|
|---|
| 125 | | `executed_at` | timestamptz | optional, set when the order settles |
|
|---|
| [d8ce4e2] | 126 |
|
|---|
| 127 | #### Transactions
|
|---|
| 128 | The financial ledger: every movement of virtual cash, in one place. This exists
|
|---|
| 129 | so that a balance is never just a number someone edited — it is the sum of an
|
|---|
| 130 | auditable list of entries, which is also what the "explain every step" goal of
|
|---|
| 131 | the project needs.
|
|---|
| 132 |
|
|---|
| [9577c79] | 133 | **Keys:** candidate `{id}` only; primary key **`id`**.
|
|---|
| 134 |
|
|---|
| 135 | | Attribute | Type | Constraints |
|
|---|
| 136 | |---|---|---|
|
|---|
| 137 | | `id` | UUID | PK, required |
|
|---|
| 138 | | `type` | text | required, `deposit`, `buy`, `sell` or `fee` |
|
|---|
| 139 | | `amount` | numeric(18,4) | required, signed — negative for money leaving the cash balance, positive for money arriving |
|
|---|
| 140 | | `currency` | text(3) | required, default `USD` |
|
|---|
| 141 | | `created_at` | timestamptz | required, defaults to now |
|
|---|
| 142 | | `description` | text | optional, free-form |
|
|---|
| [d8ce4e2] | 143 |
|
|---|
| 144 | #### MarketTrades
|
|---|
| 145 | Individual executed trades on a market, from the user's own fills and from the
|
|---|
| 146 | market simulator. This is the single source of truth for the current price: the
|
|---|
| 147 | price of a market is the price of its most recent trade, never a column someone
|
|---|
| 148 | writes directly.
|
|---|
| 149 |
|
|---|
| [9577c79] | 150 | **Keys:** candidate `{id}` — `{market_id, executed_at}` looks unique in
|
|---|
| 151 | principle, but two trades can share a timestamp, so it is not a safe key;
|
|---|
| 152 | primary key **`id`** (a plain auto-incrementing integer here rather than a
|
|---|
| 153 | UUID, because this is the highest-volume entity set and it is only ever read
|
|---|
| 154 | in timestamp order, never referenced by anything else).
|
|---|
| 155 |
|
|---|
| 156 | | Attribute | Type | Constraints |
|
|---|
| 157 | |---|---|---|
|
|---|
| 158 | | `id` | integer | PK, required, auto-generated |
|
|---|
| 159 | | `executed_at` | timestamptz | required |
|
|---|
| 160 | | `price` | numeric(18,6) | required, > 0 |
|
|---|
| 161 | | `quantity` | numeric(20,6) | required, > 0 |
|
|---|
| 162 | | `side` | text | optional, `buy` or `sell` |
|
|---|
| 163 | | `source` | text(50) | required, default `simulation` — distinguishes a simulated trade from a user's own fill (`user`) |
|
|---|
| [d8ce4e2] | 164 |
|
|---|
| [ef1c1c7] | 165 | Since v04 (after P7) a trade also records which orders it filled, through the
|
|---|
| 166 | relationships `FillsBuy` and `FillsSell` below.
|
|---|
| 167 |
|
|---|
| 168 | #### OrderEvents
|
|---|
| 169 | *Added in v04, after P7.* The audit trail of an order: one event for its
|
|---|
| 170 | placement, one for every (partial) fill, and one for a cancellation. The
|
|---|
| 171 | `Orders` row only holds the current state; this entity keeps the history of
|
|---|
| 172 | how the order got there. Events are recorded automatically by the database.
|
|---|
| 173 |
|
|---|
| 174 | **Keys:** candidate `{id}` only; primary key **`id`** (auto-incrementing
|
|---|
| 175 | integer, events are only read in order).
|
|---|
| 176 |
|
|---|
| 177 | | Attribute | Type | Constraints |
|
|---|
| 178 | |---|---|---|
|
|---|
| 179 | | `id` | integer | PK, required, auto-generated |
|
|---|
| 180 | | `event_type` | text | required, `placed`, `partially_filled`, `filled` or `cancelled` |
|
|---|
| 181 | | `quantity` | numeric(20,4) | required — the ordered quantity for `placed`, the filled amount for a fill, the unfilled rest for `cancelled` |
|
|---|
| 182 | | `price` | numeric(18,6) | optional — the order price, or the trade price for a fill |
|
|---|
| 183 | | `status_after` | text | required, the order's status after the event |
|
|---|
| 184 | | `created_at` | timestamptz | required, defaults to now |
|
|---|
| 185 |
|
|---|
| [d8ce4e2] | 186 | #### MarketCandles
|
|---|
| 187 | OHLCV aggregates per market and timeframe — the data a price chart is drawn
|
|---|
| 188 | from. Stored rather than computed on the fly because the point of the project is
|
|---|
| 189 | a chart-driven interface, and re-aggregating the whole trade history for every
|
|---|
| 190 | screen refresh does not scale.
|
|---|
| 191 |
|
|---|
| [9577c79] | 192 | **Keys:** candidates `{id}`, `{market_id, timeframe, candle_time}` — a market
|
|---|
| 193 | has exactly one candle per timeframe per time bucket; primary key **`id`**, the
|
|---|
| 194 | composite is enforced as a uniqueness rule because it is the real-world
|
|---|
| 195 | constraint and it is what prevents duplicate candles.
|
|---|
| 196 |
|
|---|
| 197 | | Attribute | Type | Constraints |
|
|---|
| 198 | |---|---|---|
|
|---|
| 199 | | `id` | integer | PK, required, auto-generated |
|
|---|
| 200 | | `timeframe` | text | required, `1m`, `5m`, `1h` or `1d` |
|
|---|
| 201 | | `open`, `high`, `low`, `close` | numeric(18,6) | all required |
|
|---|
| 202 | | `volume` | numeric(20,6) | required |
|
|---|
| 203 | | `candle_time` | timestamptz | required — the start of the bucket |
|
|---|
| [d8ce4e2] | 204 |
|
|---|
| 205 | #### Watchlists
|
|---|
| 206 | A named list of assets a user wants to monitor. A separate entity set rather than
|
|---|
| 207 | a flag on the relationship between users and assets, because a user may want
|
|---|
| 208 | several lists ("long term", "watching today") and each needs its own name.
|
|---|
| 209 |
|
|---|
| [9577c79] | 210 | **Keys:** candidate `{id}` — `{user_id, name}` would also work if list names
|
|---|
| 211 | were required to be unique per user, which the model does not impose, so it is
|
|---|
| 212 | not listed as a candidate key; primary key **`id`**.
|
|---|
| 213 |
|
|---|
| 214 | | Attribute | Type | Constraints |
|
|---|
| 215 | |---|---|---|
|
|---|
| 216 | | `id` | UUID | PK, required |
|
|---|
| 217 | | `name` | text(100) | required |
|
|---|
| 218 | | `created_at` | timestamptz | required, defaults to now |
|
|---|
| [d8ce4e2] | 219 |
|
|---|
| 220 | ### Relationships
|
|---|
| 221 |
|
|---|
| 222 | #### QuotedOn — Cryptos (1) : Markets (N), total on Markets
|
|---|
| 223 | Ties a market to the asset it trades. One asset can be quoted in many markets;
|
|---|
| 224 | every market must have exactly one asset, hence total participation on the
|
|---|
| [9577c79] | 225 | `Markets` side. No attributes.
|
|---|
| [d8ce4e2] | 226 |
|
|---|
| 227 | #### PlacedOn — Markets (1) : Orders (N), total on Orders
|
|---|
| 228 | Records which market an order was placed on. Every order must name a market;
|
|---|
| 229 | a market may have no orders yet. No attributes.
|
|---|
| 230 |
|
|---|
| 231 | #### Places — Users (1) : Orders (N), total on Orders
|
|---|
| [9577c79] | 232 | Records who placed an order. Every order belongs to exactly one user; a new
|
|---|
| 233 | user has no orders. No attributes.
|
|---|
| [d8ce4e2] | 234 |
|
|---|
| 235 | #### Records — Users (1) : Transactions (N), total on Transactions
|
|---|
| [9577c79] | 236 | Attributes each ledger entry to a user. Every entry belongs to exactly one
|
|---|
| 237 | user. No attributes.
|
|---|
| [d8ce4e2] | 238 |
|
|---|
| 239 | #### Settles — Orders (1) : Transactions (N), partial on both sides
|
|---|
| [9577c79] | 240 | Links a ledger entry to the order that caused it. Partial on the
|
|---|
| 241 | `Transactions` side because deposits have no originating order, and partial on
|
|---|
| 242 | the `Orders` side because an order that never executes never produces a
|
|---|
| 243 | ledger entry — which is why the corresponding column is nullable in P2. No
|
|---|
| 244 | attributes.
|
|---|
| [d8ce4e2] | 245 |
|
|---|
| 246 | #### Fills — Markets (1) : MarketTrades (N), total on MarketTrades
|
|---|
| 247 | Every executed trade happened on exactly one market. No attributes.
|
|---|
| 248 |
|
|---|
| [ef1c1c7] | 249 | #### FillsBuy — Orders (1) : MarketTrades (N), partial on both sides
|
|---|
| 250 | *Added in v04, after P7.* The buy order a trade filled. An order can be
|
|---|
| 251 | filled by many trades (partial fills); a trade fills at most one buy order,
|
|---|
| 252 | and none when the simulated market was the buyer. No attributes.
|
|---|
| 253 |
|
|---|
| 254 | #### FillsSell — Orders (1) : MarketTrades (N), partial on both sides
|
|---|
| 255 | *Added in v04, after P7.* The sell order a trade filled, symmetric to
|
|---|
| 256 | `FillsBuy`. A trade between two users' orders participates in both. No
|
|---|
| 257 | attributes.
|
|---|
| 258 |
|
|---|
| 259 | #### Logs — Orders (1) : OrderEvents (N), total on OrderEvents
|
|---|
| 260 | *Added in v04, after P7.* Every event belongs to exactly one order. No
|
|---|
| 261 | attributes.
|
|---|
| 262 |
|
|---|
| [d8ce4e2] | 263 | #### Aggregates — Markets (1) : MarketCandles (N), total on MarketCandles
|
|---|
| 264 | Every candle summarises trades of exactly one market. No attributes.
|
|---|
| 265 |
|
|---|
| 266 | #### Owns — Users (1) : Watchlists (N), total on Watchlists
|
|---|
| 267 | Every watchlist belongs to exactly one user. No attributes.
|
|---|
| 268 |
|
|---|
| 269 | #### Holds — Users (M) : Cryptos (N), partial on both sides, **with attributes**
|
|---|
| 270 | A user's position in an asset. M:N because one user holds many assets and one
|
|---|
| 271 | asset is held by many users, and partial on both sides because a user may hold
|
|---|
| 272 | nothing and an asset may be held by nobody. Modeled as a relationship rather
|
|---|
| 273 | than an entity set because a position has no identity of its own — it is
|
|---|
| 274 | entirely described by *which user*, *which asset*, and how much.
|
|---|
| 275 |
|
|---|
| [9577c79] | 276 | `reserved_quantity` mirrors `available_balance`/`invested_balance` on `Users`:
|
|---|
| 277 | two independently updated stored numbers, with the amount actually free to use
|
|---|
| 278 | computed on demand rather than stored (`quantity − reserved_quantity` here,
|
|---|
| 279 | `available_balance` alone on the cash side). Without it, nothing stopped a
|
|---|
| 280 | user from placing a second sell order against crypto already promised to a
|
|---|
| 281 | first one — `quantity` alone cannot tell "owned" apart from "owned, but
|
|---|
| 282 | already committed elsewhere." See [history](#entity-relationship-model-history), v03.
|
|---|
| 283 |
|
|---|
| 284 | | Attribute | Type | Constraints |
|
|---|
| 285 | |---|---|---|
|
|---|
| 286 | | `quantity` | numeric(20,4) | required, ≥ 0 — total amount owned |
|
|---|
| 287 | | `reserved_quantity` | numeric(20,4) | required, default 0, `0 ≤ reserved_quantity ≤ quantity` — committed to the user's own open sell orders, not yet removed from the position |
|
|---|
| 288 | | `avg_price` | numeric(18,6) | required, ≥ 0, **derived** (dashed ellipse) — the weighted average of the prices at which the position was accumulated; derivable from the buy history, stored anyway so unrealised P/L can be shown without replaying the whole ledger |
|
|---|
| 289 | | `created_at` | timestamptz | required, defaults to now |
|
|---|
| 290 | | `updated_at` | timestamptz | optional |
|
|---|
| [d8ce4e2] | 291 |
|
|---|
| 292 | #### Contains — Watchlists (M) : Cryptos (N), partial on both sides, **with attribute**
|
|---|
| 293 | Which assets are on which watchlist. M:N: a list holds many assets, an asset
|
|---|
| 294 | appears on many lists. Partial on both sides — an empty list is valid and an
|
|---|
| 295 | asset need not be on any list.
|
|---|
| 296 |
|
|---|
| [9577c79] | 297 | | Attribute | Type | Constraints |
|
|---|
| 298 | |---|---|---|
|
|---|
| 299 | | `added_at` | timestamptz | required, defaults to now — recorded so a list can be shown in the order the user built it |
|
|---|
| [d8ce4e2] | 300 |
|
|---|
| 301 | ## Entity-Relationship Model History
|
|---|
| 302 |
|
|---|
| 303 | - **v01** — First complete version. Built from the entity notes in
|
|---|
| 304 | [`ep-diagram.md`](ep-diagram.md) (the initial hand-written model), with three
|
|---|
| 305 | changes made to that initial model while drawing it:
|
|---|
| 306 | 1. `Markets` was promoted from an implied attribute of the asset to its own
|
|---|
| 307 | entity set, so that prices, orders, trades and candles can all reference a
|
|---|
| 308 | pair rather than an asset.
|
|---|
| 309 | 2. `holdings` and `watchlist_items` were re-expressed as the M:N relationships
|
|---|
| 310 | `Holds` and `Contains` with their own attributes, instead of entity sets
|
|---|
| 311 | with foreign keys — the initial notes listed them as tables, which is a
|
|---|
| 312 | relational concept that does not belong in a Chen ERD.
|
|---|
| 313 | 3. `avg_price` was marked as a derived attribute rather than a plain one, to
|
|---|
| 314 | make the denormalisation explicit rather than hidden.
|
|---|
| [9577c79] | 315 | - **v02** — Student review pass over the AI-generated v01 in the TerraER GUI.
|
|---|
| 316 | - **v03** — Added `reserved_quantity` to `Holds`, and reworded `Orders.status`
|
|---|
| 317 | to state its reserve → settle → (cancel) lifecycle explicitly, instead of
|
|---|
| 318 | leaving `open`/`cancelled` as unused enum values. Triggered by a design
|
|---|
| 319 | review that pointed out the model had no way to stop a user from placing a
|
|---|
| 320 | second sell order against crypto already promised to a first, unsettled one
|
|---|
| 321 | — `quantity` alone cannot distinguish "owned" from "owned, but already
|
|---|
| 322 | committed." Also redrawn more compactly: every entity and relationship (with
|
|---|
| 323 | its own attributes moved along with it) was pulled proportionally toward the
|
|---|
| 324 | diagram's centroid, shrinking the canvas by roughly 45% with the same
|
|---|
| 325 | topology and no new overlaps. See [ERModelAIUsage](ERModelAIUsage.md) for
|
|---|
| 326 | the reasoning and how the diagram file itself was produced, and
|
|---|
| 327 | [RelationalDesign](../P2-RelationalDesign/RelationalDesign.md) and
|
|---|
| 328 | [UseCase0005](../P3-UseCaseModel/UseCase0005.md) for how the new attribute
|
|---|
| 329 | is enforced.
|
|---|
| [ef1c1c7] | 330 | - **v04 — after P7.** Phase 7 (order, balance and trade consistency) needed
|
|---|
| 331 | data the model did not have, so the model was extended to stay in line with
|
|---|
| 332 | the database:
|
|---|
| 333 | - `Users.reserved_balance`: cash reserved by open buy orders;
|
|---|
| 334 | - `Orders.filled_quantity` and the status value `partially_filled`: orders
|
|---|
| 335 | can now be filled in parts;
|
|---|
| 336 | - the relationships `FillsBuy` and `FillsSell` between `Orders` and
|
|---|
| 337 | `MarketTrades`: which orders a trade filled;
|
|---|
| 338 | - the entity set `OrderEvents` with the relationship `Logs`: the
|
|---|
| 339 | automatically recorded history of every order.
|
|---|
| 340 |
|
|---|
| 341 | Nothing existing was removed or changed. See
|
|---|
| 342 | [AdvancedDatabaseDevelopment](../P7-AdvancedDatabaseDevelopment/AdvancedDatabaseDevelopment.md).
|
|---|
| 343 | The diagram files are `ERModel_v04.xml` / `ERModel_v04.png`; earlier versions
|
|---|
| 344 | are kept.
|
|---|
| [d8ce4e2] | 345 |
|
|---|
| 346 | Reasoning for the AI-assisted part of this phase, and the full interaction log,
|
|---|
| 347 | are on [ERModelAIUsage](ERModelAIUsage.md).
|
|---|
| [9577c79] | 348 |
|
|---|