Changeset 9577c79 for server/db/schema_creation.sql
- Timestamp:
- 09/16/26 23:37:15 (13 days ago)
- Branches:
- main
- Children:
- 8b447ef
- Parents:
- df05838
- File:
-
- 1 edited
-
server/db/schema_creation.sql (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
server/db/schema_creation.sql
rdf05838 r9577c79 59 59 -- ============================================================================ 60 60 CREATE TABLE project.holdings ( 61 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), 62 user_id uuid NOT NULL REFERENCES project.users(id) ON DELETE CASCADE, 63 crypto_id uuid NOT NULL REFERENCES project.crypto(id), 64 quantity numeric(20,4) NOT NULL CHECK (quantity >= 0), 61 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), 62 user_id uuid NOT NULL REFERENCES project.users(id) ON DELETE CASCADE, 63 crypto_id uuid NOT NULL REFERENCES project.crypto(id), 64 quantity numeric(20,4) NOT NULL CHECK (quantity >= 0), 65 -- Committed to the user's own open sell orders, not yet removed from the 66 -- position. quantity - reserved_quantity is what is actually free to 67 -- sell — the crypto-side equivalent of users.available_balance. 68 reserved_quantity numeric(20,4) NOT NULL DEFAULT 0 69 CHECK (reserved_quantity >= 0 AND reserved_quantity <= quantity), 65 70 -- Weighted-average entry price. NOT NULL so that the P/L arithmetic in 66 71 -- v_portfolio can never silently produce NULL for an existing position. 67 avg_price numeric(18,6) NOT NULL DEFAULT 0 CHECK (avg_price >= 0),68 created_at timestamptz NOT NULL DEFAULT now(),69 updated_at timestamptz,72 avg_price numeric(18,6) NOT NULL DEFAULT 0 CHECK (avg_price >= 0), 73 created_at timestamptz NOT NULL DEFAULT now(), 74 updated_at timestamptz, 70 75 CONSTRAINT uq_holdings_user_crypto UNIQUE (user_id, crypto_id) 71 76 ); … … 184 189 c.symbol, 185 190 h.quantity, 191 h.reserved_quantity, 192 (h.quantity - h.reserved_quantity) AS available_quantity, 186 193 h.avg_price, 187 194 lp.price AS current_price, … … 192 199 LEFT JOIN project.markets m ON m.crypto_id = c.id AND m.quote_currency = 'USD' 193 200 LEFT JOIN project.v_latest_prices lp ON lp.market_id = m.id; 201 202 -- ============================================================================ 203 -- REPORTS (P6 — Complex DB Reports) 204 -- Both are single SELECT statements (with CTEs), wrapped as SQL functions so 205 -- they can be called as parameterised reports from the prototype instead of 206 -- being copy-pasted SQL text. See docs/P6-AdvancedReports/AdvancedReports.md. 207 -- ============================================================================ 208 209 -- report_top_traders: realized trading performance per user over [p_from, p_to), 210 -- bucketed into quarters to measure how consistently each user was profitable. 211 CREATE OR REPLACE FUNCTION project.report_top_traders(p_from timestamptz, p_to timestamptz) 212 RETURNS TABLE ( 213 username varchar, 214 realized_pl numeric, 215 total_invested numeric, 216 roi_pct numeric, 217 profitable_periods bigint, 218 losing_periods bigint, 219 total_periods bigint, 220 consistency_pct numeric 221 ) 222 LANGUAGE sql STABLE AS $$ 223 WITH period_pl AS ( 224 SELECT 225 t.user_id, 226 date_trunc('quarter', t.created_at) AS period, 227 SUM(t.amount) AS period_pl, 228 SUM(t.amount) FILTER (WHERE t.type = 'buy') AS period_buy 229 FROM project.transactions t 230 WHERE t.type IN ('buy', 'sell', 'fee') 231 AND t.created_at >= p_from 232 AND t.created_at < p_to 233 GROUP BY t.user_id, date_trunc('quarter', t.created_at) 234 ) 235 SELECT 236 u.username, 237 SUM(pp.period_pl) AS realized_pl, 238 ABS(SUM(pp.period_buy)) AS total_invested, 239 ROUND(SUM(pp.period_pl) / NULLIF(ABS(SUM(pp.period_buy)), 0) * 100, 2) AS roi_pct, 240 COUNT(*) FILTER (WHERE pp.period_pl > 0) AS profitable_periods, 241 COUNT(*) FILTER (WHERE pp.period_pl < 0) AS losing_periods, 242 COUNT(*) AS total_periods, 243 ROUND(COUNT(*) FILTER (WHERE pp.period_pl > 0)::numeric 244 / NULLIF(COUNT(*), 0) * 100, 2) AS consistency_pct 245 FROM period_pl pp 246 JOIN project.users u ON u.id = pp.user_id 247 GROUP BY u.id, u.username 248 ORDER BY realized_pl DESC; 249 $$; 250 251 -- report_market_performance: trading activity and price behaviour per market 252 -- over [p_from, p_to). Volume/trade-count/price stats come from market_trades 253 -- (the complete tape — user fills and simulated fills alike); participating 254 -- users can only come from orders, since market_trades has no user_id column. 255 CREATE OR REPLACE FUNCTION project.report_market_performance(p_from timestamptz, p_to timestamptz) 256 RETURNS TABLE ( 257 symbol varchar, 258 quote_currency char(3), 259 total_volume numeric, 260 trade_count bigint, 261 avg_price numeric, 262 market_return_pct numeric, 263 price_volatility numeric, 264 participating_users bigint 265 ) 266 LANGUAGE sql STABLE AS $$ 267 WITH trades AS ( 268 SELECT 269 market_id, price, quantity, executed_at, 270 FIRST_VALUE(price) OVER w AS first_price, 271 LAST_VALUE(price) OVER (PARTITION BY market_id ORDER BY executed_at 272 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_price 273 FROM project.market_trades 274 WHERE executed_at >= p_from AND executed_at < p_to 275 WINDOW w AS (PARTITION BY market_id ORDER BY executed_at) 276 ), 277 market_stats AS ( 278 SELECT 279 market_id, 280 SUM(quantity) AS total_volume, 281 COUNT(*) AS trade_count, 282 AVG(price) AS avg_price, 283 STDDEV(price) AS price_volatility, 284 MAX(first_price) AS first_price, 285 MAX(last_price) AS last_price 286 FROM trades 287 GROUP BY market_id 288 ), 289 participation AS ( 290 SELECT market_id, COUNT(DISTINCT user_id) AS participating_users 291 FROM project.orders 292 WHERE status = 'executed' AND executed_at >= p_from AND executed_at < p_to 293 GROUP BY market_id 294 ) 295 SELECT 296 c.symbol, 297 m.quote_currency, 298 ms.total_volume, 299 ms.trade_count, 300 ROUND(ms.avg_price, 6) AS avg_price, 301 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 COALESCE(p.participating_users, 0) AS participating_users 304 FROM market_stats ms 305 JOIN project.markets m ON m.id = ms.market_id 306 JOIN project.crypto c ON c.id = m.crypto_id 307 LEFT JOIN participation p ON p.market_id = ms.market_id 308 ORDER BY ms.total_volume DESC; 309 $$;
Note:
See TracChangeset
for help on using the changeset viewer.
