source: docs/P1-ConceptualModel/ERModel.md@ b715712

main
Last change on this file since b715712 was b715712, checked in by Stefan <trsunovstefan@…>, 8 weeks ago

Add the server side and configuration

  • Property mode set to 100644
File size: 12.6 KB
RevLine 
[d8ce4e2]1# Entity-Relationship Model v.01
2
3## Diagram
4
5![ERModel_v01](ERModel_v01.png)
6
7Attachments for this page: `ERModel_v01.xml` (TerraER source) and `ERModel_v01.png`
8(exported image). Open the source with TerraER 3.11:
9
10```sh
11java -jar TerraER3.11.jar # then File → Open → ERModel_v01.xml
12```
13
14Notation: Chen. Rectangles are entity sets, diamonds are relationships, ellipses
15are attributes, underlined ellipses are primary keys, the dashed ellipse is a
16derived attribute. A double line between an entity set and a relationship marks
17**total participation** (every instance of that entity set must participate); a
18single line marks partial participation.
19
20Two deliberate modeling decisions worth stating up front:
21
22- **No foreign keys appear in the diagram.** Connections between entity sets are
23 expressed as relationships, per the notation. Foreign-key columns appear only
24 in the relational model in [RelationalDesign](../P2-RelationalDesign/RelationalDesign.md).
25- **`Holds` and `Contains` are relationships, not entity sets.** Both are M:N and
26 both carry their own attributes, which is exactly what a Chen relationship is
27 for. They become tables (`holdings`, `watchlist_items`) only in P2.
28
29## Data requirements
30
31### Entity sets
32
33#### Users
34Registered participants of the platform. Every action in the simulation is
35attributed to a user, and the two balance attributes are what makes the
36simulation work: cash that is free to trade is tracked separately from cash that
37is currently committed to open positions, so the platform can refuse a purchase
38without having to recompute the whole portfolio first.
39
40- **Candidate keys:** `{id}`, `{username}`, `{email}`. Primary key: **`id`**.
41 A surrogate UUID was chosen because it is opaque and stable — `username` and
42 `email` are both things a user may legitimately want to change later, and
43 every relationship in the diagram points at `Users`, so a mutable key would
44 propagate changes across the whole database.
45- **Attributes:**
46 - `id` — UUID, required, primary key.
47 - `username` — text, max 50, required, unique.
48 - `email` — text, max 255, required, unique, must contain `@`.
49 - `full_name` — text, max 200, optional.
50 - `password_hash` — text, max 255, required. Never the password itself; the
51 prototype stores a SHA-256 hex digest.
52 - `available_balance` — numeric(18,4), required, default 0, must be ≥ 0.
53 - `invested_balance` — numeric(18,4), required, default 0, must be ≥ 0.
54 - `created_at` — timestamp with time zone, required, defaults to now.
55 - `updated_at` — timestamp with time zone, optional (null until first change).
56
57#### Cryptos
58The 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 —
60the same asset can be quoted against several currencies, and a user's holding is
61in the *asset*, not in a particular pair.
62
63- **Candidate keys:** `{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- **Attributes:**
67 - `id` — UUID, required, primary key.
68 - `symbol` — text, max 20, required, unique (e.g. `BTC`).
69 - `name` — text, max 255, required (e.g. `Bitcoin`).
70 - `created_at` — timestamptz, required, defaults to now.
71
72#### Markets
73A tradeable pair: one crypto asset quoted in one currency, e.g. BTC/USD. This is
74where prices live, and it is the thing an order is placed *on*. Modeled as its
75own entity set rather than an attribute of `Cryptos` because a market has its own
76lifecycle — it can be deactivated without deleting the asset — and because
77trades, candles and orders all reference the pair, not the asset.
78
79- **Candidate keys:** `{id}`, `{crypto_id, quote_currency}` — that pair is
80 unique by definition, since a given asset can only be quoted once per
81 currency. Primary key: **`id`**, so that the many entity sets referencing a
82 market carry one narrow column instead of a composite key.
83- **Attributes:**
84 - `id` — UUID, required, primary key.
85 - `quote_currency` — text, exactly 3 characters, required, default `USD`.
86 - `is_active` — boolean, required, default true. Inactive markets are hidden
87 from the trading menus but keep their history.
88 - `created_at` — timestamptz, required, defaults to now.
89
90#### Orders
91A user's instruction to buy or sell on a market. Needed as a separate entity set
92because an order is a record of *intent* that outlives its execution: it keeps
93the requested quantity and price even after it has been filled, which is what
94makes the ledger auditable.
95
96- **Candidate keys:** `{id}` only. There is no natural key — the same user can
97 place two identical orders on the same market in the same second, and both are
98 legitimately distinct. Primary key: **`id`**.
99- **Attributes:**
100 - `id` — UUID, required, primary key.
101 - `side` — text, required, restricted to `buy` or `sell`.
102 - `type` — text, required, restricted to `market` or `limit`. The prototype
103 executes only `market` orders; `limit` exists so the model does not have to
104 change when limit orders are implemented.
105 - `status` — text, required, restricted to `open`, `executed`, `cancelled`.
106 - `quantity` — numeric(20,4), required, must be > 0.
107 - `price` — numeric(18,6), optional — null for a market order until it fills,
108 then the fill price.
109 - `placed_at` — timestamptz, required, defaults to now.
110 - `executed_at` — timestamptz, optional, set when the order fills.
111
112#### Transactions
113The financial ledger: every movement of virtual cash, in one place. This exists
114so that a balance is never just a number someone edited — it is the sum of an
115auditable list of entries, which is also what the "explain every step" goal of
116the project needs.
117
118- **Candidate keys:** `{id}` only. Primary key: **`id`**.
119- **Attributes:**
120 - `id` — UUID, required, primary key.
121 - `type` — text, required, restricted to `deposit`, `buy`, `sell`, `fee`.
122 - `amount` — numeric(18,4), required. Signed: negative for money leaving the
123 cash balance, positive for money arriving.
124 - `currency` — text, exactly 3 characters, required, default `USD`.
125 - `created_at` — timestamptz, required, defaults to now.
126 - `description` — text, optional, free-form human-readable explanation.
127
128#### MarketTrades
129Individual executed trades on a market, from the user's own fills and from the
130market simulator. This is the single source of truth for the current price: the
131price of a market is the price of its most recent trade, never a column someone
132writes directly.
133
134- **Candidate keys:** `{id}`. In principle `{market_id, executed_at}` looks
135 unique, but two trades can share a timestamp, so it is not a safe key.
136 Primary key: **`id`** (a plain auto-incrementing integer here rather than a
137 UUID, because this is the highest-volume entity set and it is only ever read
138 in timestamp order, never referenced by anything else).
139- **Attributes:**
140 - `id` — integer, required, primary key, auto-generated.
141 - `executed_at` — timestamptz, required.
142 - `price` — numeric(18,6), required, must be > 0.
143 - `quantity` — numeric(20,6), required, must be > 0.
144 - `side` — text, optional, `buy` or `sell`.
145 - `source` — text, max 50, required, default `simulation`. Distinguishes a
146 simulated trade from a user's own fill (`user`).
147
148#### MarketCandles
149OHLCV aggregates per market and timeframe — the data a price chart is drawn
150from. Stored rather than computed on the fly because the point of the project is
151a chart-driven interface, and re-aggregating the whole trade history for every
152screen refresh does not scale.
153
154- **Candidate keys:** `{id}`, and `{market_id, timeframe, candle_time}` — a
155 market has exactly one candle per timeframe per time bucket. Primary key:
156 **`id`**; the composite is enforced as a uniqueness rule because it is the
157 real-world constraint and it is what prevents duplicate candles.
158- **Attributes:**
159 - `id` — integer, required, primary key, auto-generated.
160 - `timeframe` — text, required, restricted to `1m`, `5m`, `1h`, `1d`.
161 - `open`, `high`, `low`, `close` — numeric(18,6), all required.
162 - `volume` — numeric(20,6), required.
163 - `candle_time` — timestamptz, required — the start of the bucket.
164
165#### Watchlists
166A named list of assets a user wants to monitor. A separate entity set rather than
167a flag on the relationship between users and assets, because a user may want
168several lists ("long term", "watching today") and each needs its own name.
169
170- **Candidate keys:** `{id}`. `{user_id, name}` would also work if list names
171 are required to be unique per user; the model does not impose that, so it is
172 not listed as a candidate key. Primary key: **`id`**.
173- **Attributes:**
174 - `id` — UUID, required, primary key.
175 - `name` — text, max 100, required.
176 - `created_at` — timestamptz, required, defaults to now.
177
178### Relationships
179
180#### QuotedOn — Cryptos (1) : Markets (N), total on Markets
181Ties a market to the asset it trades. One asset can be quoted in many markets;
182every market must have exactly one asset, hence total participation on the
183`Markets` side. No attributes of its own.
184
185#### PlacedOn — Markets (1) : Orders (N), total on Orders
186Records which market an order was placed on. Every order must name a market;
187a market may have no orders yet. No attributes.
188
189#### Places — Users (1) : Orders (N), total on Orders
190Records who placed an order. Every order belongs to exactly one user; a new user
191has no orders. No attributes.
192
193#### Records — Users (1) : Transactions (N), total on Transactions
194Attributes each ledger entry to a user. Every entry belongs to exactly one user.
195No attributes.
196
197#### Settles — Orders (1) : Transactions (N), partial on both sides
198Links a ledger entry to the order that caused it. Partial on the `Transactions`
199side because deposits have no originating order, and partial on the `Orders` side
200because an order that never executes never produces a ledger entry. This is why
201the corresponding column is nullable in P2. No attributes.
202
203#### Fills — Markets (1) : MarketTrades (N), total on MarketTrades
204Every executed trade happened on exactly one market. No attributes.
205
206#### Aggregates — Markets (1) : MarketCandles (N), total on MarketCandles
207Every candle summarises trades of exactly one market. No attributes.
208
209#### Owns — Users (1) : Watchlists (N), total on Watchlists
210Every watchlist belongs to exactly one user. No attributes.
211
212#### Holds — Users (M) : Cryptos (N), partial on both sides, **with attributes**
213A user's position in an asset. M:N because one user holds many assets and one
214asset is held by many users, and partial on both sides because a user may hold
215nothing and an asset may be held by nobody. Modeled as a relationship rather
216than an entity set because a position has no identity of its own — it is
217entirely described by *which user*, *which asset*, and how much.
218
219- **Attributes:**
220 - `quantity` — numeric(20,4), required, must be ≥ 0.
221 - `avg_price` — numeric(18,6), required, ≥ 0, **derived** (dashed ellipse):
222 the weighted average of the prices at which the position was accumulated.
223 It is derivable from the buy history, and is stored anyway so that
224 unrealised P/L can be shown without replaying the whole ledger.
225 - `created_at` — timestamptz, required, defaults to now.
226 - `updated_at` — timestamptz, optional.
227
228#### Contains — Watchlists (M) : Cryptos (N), partial on both sides, **with attribute**
229Which assets are on which watchlist. M:N: a list holds many assets, an asset
230appears on many lists. Partial on both sides — an empty list is valid and an
231asset need not be on any list.
232
233- **Attributes:**
234 - `added_at` — timestamptz, required, defaults to now. Recorded so a list can
235 be shown in the order the user built it.
236
237## Entity-Relationship Model History
238
239- **v01** — First complete version. Built from the entity notes in
240 [`ep-diagram.md`](ep-diagram.md) (the initial hand-written model), with three
241 changes made to that initial model while drawing it:
242 1. `Markets` was promoted from an implied attribute of the asset to its own
243 entity set, so that prices, orders, trades and candles can all reference a
244 pair rather than an asset.
245 2. `holdings` and `watchlist_items` were re-expressed as the M:N relationships
246 `Holds` and `Contains` with their own attributes, instead of entity sets
247 with foreign keys — the initial notes listed them as tables, which is a
248 relational concept that does not belong in a Chen ERD.
249 3. `avg_price` was marked as a derived attribute rather than a plain one, to
250 make the denormalisation explicit rather than hidden.
251
252Reasoning for the AI-assisted part of this phase, and the full interaction log,
253are on [ERModelAIUsage](ERModelAIUsage.md).
Note: See TracBrowser for help on using the repository browser.