Changes between Initial Version and Version 1 of AdvancedReports


Ignore:
Timestamp:
09/17/26 00:24:27 (10 days ago)
Author:
231285
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AdvancedReports

    v1 v1  
     1== Advanced Reports
     2
     3Both reports are implemented as single SQL statements wrapped as callable SQL
     4functions in [`schema_creation.sql`](../../server/db/schema_creation.sql):
     5
     6* `report_top_traders`
     7* `report_market_performance`
     8
     9They are exposed directly in the prototype through menu options `[10]` and `[11]`
     10in `server/reports.go`.
     11
     12== Report 1: Top traders by realized performance
     13
     14=== Description
     15
     16The purpose of this report is to determine which users achieved the strongest
     17trading performance over a selected time period.
     18
     19For a given interval `[from, to)`, the report calculates:
     20
     21* **Realized P/L** — the sum of `buy`, `sell` and `fee` transaction amounts.
     22  Deposits are excluded because they are not trading results.
     23* **Total invested** — the absolute value of the user's buy amounts.
     24* **ROI %** — realized P/L divided by total invested, multiplied by 100.
     25* **Profitable periods** — number of calendar quarters with positive P/L.
     26* **Losing periods** — number of calendar quarters with negative P/L.
     27* **Total periods** — number of quarters containing any trading activity.
     28* **Consistency %** — profitable quarters divided by total active quarters,
     29  multiplied by 100.
     30
     31Quarterly grouping is performed internally even when the requested date range
     32covers several years. This makes it possible to distinguish a user with one
     33large profitable period from a user whose results are consistently positive
     34across several periods.
     35
     36The report is ordered by realized P/L in descending order.
     37
     38=== SQL implementation
     39
     40Implemented as `project.report_top_traders(p_from, p_to)` in
     41[`schema_creation.sql`](../../server/db/schema_creation.sql).
     42
     43## [source,sql]
     44
     45CREATE OR REPLACE FUNCTION project.report_top_traders(
     46p_from timestamptz,
     47p_to   timestamptz
     48)
     49RETURNS TABLE (
     50username            varchar,
     51realized_pl         numeric,
     52total_invested      numeric,
     53roi_pct             numeric,
     54profitable_periods  bigint,
     55losing_periods      bigint,
     56total_periods       bigint,
     57consistency_pct     numeric
     58)
     59LANGUAGE sql STABLE AS $$
     60WITH period_pl AS (
     61SELECT
     62t.user_id,
     63date_trunc('quarter', t.created_at) AS period,
     64SUM(t.amount) AS period_pl,
     65SUM(t.amount) FILTER (
     66WHERE t.type = 'buy'
     67) AS period_buy
     68FROM project.transactions t
     69WHERE t.type IN ('buy', 'sell', 'fee')
     70AND t.created_at >= p_from
     71AND t.created_at <  p_to
     72GROUP BY
     73t.user_id,
     74date_trunc('quarter', t.created_at)
     75)
     76SELECT
     77u.username,
     78
     79```
     80    SUM(pp.period_pl) AS realized_pl,
     81
     82    ABS(SUM(pp.period_buy)) AS total_invested,
     83
     84    ROUND(
     85        SUM(pp.period_pl)
     86        / NULLIF(ABS(SUM(pp.period_buy)), 0) * 100,
     87        2
     88    ) AS roi_pct,
     89
     90    COUNT(*) FILTER (
     91        WHERE pp.period_pl > 0
     92    ) AS profitable_periods,
     93
     94    COUNT(*) FILTER (
     95        WHERE pp.period_pl < 0
     96    ) AS losing_periods,
     97
     98    COUNT(*) AS total_periods,
     99
     100    ROUND(
     101        COUNT(*) FILTER (
     102            WHERE pp.period_pl > 0
     103        )::numeric
     104        / NULLIF(COUNT(*), 0) * 100,
     105        2
     106    ) AS consistency_pct
     107
     108FROM period_pl pp
     109JOIN project.users u
     110    ON u.id = pp.user_id
     111
     112GROUP BY
     113    u.id,
     114    u.username
     115
     116ORDER BY
     117    realized_pl DESC;
     118```
     119
     120$$$;
     121----
     122
     123=== SQL logic
     124
     125The query consists of one CTE and one outer `SELECT`.
     126
     127The `period_pl` CTE first filters the transaction history to the requested
     128interval and keeps only `buy`, `sell` and `fee` transactions. Transactions are
     129then grouped by user and calendar quarter.
     130
     131For each quarter it calculates:
     132
     133- the total P/L for that quarter;
     134- the total value of buy transactions.
     135
     136The outer query combines those quarterly results into user-level totals and
     137derives ROI and consistency.
     138
     139`NULLIF` prevents division by zero for users who have no invested amount, while
     140the `FILTER` clauses allow profitable and losing quarters to be counted
     141independently.
     142
     143== Report 2: Market performance leaderboard
     144
     145=== Description
     146
     147The purpose of this report is to compare the performance and activity of all
     148markets over a selected time interval.
     149
     150For each market, the report calculates:
     151
     152- **Total volume** — sum of traded quantity.
     153- **Trade count** — number of market trades.
     154- **Average price** — average trade price.
     155- **Market return %** — percentage change from the first trade price in the
     156  period to the last trade price.
     157- **Price volatility** — sample standard deviation of trade prices.
     158- **Participating users** — number of distinct users with executed orders on the
     159  market during the period.
     160
     161`market_trades` is used for volume, trade count, average price and price
     162movement because it is the source of executed market activity.
     163
     164`orders` is used for participating users because `market_trades` intentionally
     165does not contain a `user_id`; it records both user-generated fills and simulated
     166market activity.
     167
     168The result is ordered by total traded volume in descending order.
     169
     170=== SQL implementation
     171
     172Implemented as `project.report_market_performance(p_from, p_to)` in
     173[`schema_creation.sql`](../../server/db/schema_creation.sql).
     174
     175[source,sql]
     176----
     177CREATE OR REPLACE FUNCTION project.report_market_performance(
     178    p_from timestamptz,
     179    p_to   timestamptz
     180)
     181RETURNS TABLE (
     182    symbol               varchar,
     183    quote_currency       char(3),
     184    total_volume         numeric,
     185    trade_count          bigint,
     186    avg_price            numeric,
     187    market_return_pct    numeric,
     188    price_volatility     numeric,
     189    participating_users  bigint
     190)
     191LANGUAGE sql STABLE AS $$
     192    WITH trades AS (
     193        SELECT
     194            market_id,
     195            price,
     196            quantity,
     197            executed_at,
     198
     199            FIRST_VALUE(price) OVER w AS first_price,
     200
     201            LAST_VALUE(price) OVER (
     202                PARTITION BY market_id
     203                ORDER BY executed_at
     204                ROWS BETWEEN UNBOUNDED PRECEDING
     205                         AND UNBOUNDED FOLLOWING
     206            ) AS last_price
     207
     208        FROM project.market_trades
     209
     210        WHERE executed_at >= p_from
     211          AND executed_at <  p_to
     212
     213        WINDOW w AS (
     214            PARTITION BY market_id
     215            ORDER BY executed_at
     216        )
     217    ),
     218
     219    market_stats AS (
     220        SELECT
     221            market_id,
     222            SUM(quantity) AS total_volume,
     223            COUNT(*) AS trade_count,
     224            AVG(price) AS avg_price,
     225            STDDEV(price) AS price_volatility,
     226            MAX(first_price) AS first_price,
     227            MAX(last_price) AS last_price
     228        FROM trades
     229        GROUP BY market_id
     230    ),
     231
     232    participation AS (
     233        SELECT
     234            market_id,
     235            COUNT(DISTINCT user_id) AS participating_users
     236        FROM project.orders
     237        WHERE status = 'executed'
     238          AND executed_at >= p_from
     239          AND executed_at <  p_to
     240        GROUP BY market_id
     241    )
     242
     243    SELECT
     244        c.symbol,
     245        m.quote_currency,
     246
     247        ms.total_volume,
     248        ms.trade_count,
     249
     250        ROUND(ms.avg_price, 6) AS avg_price,
     251
     252        ROUND(
     253            (ms.last_price - ms.first_price)
     254            / NULLIF(ms.first_price, 0) * 100,
     255            2
     256        ) AS market_return_pct,
     257
     258        ROUND(
     259            COALESCE(ms.price_volatility, 0),
     260            6
     261        ) AS price_volatility,
     262
     263        COALESCE(
     264            p.participating_users,
     265            0
     266        ) AS participating_users
     267
     268    FROM market_stats ms
     269
     270    JOIN project.markets m
     271        ON m.id = ms.market_id
     272
     273    JOIN project.crypto c
     274        ON c.id = m.crypto_id
     275
     276    LEFT JOIN participation p
     277        ON p.market_id = ms.market_id
     278
     279    ORDER BY
     280        ms.total_volume DESC;
     281$$;
     282----
     283
     284=== SQL logic
     285
     286The `trades` CTE restricts `market_trades` to the requested time interval and
     287uses window functions to identify the first and last trade price for each
     288market.
     289
     290The `market_stats` CTE aggregates those trades into one row per market and
     291calculates volume, trade count, average price and price volatility.
     292
     293The `participation` CTE independently counts distinct users with executed
     294orders. This must be calculated from `orders` because simulated
     295`market_trades` do not belong to individual users.
     296
     297The final query joins the calculated statistics with `markets` and `crypto` to
     298obtain the market symbol and quote currency.
     299
     300A `LEFT JOIN` is used for participation so that a market with trades but no
     301executed user orders still appears in the report with `participating_users = 0`.
     302
     303== Implementation summary
     304
     305Both reports are implemented as callable SQL functions and therefore execute
     306as part of the actual database-backed prototype rather than existing only as
     307documentation.
     308
     309The two functions are:
     310
     311- `project.report_top_traders(p_from, p_to)`
     312- `project.report_market_performance(p_from, p_to)`
     313
     314They are exposed through:
     315
     316- `[10] Report: top traders`
     317- `[11] Report: market performance`
     318
     319No additional database objects or schema changes were necessary because all
     320required information already exists in the relational model.
     321$$$