Changeset 9e6d8a2
- Timestamp:
- 09/17/26 00:36:39 (13 days ago)
- Branches:
- main
- Children:
- 4438e45
- Parents:
- 35bcb41
- Files:
-
- 5 edited
-
docs/P6-AdvancedReports/AdvancedReports.md (modified) (9 diffs)
-
docs/P6-AdvancedReports/AdvancedReportsAIUsage.md (modified) (1 diff)
-
docs/P6-AdvancedReports/P6.zip (modified) ( previous)
-
server/db/schema_creation.sql (modified) (3 diffs)
-
server/reports.go (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
docs/P6-AdvancedReports/AdvancedReports.md
r35bcb41 r9e6d8a2 175 175 physical products: a market with heavy volume but a dead price, or a big price swing nobody 176 176 actually traded, are both misleading on their own; this report puts volume, trade count, 177 price return , volatilityand user participation side by side so a market's performance over a177 price return and user participation side by side so a market's performance over a 178 178 quarter/year/multi-year window can be judged as a whole, not from one number in isolation. 179 179 Everything needed already exists: `market_trades` is the single source of truth for price and … … 192 192 - **Market return %** = `(last trade price − first trade price) ÷ first trade price × 100`, 193 193 ordering trades by `executed_at` inside the period. 194 - **Price volatility** = the (sample) standard deviation of trade prices in the period.195 194 - **Participating users** = `COUNT(DISTINCT user_id)` from that market's **executed orders** 196 195 in the period — the only correct source, since `market_trades` cannot answer this question … … 211 210 avg_price numeric, 212 211 market_return_pct numeric, 213 price_volatility numeric,214 212 participating_users bigint 215 213 ) … … 231 229 COUNT(*) AS trade_count, 232 230 AVG(price) AS avg_price, 233 STDDEV(price) AS price_volatility,234 231 MAX(first_price) AS first_price, 235 232 MAX(last_price) AS last_price … … 250 247 ROUND(ms.avg_price, 6) AS avg_price, 251 248 ROUND((ms.last_price - ms.first_price) / NULLIF(ms.first_price, 0) * 100, 2) AS market_return_pct, 252 ROUND(COALESCE(ms.price_volatility, 0), 6) AS price_volatility,253 249 COALESCE(p.participating_users, 0) AS participating_users 254 250 FROM market_stats ms … … 270 266 271 267 ``` 272 Symbol Quote Volume Trades Avg Price Return % VolatilityUsers273 ----------------------------------------------------------------------------- ----------------274 DOGE USD 29500.0000 3 0.120583 +2.95 0.0018430275 ADA USD 2500.0000 3 0.450750 +1.62 0.0037830276 SOL USD 23.5000 3 165.283333 +1.13 0.9438400277 ETH USD 14.3500 8 3622.312500 -12.00 182.6579922278 BTC USD 3.9750 9 59447.400000 +67.85 10563.4133392268 Symbol Quote Volume Trades Avg Price Return % Users 269 ----------------------------------------------------------------------------- 270 DOGE USD 29500.0000 3 0.120583 +2.95 0 271 ADA USD 2500.0000 3 0.450750 +1.62 0 272 SOL USD 23.5000 3 165.283333 +1.13 0 273 ETH USD 14.3500 8 3622.312500 -12.00 2 274 BTC USD 3.9750 9 59447.400000 +67.85 2 279 275 ``` 280 276 281 277 BTC/USD and ETH/USD are the only two markets with historical (multi-quarter) data seeded, and 282 they show it: BTC's price nearly tripled over the period (`+67.85%`) with by far the highest 283 volatility, while ETH quietly lost `12%`. ADA/SOL/DOGE only have the few minutes of 284 `data_load.sql`'s own recent seed trades, so their return/volatility numbers reflect that 285 narrow window, and their `0` participating users is correct — `data_load.sql` seeds trade 286 history for every market but only ever places an *order* on ETH. 278 they show it: BTC's price nearly tripled over the period (`+67.85%`), while ETH quietly lost 279 `12%`. ADA/SOL/DOGE only have the few minutes of `data_load.sql`'s own recent seed trades, so 280 their return numbers reflect that narrow window, and their `0` participating users is correct 281 — `data_load.sql` seeds trade history for every market but only ever places an *order* on ETH. 282 283 A price-volatility column (standard deviation of trade price) was dropped from this report 284 after review — with only a handful of trades per market in most periods it read as noise 285 rather than signal, and total volume plus return already carry the useful information. 287 286 288 287 ### Solution Relational Algebra … … 301 300 302 301 Stats = γ_{market_id ; SUM(quantity) → total_volume, COUNT(*) → trade_count, 303 AVG(price) → avg_price , STDDEV(price) → price_volatility} (MT_period)302 AVG(price) → avg_price} (MT_period) 304 303 305 304 MarketStats = (Stats ⋈_{market_id} FirstPx) ⋈_{market_id} LastPx … … 314 313 π_{symbol, quote_currency, total_volume, trade_count, avg_price, 315 314 (last_price − first_price) / first_price × 100 → market_return_pct, 316 COALESCE(price_volatility, 0) → price_volatility,317 315 COALESCE(participating_users, 0) → participating_users} 318 316 (Joined) ) … … 338 336 turn them into working SQL, wire them into the prototype as real reports, build the 339 337 relational-algebra equivalents, and produce demonstration data rich enough to show the 340 reports doing something non-trivial. 338 reports doing something non-trivial. In a follow-up, I asked for the price-volatility column 339 to be dropped from the market performance report — see 340 [AdvancedReportsAIUsage](AdvancedReportsAIUsage.md#follow-up--2026-09-17) for that change. -
docs/P6-AdvancedReports/AdvancedReportsAIUsage.md
r35bcb41 r9e6d8a2 126 126 > [`reports_demo_data.sql`](../../server/db/reports_demo_data.sql) directly. Append any further 127 127 > prompts here if you ask for revisions. 128 129 ### Follow-up — 2026-09-17 130 131 **Intent:** simplify the market performance report after seeing it run — with only a handful 132 of trades per market in most periods, price volatility read as noise rather than a useful 133 signal. 134 135 **Prompt (student, verbatim):** 136 > Can you tell me the exact database_url so I can execute the schema creating and the data_load? 137 > 138 > From the Market Performance can we remove Volatility, I don't think that is that useful, also 139 > can you tell me what is users column? 140 141 **Response (AI, summarised):** 142 - Gave the local dev connection string (`postgresql://bp_project:1234@localhost:5433/bp_database`, 143 from `.env`) and explained `participating_users` is `COUNT(DISTINCT user_id)` from that 144 market's executed orders, not from `market_trades` (which has no `user_id`). 145 - Removed `price_volatility`/`STDDEV(price)` from `report_market_performance()` in 146 `schema_creation.sql`, from its `RETURNS TABLE` and its `SELECT`, and from the corresponding 147 column in `server/reports.go`'s `ShowMarketPerformance`. 148 - Updated the relational algebra (`Stats`, `Result`) and the SQL listing, verified run output, 149 and prose in [AdvancedReports.md](AdvancedReports.md) to match, and added a short note 150 explaining why the column was dropped. 151 - Rebuilt, re-ran `-init` and `reports_demo_data.sql` against the live database, and 152 re-verified the market performance report through the actual CLI before and after the 153 change to confirm only the one column disappeared and every other number is unchanged. 154 155 **What I decided:** to drop the column entirely rather than keep it computed-but-hidden, 156 since an unused computation left in the query is exactly the kind of thing that should not 157 survive a review. -
server/db/schema_creation.sql
r35bcb41 r9e6d8a2 261 261 avg_price numeric, 262 262 market_return_pct numeric, 263 price_volatility numeric,264 263 participating_users bigint 265 264 ) … … 281 280 COUNT(*) AS trade_count, 282 281 AVG(price) AS avg_price, 283 STDDEV(price) AS price_volatility,284 282 MAX(first_price) AS first_price, 285 283 MAX(last_price) AS last_price … … 300 298 ROUND(ms.avg_price, 6) AS avg_price, 301 299 ROUND((ms.last_price - ms.first_price) / NULLIF(ms.first_price, 0) * 100, 2) AS market_return_pct, 302 ROUND(COALESCE(ms.price_volatility, 0), 6) AS price_volatility,303 300 COALESCE(p.participating_users, 0) AS participating_users 304 301 FROM market_stats ms -
server/reports.go
r35bcb41 r9e6d8a2 81 81 defer rows.Close() 82 82 83 header := fmt.Sprintf(" %-6s %-5s %12s %8s %14s %12s % 14s %8s",84 "Symbol", "Quote", "Volume", "Trades", "Avg Price", "Return %", " Volatility", "Users")83 header := fmt.Sprintf(" %-6s %-5s %12s %8s %14s %12s %8s", 84 "Symbol", "Quote", "Volume", "Trades", "Avg Price", "Return %", "Users") 85 85 fmt.Println() 86 86 fmt.Println(header) … … 90 90 for rows.Next() { 91 91 var symbol, quote string 92 var volume, avgPrice, returnPct , volatilityfloat6492 var volume, avgPrice, returnPct float64 93 93 var tradeCount, users int64 94 if err := rows.Scan(&symbol, "e, &volume, &tradeCount, &avgPrice, &returnPct, & volatility, &users); err != nil {94 if err := rows.Scan(&symbol, "e, &volume, &tradeCount, &avgPrice, &returnPct, &users); err != nil { 95 95 fmt.Println("scan error:", err) 96 96 return 97 97 } 98 fmt.Printf(" %-6s %-5s %12.4f %8d %14.6f %+12.2f % 14.6f %8d\n",99 symbol, quote, volume, tradeCount, avgPrice, returnPct, volatility,users)98 fmt.Printf(" %-6s %-5s %12.4f %8d %14.6f %+12.2f %8d\n", 99 symbol, quote, volume, tradeCount, avgPrice, returnPct, users) 100 100 empty = false 101 101 }
Note:
See TracChangeset
for help on using the changeset viewer.
