source: docs/P6-AdvancedReports/wiki/AdvancedReports.md@ ef1c1c7

main
Last change on this file since ef1c1c7 was ef1c1c7, checked in by Stefan <trsunovstefan@…>, 6 days ago

Wiki docs, phase 6 and phase 7 added

  • Property mode set to 100644
File size: 25.3 KB
Line 
1= Advanced Reports =
2
3This is a solo project (see [wiki:UseCaseModel]),
4so the rubric's "2 per team member" is 2 reports total. Both are implemented as
5single SQL statements, wrapped as callable SQL functions in
6`schema_creation.sql` (`report_top_traders`,
7`report_market_performance`) so they are actual reports inside the prototype — menu
8options `[10]` and `[11]` in `server/reports.go` — not just documentation. No change to
9[wiki:ERModel] or [wiki:RelationalDesign]
10was needed: both reports read `transactions`, `market_trades` and `orders`, all of which
11already carry everything required.
12
13=== Notation used below ===
14
15Both solutions need grouping, aggregation and computed attributes that plain relational
16algebra has no notation for, so the relational-algebra sections use the standard ''extended''
17operators:
18
19||= Symbol =||= Meaning =||
20|| `σ_cond(R)` || selection ||
21|| `π_list(R)` || projection — a list entry `expr → name` is a '''generalized projection''': a computed attribute, not just a column reference ||
22|| `ρ_name(R)` || rename ||
23|| `R ⋈_cond S` || inner join ||
24|| `R ⟕_cond S` || left outer join (needed wherever a group can legitimately have zero matching rows on the other side, e.g. zero profitable periods, zero participating users) ||
25|| `γ_{grouping; agg → name, …}(R)` || grouping/aggregation ||
26|| `τ_attr(R)` || sort, for the presentation order only ||
27
28== Top traders by realized performance ==
29
30=== Data requirements description ===
31
32''"Which users actually made money, how much, how efficiently, and how consistently — over
33a quarter, a year, or several years?"'' This is the natural crypto-exchange analogue of "which
34customers bring the most profit" from the phase brief: a Trader's `available_balance` and
35`invested_balance` (P1 `Users`) show a live snapshot, but they say nothing about performance
36''over a chosen window'', and nothing at all about whether a user's results are one lucky
37quarter or a repeatable pattern. All of it is derivable from
38`transactions` (defined in `schema_creation.sql`) as it already exists: every buy, sell
39and fee is one signed row there (see [wiki:UseCase0004] and
40[wiki:UseCase0005] for how each row is produced), so no new
41column or table is needed.
42
43The `transactions` table, from `schema_creation.sql`:
44
45{{{
46CREATE TABLE project.transactions (
47 id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
48 user_id uuid NOT NULL REFERENCES project.users(id) ON DELETE CASCADE,
49 type varchar(50) NOT NULL CHECK (type IN ('deposit', 'buy', 'sell', 'fee')),
50 amount numeric(18,4) NOT NULL,
51 currency char(3) NOT NULL DEFAULT 'USD',
52 related_order uuid REFERENCES project.orders(id),
53 created_at timestamptz NOT NULL DEFAULT now(),
54 description text
55);
56}}}
57
58Given a period `[from, to)`:
59
60 * '''Realized P/L''' = `SUM(amount)` over that user's `buy`, `sell` and `fee` transactions in
61 the period (deposits excluded — they are not trading results).
62 * '''Total invested''' = absolute value of the sum of that user's `buy` transactions in the
63 period (buy amounts are stored negative, per
64 [wiki:ERModel]).
65 * '''ROI %''' = realized P/L ÷ total invested × 100.
66 * The period is additionally bucketed into '''quarters''' internally, regardless of how wide
67 `[from, to)` is, to measure:
68 * '''Profitable / losing periods''' — how many quarters inside the window had positive vs.
69 negative P/L.
70 * '''Consistency %''' = profitable periods ÷ total periods with any activity × 100 — two
71 users can have the same total P/L with very different risk profiles, and this is the
72 number that tells them apart.
73
74=== Solution SQL ===
75
76Implemented as `project.report_top_traders(p_from, p_to)` in
77`schema_creation.sql`:
78
79{{{
80CREATE OR REPLACE FUNCTION project.report_top_traders(p_from timestamptz, p_to timestamptz)
81RETURNS TABLE (
82 username varchar,
83 realized_pl numeric,
84 total_invested numeric,
85 roi_pct numeric,
86 profitable_periods bigint,
87 losing_periods bigint,
88 total_periods bigint,
89 consistency_pct numeric
90)
91LANGUAGE sql STABLE AS $$
92 WITH period_pl AS (
93 SELECT
94 t.user_id,
95 date_trunc('quarter', t.created_at) AS period,
96 SUM(t.amount) AS period_pl,
97 SUM(t.amount) FILTER (WHERE t.type = 'buy') AS period_buy
98 FROM project.transactions t
99 WHERE t.type IN ('buy', 'sell', 'fee')
100 AND t.created_at >= p_from
101 AND t.created_at < p_to
102 GROUP BY t.user_id, date_trunc('quarter', t.created_at)
103 )
104 SELECT
105 u.username,
106 SUM(pp.period_pl) AS realized_pl,
107 ABS(SUM(pp.period_buy)) AS total_invested,
108 ROUND(SUM(pp.period_pl) / NULLIF(ABS(SUM(pp.period_buy)), 0) * 100, 2) AS roi_pct,
109 COUNT(*) FILTER (WHERE pp.period_pl > 0) AS profitable_periods,
110 COUNT(*) FILTER (WHERE pp.period_pl < 0) AS losing_periods,
111 COUNT(*) AS total_periods,
112 ROUND(COUNT(*) FILTER (WHERE pp.period_pl > 0)::numeric
113 / NULLIF(COUNT(*), 0) * 100, 2) AS consistency_pct
114 FROM period_pl pp
115 JOIN project.users u ON u.id = pp.user_id
116 GROUP BY u.id, u.username
117 ORDER BY realized_pl DESC;
118$$;
119}}}
120
121One `SELECT`, one `WITH` CTE — the CTE does the quarter bucketing per user, the outer query
122rolls those buckets up into the totals, the ROI/consistency percentages and the ranking.
123
124'''Verified run.''' `reports_demo_data.sql` adds five
125quarters of round-trip trades (2025-07 through 2026-07) on top of the normal seed data
126specifically so this report has more than one period to work with — see that file's header
127(shown in full in the Demonstration data section below)
128for exactly what it inserts and why it is optional rather than part of `-init`. Run against
129PostgreSQL 16 with `data_load.sql` + `reports_demo_data.sql` loaded, through the actual CLI
130(`[10] Report: top traders`, range `2025-01-01` to `2026-09-17`):
131
132{{{
133 Username Realized P/L Invested ROI % Prof. Loss Total Consist. %
134 ------------------------------------------------------------------------------------------
135 bob +991.0000 6300.0000 15.73 3 0 3 100.00
136 alice -475.0000 24250.0000 -1.96 2 3 5 40.00
137}}}
138
139Sorting by raw P/L alone would rank alice above bob if alice's numbers were all positive; here
140it does the opposite, and that is the point of the report — alice traded a much larger total
141(and one of her seeded round trips landed in the same quarter as the ETH buy already in
142`data_load.sql`, tipping that quarter into a loss), while bob's three quarters were smaller
143but every one of them profitable, giving him both the better ROI and a perfect consistency
144score. A single "total profit" column would have hidden that difference completely.
145
146=== Solution Relational Algebra ===
147
148{{{
149T_period = σ_{type ∈ {buy,sell,fee} ∧ created_at ≥ from ∧ created_at < to} (Transactions)
150
151T_tagged = π_{user_id, created_at, amount,
152 (type = 'buy' ? amount : 0) → buy_amt} (T_period)
153
154Periods = γ_{user_id, quarter(created_at) → period ;
155 SUM(amount) → period_pl, SUM(buy_amt) → period_buy} (T_tagged)
156
157Totals = γ_{user_id ; SUM(period_pl) → realized_pl,
158 ABS(SUM(period_buy)) → total_invested,
159 COUNT(*) → total_periods} (Periods)
160Profitable = γ_{user_id ; COUNT(*) → profitable_periods} (σ_{period_pl > 0} (Periods))
161Losing = γ_{user_id ; COUNT(*) → losing_periods} (σ_{period_pl < 0} (Periods))
162
163Combined = (Totals ⟕_{user_id} Profitable) ⟕_{user_id} Losing
164
165Ranked = π_{user_id, realized_pl, total_invested,
166 (realized_pl / total_invested × 100) → roi_pct,
167 COALESCE(profitable_periods, 0) → profitable_periods,
168 COALESCE(losing_periods, 0) → losing_periods,
169 total_periods,
170 (COALESCE(profitable_periods, 0) / total_periods × 100) → consistency_pct}
171 (Combined)
172
173Result = τ_{realized_pl ↓} (π_{username, realized_pl, total_invested, roi_pct,
174 profitable_periods, losing_periods, total_periods, consistency_pct}
175 (Ranked ⋈_{user_id = id} Users))
176}}}
177
178`Totals`/`Profitable`/`Losing` are three separate groupings of the same `Periods` relation
179because plain aggregation has no built-in "count only where X" operator; the two outer joins
180recombine them (`⟕`, not `⋈`, because a user with zero losing quarters must still appear with
181`losing_periods = 0`, not disappear from the result).
182
183== Market performance leaderboard ==
184
185=== Data requirements description ===
186
187''"Which markets were actually worth making — high volume, real price movement, real user
188interest — over a chosen period?"'' This is the "products that bring the most profit" /
189"good locations" family of question from the phase brief, translated to markets instead of
190physical products: a market with heavy volume but a dead price, or a big price swing nobody
191actually traded, are both misleading on their own; this report puts volume, trade count,
192price return and user participation side by side so a market's performance over a
193quarter/year/multi-year window can be judged as a whole, not from one number in isolation.
194Everything needed already exists: `market_trades` is the single source of truth for price and
195volume for every market ([wiki:PrototypeImplementation]),
196and `orders` is the only place a specific user is tied to a specific market
197([wiki:ERModel]) —
198`market_trades` deliberately has no `user_id` column, since it also records the market
199simulator's own fills.
200
201Given a period `[from, to)`, per market:
202
203 * '''Total volume''' = `SUM(quantity)` over its trades in the period.
204 * '''Trade count''' = `COUNT(*)` over the same trades (real fills and simulated fills alike —
205 this is activity, not just user activity).
206 * '''Average trading price''' = `AVG(price)` over the same trades.
207 * '''Market return %''' = `(last trade price − first trade price) ÷ first trade price × 100`,
208 ordering trades by `executed_at` inside the period.
209 * '''Participating users''' = `COUNT(DISTINCT user_id)` from that market's '''executed orders'''
210 in the period — the only correct source, since `market_trades` cannot answer this question
211 at all.
212
213=== Solution SQL ===
214
215Implemented as `project.report_market_performance(p_from, p_to)` in
216`schema_creation.sql`:
217
218{{{
219CREATE OR REPLACE FUNCTION project.report_market_performance(p_from timestamptz, p_to timestamptz)
220RETURNS TABLE (
221 symbol varchar,
222 quote_currency char(3),
223 total_volume numeric,
224 trade_count bigint,
225 avg_price numeric,
226 market_return_pct numeric,
227 participating_users bigint
228)
229LANGUAGE sql STABLE AS $$
230 WITH trades AS (
231 SELECT
232 market_id, price, quantity, executed_at,
233 FIRST_VALUE(price) OVER w AS first_price,
234 LAST_VALUE(price) OVER (PARTITION BY market_id ORDER BY executed_at
235 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_price
236 FROM project.market_trades
237 WHERE executed_at >= p_from AND executed_at < p_to
238 WINDOW w AS (PARTITION BY market_id ORDER BY executed_at)
239 ),
240 market_stats AS (
241 SELECT
242 market_id,
243 SUM(quantity) AS total_volume,
244 COUNT(*) AS trade_count,
245 AVG(price) AS avg_price,
246 MAX(first_price) AS first_price,
247 MAX(last_price) AS last_price
248 FROM trades
249 GROUP BY market_id
250 ),
251 participation AS (
252 SELECT market_id, COUNT(DISTINCT user_id) AS participating_users
253 FROM project.orders
254 WHERE status = 'executed' AND executed_at >= p_from AND executed_at < p_to
255 GROUP BY market_id
256 )
257 SELECT
258 c.symbol,
259 m.quote_currency,
260 ms.total_volume,
261 ms.trade_count,
262 ROUND(ms.avg_price, 6) AS avg_price,
263 ROUND((ms.last_price - ms.first_price) / NULLIF(ms.first_price, 0) * 100, 2) AS market_return_pct,
264 COALESCE(p.participating_users, 0) AS participating_users
265 FROM market_stats ms
266 JOIN project.markets m ON m.id = ms.market_id
267 JOIN project.crypto c ON c.id = m.crypto_id
268 LEFT JOIN participation p ON p.market_id = ms.market_id
269 ORDER BY ms.total_volume DESC;
270$$;
271}}}
272
273`FIRST_VALUE`/`LAST_VALUE` pick the period's opening and closing price per market without a
274self-join; `LEFT JOIN participation` is required, not optional — a market can have trades
275from the simulator alone and legitimately zero participating users, and it must still show
276`0`, not disappear from the report.
277
278'''Verified run.''' Same seed as above (`data_load.sql` + `reports_demo_data.sql`, which also
279adds a BTC/USD uptrend and an ETH/USD downtrend across the same five quarters — see that
280file in the Demonstration data section below). Run through the CLI (`[11] Report: market performance`, `2025-01-01` to `2026-09-17`):
281
282{{{
283 Symbol Quote Volume Trades Avg Price Return % Users
284 -----------------------------------------------------------------------------
285 DOGE USD 29500.0000 3 0.120583 +2.95 0
286 ADA USD 2500.0000 3 0.450750 +1.62 0
287 SOL USD 23.5000 3 165.283333 +1.13 0
288 ETH USD 14.3500 8 3622.312500 -12.00 2
289 BTC USD 3.9750 9 59447.400000 +67.85 2
290}}}
291
292BTC/USD and ETH/USD are the only two markets with historical (multi-quarter) data seeded, and
293they show it: BTC's price nearly tripled over the period (`+67.85%`), while ETH quietly lost
294`12%`. ADA/SOL/DOGE only have the few minutes of `data_load.sql`'s own recent seed trades, so
295their return numbers reflect that narrow window, and their `0` participating users is correct
296— `data_load.sql` seeds trade history for every market but only ever places an ''order'' on ETH.
297
298A price-volatility column (standard deviation of trade price) was dropped from this report
299after review — with only a handful of trades per market in most periods it read as noise
300rather than signal, and total volume plus return already carry the useful information.
301
302=== Solution Relational Algebra ===
303
304{{{
305MT_period = σ_{executed_at ≥ from ∧ executed_at < to} (MarketTrades)
306
307Bounds = γ_{market_id ; MIN(executed_at) → t_first, MAX(executed_at) → t_last} (MT_period)
308
309FirstPx = π_{market_id, price → first_price}
310 (MT_period ⋈_{MT_period.market_id = Bounds.market_id
311 ∧ executed_at = t_first} Bounds)
312LastPx = π_{market_id, price → last_price}
313 (MT_period ⋈_{MT_period.market_id = Bounds.market_id
314 ∧ executed_at = t_last} Bounds)
315
316Stats = γ_{market_id ; SUM(quantity) → total_volume, COUNT(*) → trade_count,
317 AVG(price) → avg_price} (MT_period)
318
319MarketStats = (Stats ⋈_{market_id} FirstPx) ⋈_{market_id} LastPx
320
321O_period = σ_{status = 'executed' ∧ executed_at ≥ from ∧ executed_at < to} (Orders)
322Participation = γ_{market_id ; COUNT_DISTINCT(user_id) → participating_users} (O_period)
323
324Joined = ((MarketStats ⟕_{market_id} Participation)
325 ⋈_{market_id = id} Markets) ⋈_{crypto_id = id} Crypto
326
327Result = τ_{total_volume ↓} (
328 π_{symbol, quote_currency, total_volume, trade_count, avg_price,
329 (last_price − first_price) / first_price × 100 → market_return_pct,
330 COALESCE(participating_users, 0) → participating_users}
331 (Joined) )
332}}}
333
334`FirstPx`/`LastPx` express `FIRST_VALUE`/`LAST_VALUE` — which have no classical relational-
335algebra equivalent — as an aggregation for the boundary timestamp per market followed by a
336self-join back to `MarketTrades` to recover the price at that timestamp; this is the standard
337way to express "value at the extreme of a group" in extended relational algebra.
338
339== Demonstration data ==
340
341`reports_demo_data.sql`, the optional script both verified runs above were produced with:
342
343{{{
344-- reports_demo_data.sql
345-- EduBerza - optional historical data for the P6 reports
346-- Course: Databases 2025/2026 Winter, FINKI UKIM
347--
348-- data_load.sql only seeds ~10 minutes of trade history, which is enough to
349-- demonstrate UC0001-UC0007 but not enough to show report_top_traders() or
350-- report_market_performance() doing anything interesting: everything falls
351-- into a single quarter, so "number of profitable periods" and "consistency"
352-- are trivial and "market return" has almost no history to work with.
353--
354-- This script adds five quarters of synthetic transactions, market trades and
355-- executed orders on top of an already-loaded data_load.sql, spanning
356-- 2025-07 to 2026-07, so the two P6 reports have several periods and two
357-- markets with opposite price trends to actually compare.
358--
359-- Deliberately NOT part of -init / -load-data: it only inserts into
360-- transactions, market_trades and orders, and does not touch
361-- users.available_balance/invested_balance or holdings, so it does not
362-- disturb the balances the other use cases' documented "verified run"
363-- sections depend on. Run it by hand, after data_load.sql, only to exercise
364-- the two reports:
365--
366-- psql "$DATABASE_URL" -f server/db/schema_creation.sql
367-- psql "$DATABASE_URL" -f server/db/data_load.sql
368-- psql "$DATABASE_URL" -f server/db/reports_demo_data.sql
369--
370-- Idempotent: deletes its own previously-inserted rows (tagged via
371-- description/source) before re-inserting.
372
373SET search_path TO project, public;
374
375DELETE FROM transactions WHERE description = 'P6 demo data';
376DELETE FROM orders WHERE id IN (
377 'e1111111-1111-1111-1111-111111111111', 'e2222222-2222-2222-2222-222222222222',
378 'e3333333-3333-3333-3333-333333333333', 'e4444444-4444-4444-4444-444444444444',
379 'e5555555-5555-5555-5555-555555555555'
380);
381DELETE FROM market_trades WHERE source = 'p6_demo';
382
383-- ============================================================================
384-- Alice: five quarterly round trips, 3 profitable / 2 losing (60% consistency)
385-- ============================================================================
386INSERT INTO transactions (user_id, type, amount, currency, created_at, description) VALUES
387 ('b1111111-1111-1111-1111-111111111111', 'buy', -5000.0000, 'USD', '2025-07-15 10:00', 'P6 demo data'),
388 ('b1111111-1111-1111-1111-111111111111', 'sell', 5800.0000, 'USD', '2025-07-20 10:00', 'P6 demo data'),
389 ('b1111111-1111-1111-1111-111111111111', 'fee', -5.0000, 'USD', '2025-07-20 10:00', 'P6 demo data'),
390
391 ('b1111111-1111-1111-1111-111111111111', 'buy', -4000.0000, 'USD', '2025-10-15 10:00', 'P6 demo data'),
392 ('b1111111-1111-1111-1111-111111111111', 'sell', 3500.0000, 'USD', '2025-10-20 10:00', 'P6 demo data'),
393 ('b1111111-1111-1111-1111-111111111111', 'fee', -5.0000, 'USD', '2025-10-20 10:00', 'P6 demo data'),
394
395 ('b1111111-1111-1111-1111-111111111111', 'buy', -6000.0000, 'USD', '2026-01-15 10:00', 'P6 demo data'),
396 ('b1111111-1111-1111-1111-111111111111', 'sell', 6700.0000, 'USD', '2026-01-20 10:00', 'P6 demo data'),
397 ('b1111111-1111-1111-1111-111111111111', 'fee', -5.0000, 'USD', '2026-01-20 10:00', 'P6 demo data'),
398
399 ('b1111111-1111-1111-1111-111111111111', 'buy', -3000.0000, 'USD', '2026-04-15 10:00', 'P6 demo data'),
400 ('b1111111-1111-1111-1111-111111111111', 'sell', 2600.0000, 'USD', '2026-04-20 10:00', 'P6 demo data'),
401 ('b1111111-1111-1111-1111-111111111111', 'fee', -5.0000, 'USD', '2026-04-20 10:00', 'P6 demo data'),
402
403 ('b1111111-1111-1111-1111-111111111111', 'buy', -4500.0000, 'USD', '2026-07-15 10:00', 'P6 demo data'),
404 ('b1111111-1111-1111-1111-111111111111', 'sell', 5200.0000, 'USD', '2026-07-20 10:00', 'P6 demo data'),
405 ('b1111111-1111-1111-1111-111111111111', 'fee', -5.0000, 'USD', '2026-07-20 10:00', 'P6 demo data');
406
407-- ============================================================================
408-- Bob: three quarterly round trips, all profitable (100% consistency),
409-- smaller total P/L than Alice but a higher ROI.
410-- ============================================================================
411INSERT INTO transactions (user_id, type, amount, currency, created_at, description) VALUES
412 ('b2222222-2222-2222-2222-222222222222', 'buy', -2000.0000, 'USD', '2025-10-10 10:00', 'P6 demo data'),
413 ('b2222222-2222-2222-2222-222222222222', 'sell', 2300.0000, 'USD', '2025-10-12 10:00', 'P6 demo data'),
414 ('b2222222-2222-2222-2222-222222222222', 'fee', -3.0000, 'USD', '2025-10-12 10:00', 'P6 demo data'),
415
416 ('b2222222-2222-2222-2222-222222222222', 'buy', -2500.0000, 'USD', '2026-01-10 10:00', 'P6 demo data'),
417 ('b2222222-2222-2222-2222-222222222222', 'sell', 2900.0000, 'USD', '2026-01-12 10:00', 'P6 demo data'),
418 ('b2222222-2222-2222-2222-222222222222', 'fee', -3.0000, 'USD', '2026-01-12 10:00', 'P6 demo data'),
419
420 ('b2222222-2222-2222-2222-222222222222', 'buy', -1800.0000, 'USD', '2026-04-10 10:00', 'P6 demo data'),
421 ('b2222222-2222-2222-2222-222222222222', 'sell', 2100.0000, 'USD', '2026-04-12 10:00', 'P6 demo data'),
422 ('b2222222-2222-2222-2222-222222222222', 'fee', -3.0000, 'USD', '2026-04-12 10:00', 'P6 demo data');
423
424-- ============================================================================
425-- Market trades: BTC/USD trending up, ETH/USD trending down, five quarters.
426-- source='p6_demo' keeps these separate from data_load.sql's own rows and
427-- from live user/bot fills so this script can clean up after itself.
428-- ============================================================================
429INSERT INTO market_trades (market_id, executed_at, price, quantity, side, source) VALUES
430 ('a1111111-1111-1111-1111-111111111111', '2025-07-15 10:00', 40000.000000, 0.500000, 'buy', 'p6_demo'),
431 ('a1111111-1111-1111-1111-111111111111', '2025-10-15 10:00', 45000.000000, 0.800000, 'buy', 'p6_demo'),
432 ('a1111111-1111-1111-1111-111111111111', '2026-01-15 10:00', 55000.000000, 1.200000, 'buy', 'p6_demo'),
433 ('a1111111-1111-1111-1111-111111111111', '2026-04-15 10:00', 60000.000000, 1.000000, 'buy', 'p6_demo'),
434
435 ('a2222222-2222-2222-2222-222222222222', '2025-07-15 10:00', 4000.000000, 3.000000, 'sell', 'p6_demo'),
436 ('a2222222-2222-2222-2222-222222222222', '2025-10-15 10:00', 3800.000000, 2.500000, 'sell', 'p6_demo'),
437 ('a2222222-2222-2222-2222-222222222222', '2026-01-15 10:00', 3600.000000, 2.000000, 'sell', 'p6_demo'),
438 ('a2222222-2222-2222-2222-222222222222', '2026-04-15 10:00', 3550.000000, 1.800000, 'sell', 'p6_demo');
439
440-- ============================================================================
441-- Executed orders: who participated in which market, across the same quarters.
442-- ============================================================================
443INSERT INTO orders (id, user_id, market_id, side, type, status, quantity, price, placed_at, executed_at) VALUES
444 ('e1111111-1111-1111-1111-111111111111', 'b1111111-1111-1111-1111-111111111111',
445 'a1111111-1111-1111-1111-111111111111', 'buy', 'market', 'executed', 0.5000, 40000.000000,
446 '2025-07-15 10:00', '2025-07-15 10:00'),
447 ('e2222222-2222-2222-2222-222222222222', 'b1111111-1111-1111-1111-111111111111',
448 'a2222222-2222-2222-2222-222222222222', 'sell', 'market', 'executed', 3.0000, 4000.000000,
449 '2025-10-15 10:00', '2025-10-15 10:00'),
450 ('e3333333-3333-3333-3333-333333333333', 'b2222222-2222-2222-2222-222222222222',
451 'a1111111-1111-1111-1111-111111111111', 'buy', 'market', 'executed', 1.2000, 55000.000000,
452 '2026-01-15 10:00', '2026-01-15 10:00'),
453 ('e4444444-4444-4444-4444-444444444444', 'b2222222-2222-2222-2222-222222222222',
454 'a1111111-1111-1111-1111-111111111111', 'buy', 'market', 'executed', 1.0000, 60000.000000,
455 '2026-04-15 10:00', '2026-04-15 10:00'),
456 ('e5555555-5555-5555-5555-555555555555', 'b3333333-3333-3333-3333-333333333333',
457 'a2222222-2222-2222-2222-222222222222', 'sell', 'market', 'executed', 2.0000, 3600.000000,
458 '2026-01-15 10:00', '2026-01-15 10:00');
459}}}
460
461== AI usage ==
462
463AI was used in this phase and is logged in full, per the course rule for P1 onward.
464
465 * '''Phase log:''' [wiki:AdvancedReportsAIUsage] — service used, what
466 the AI produced, and what I decided myself.
467
468'''Service:''' Claude Code (Anthropic), `https://claude.com/claude-code` — Claude subscription,
469model Claude Sonnet 5.
470
471'''In short:''' I specified both report questions in full — including the exact formulas for
472P/L, ROI, consistency, market return, volatility and user participation — and asked the AI to
473turn them into working SQL, wire them into the prototype as real reports, build the
474relational-algebra equivalents, and produce demonstration data rich enough to show the
475reports doing something non-trivial. In a follow-up, I asked for the price-volatility column
476to be dropped from the market performance report — see the "Follow-up — 2026-09-17" section of
477[wiki:AdvancedReportsAIUsage] for that change.
Note: See TracBrowser for help on using the repository browser.