Ignore:
Timestamp:
09/16/26 23:37:15 (13 days ago)
Author:
Stefan <trsunovstefan@…>
Branches:
main
Children:
8b447ef
Parents:
df05838
Message:

add reserved_quantity and modify the phases, add v_03.png and v_03.xml for P1

File:
1 edited

Legend:

Unmodified
Added
Removed
  • server/db/schema_creation.sql

    rdf05838 r9577c79  
    5959-- ============================================================================
    6060CREATE 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),
    6570    -- Weighted-average entry price. NOT NULL so that the P/L arithmetic in
    6671    -- 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,
    7075    CONSTRAINT uq_holdings_user_crypto UNIQUE (user_id, crypto_id)
    7176);
    … …  
    184189       c.symbol,
    185190       h.quantity,
     191       h.reserved_quantity,
     192       (h.quantity - h.reserved_quantity) AS available_quantity,
    186193       h.avg_price,
    187194       lp.price                           AS current_price,
    … …  
    192199LEFT   JOIN project.markets m ON m.crypto_id = c.id AND m.quote_currency = 'USD'
    193200LEFT   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.
     211CREATE OR REPLACE FUNCTION project.report_top_traders(p_from timestamptz, p_to timestamptz)
     212RETURNS 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)
     222LANGUAGE 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.
     255CREATE OR REPLACE FUNCTION project.report_market_performance(p_from timestamptz, p_to timestamptz)
     256RETURNS 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)
     266LANGUAGE 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.