Tagee-xyz / tagee

Web3的数据城市
16 stars 8 forks source link

uniswap流动性套利分析的数据表设计 #15

Open zhangmaosen opened 2 years ago

zhangmaosen commented 2 years ago

纬度:时间、钱包地址、币对儿 Metric:tvl,成交量、流动性佣金提成,gas费 fyi:https://www.bitpush.news/articles/3154196

zexianghuang commented 2 years ago

tvl计算: 将以太币转换为美元价值:不同的时间,不同的价格来计算 里面核心有几块: 交易数据 trade , 价格:price,不同时间的美元价格。

--for tokens where dune doesn't have the price, calculate the avg price in it's last hour of swaps (6 hr if no swaps in the last hour) --since we don't have an amount field for unmapped tokens, I'll calc it based on... raw amount -- through a few queries, this gets the unit price of each token (assuming 18 decimals) -- DO NOT USE THIS FOR KNOWN TOKENS - only for the ones we don't have, we'll assume 18 decimals

对于价格,确实有这几种方式 1.没有价格的代币,计算过去1个小时的平均价格; 2.没有价格的话,用原始金额计算: coalesce(amount_usd/token_sold_amount, amount_usd/(token_sold_amount_raw*power(10,decimals))) AS price, 3.查询几个代币的单价作为参考
第一种算法:根据dex的价格进行计算:

WITH
  trades AS (
    SELECT
      DATE_TRUNC('day', t.block_time) AS dt,
      project,
      version,
      CASE
        WHEN version = '1' THEN project
        ELSE project || ' V' || version || ' - ' || (ROUND(p.fee / 1e4, 2)) || '%'
      END as proj_v,
      t.project_contract_address as exchange_contract_address,
      t.token_bought_address,
      t.token_sold_address,
      t.token_bought_symbol,
      t.token_sold_symbol,
      t.token_bought_symbol || '/' || t.token_sold_symbol AS pair,
      t.token_bought_symbol || '/' || t.token_sold_symbol || ' - ' || (ROUND(p.fee / 1e4, 2))  || '%' AS pool,
      SUM(t.token_bought_amount) AS token_bought_amount,
      SUM(t.token_sold_amount) AS token_sold_amount,
      SUM(t.amount_usd) AS usd_traded,
      COUNT(t.amount_usd) AS num_trades,
      COUNT(DISTINCT t.taker) AS num_users
    FROM
      uniswap_v3_ethereum.trades t
      INNER JOIN uniswap_v3_ethereum.Factory_evt_PoolCreated p ON t.project_contract_address = p.pool
    WHERE
      t.block_date >= '2021-05-05'
      and t.project_contract_address in (
        '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8',
        '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',
        '0x7bea39867e4169dbe237d55c8242a8f2fcdcc387'
      ) 
    GROUP BY
      1,
      2,
      3,
      4,
      5,
      6,
      7,
      8,
      9,
      10,
      11
  ),
  prices AS (
    SELECT
      *
    FROM
      prices.usd p
    WHERE
      p.minute > '2021-05-05'
      AND (
        contract_address IN (
          SELECT
            token_sold_address
          FROM
            trades
          GROUP BY
            1
        )
        OR contract_address IN (
          SELECT
            token_bought_address
          FROM
            trades
          GROUP BY
            1
        )
      )
  ),
  projects AS (
    SELECT
      DISTINCT exchange_contract_address,
      proj_v,
      pair,
      pool,
      token_bought_address,
      token_sold_address,
      token_bought_symbol,
      token_sold_symbol
    FROM
      trades
  ),
  gs AS (
    SELECT
      DISTINCT d.token_bought_address AS token,
      gs.hour
    FROM
      trades d
      INNER JOIN (
        SELECT
          explode(sequence(to_date('2021-05-05'), date_trunc('day', now()), interval 1 day)) as hour
      ) gs -- Generate all days since the first contract
      ON 1 = 1
    UNION
    SELECT
      DISTINCT d.token_sold_address AS token,
      gs.hour
    FROM
      trades d
      INNER JOIN (
        SELECT
          explode(sequence(to_date('2021-05-05'), date_trunc('day', now()), interval 1 day)) as hour
      ) gs -- Generate all days since the first contract
      ON 1 = 1
  ),
  tokens_in_prices_usd AS (
        SELECT DISTINCT contract_address
        FROM prices.usd
        WHERE minute > now() - interval 10 minutes
    ),
  dex_trades as(
        SELECT
            token_bought_address as contract_address,
            coalesce(amount_usd/token_bought_amount, amount_usd/(token_bought_amount_raw*power(1/10,decimals))) AS price,
            block_time
        FROM dex.trades
        LEFT JOIN tokens.erc20 ON contract_address = token_bought_address
        WHERE 1=1
        AND amount_usd  > 0
        AND token_bought_amount_raw > 0
        AND token_bought_address NOT IN (SELECT contract_address FROM tokens_in_prices_usd)

        UNION ALL

        SELECT
            token_sold_address as contract_address,
            coalesce(amount_usd/token_sold_amount, amount_usd/(token_sold_amount_raw*power(1/10,decimals))) AS price,
            block_time
        FROM dex.trades
        LEFT JOIN tokens.erc20 ON contract_address = token_sold_address
        WHERE 1=1
        AND amount_usd  > 0
        AND token_sold_amount_raw > 0
        AND token_sold_address NOT IN (SELECT contract_address FROM tokens_in_prices_usd)
  ),
  view_token_prices as (
    SELECT
        date_trunc('hour', block_time) as hour,
        contract_address,
        (PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY price)) AS median_price,
        count(1) AS sample_size
    FROM dex_trades
    GROUP BY 1, 2

  ),
  dex_price AS(
    --for tokens where dune doesn't have the price, calculate the avg price in it's last hour of swaps (6 hr if no swaps in the last hour)
    --since we don't have an amount field for unmapped tokens, I'll calc it based on... raw amount
    -- through a few queries, this gets the unit price of each token (assuming 18 decimals)
    -- DO NOT USE THIS FOR KNOWN TOKENS - only for the ones we don't have, we'll assume 18 decimals
    SELECT
      gs.hour,
      gs.token,
      symbol,
      decimals,
      median_price
    FROM
      (
        SELECT
          *,
          lead(hour, 1, NOW()) OVER (
            PARTITION BY token
            ORDER BY
              hour asc
          ) AS next_hour
        FROM
          (
            SELECT
              DATE_TRUNC('day', hour) AS hour,
              t.contract_address AS token,
              ea.symbol AS symbol,
              ea.decimals AS decimals,
              percentile_cont(0.5) WITHIN GROUP (
                ORDER BY
                  median_price
              ) AS median_price
            FROM
              view_token_prices t
              INNER JOIN (
                SELECT
                  token
                FROM
                  gs
                GROUP BY
                  1
              ) c ON t.contract_address = c.token
              INNER JOIN tokens.erc20 ea --both need to have known decimals, we're not going to assume anything.
              ON ea.contract_address = t.contract_address
            WHERE
              (ea.decimals IS NOT NULL)
              AND median_price > 0
              AND DATE_TRUNC('day', hour) > '2021-05-05' 
              AND DATE_TRUNC('day', hour) <= NOW() + interval 1 day
            GROUP BY
              1,
              2,
              3,
              4
          ) b
      ) c
      INNER JOIN gs ON c.hour <= gs.hour
      AND gs.hour < c.next_hour
      AND gs.token = c.token   
  ),
  tvl AS (
    SELECT
      DISTINCT dt,
      exchange_contract_address,
      SUM(delta_tvl) OVER (PARTITION BY dt, exchange_contract_address) AS delta_tvl,
      SUM(add_tvl) OVER (PARTITION BY dt, exchange_contract_address) AS add_tvl,
      LAST_VALUE(running_tvl) OVER (
        PARTITION BY dt,
        exchange_contract_address
        ORDER BY
          hour  ASC ROWS BETWEEN UNBOUNDED PRECEDING
          AND UNBOUNDED FOLLOWING
      ) AS running_tvl
    FROM
      (
        SELECT
          hour,
          DATE_TRUNC('day', hour) AS dt,
          exchange_contract_address,
          COALESCE(SUM(val_usd), 0) AS delta_tvl,
          COALESCE(SUM(addval_usd), 0) AS add_tvl,
          COALESCE(SUM(sumval_usd), 0) AS running_tvl
        FROM
          (
            SELECT
              gs.hour AS hour,
              gs.token,
              exchange_contract_address,
              CASE
                WHEN gs.hour = c.hour THEN val
                ELSE 0
              END AS val,
              CASE
                WHEN gs.hour = c.hour THEN val*power(1/10,COALESCE(p.decimals, dp.decimals))
                ELSE 0
              END AS val_tokens,
              CASE
                WHEN gs.hour = c.hour THEN COALESCE(p.price, dp.median_price) * (val *power(1/10, COALESCE(p.decimals, dp.decimals)))
                ELSE 0
              END AS val_usd,
              CASE
                WHEN gs.hour = c.hour THEN addval
                ELSE 0
              END AS addval,
              CASE
                WHEN gs.hour = c.hour THEN addval * power(1/10 ,COALESCE(p.decimals, dp.decimals))
                ELSE 0
              END AS addval_tokens,
              CASE
                WHEN gs.hour = c.hour THEN COALESCE(p.price, dp.median_price) * (addval  * power(1/10,COALESCE(p.decimals, dp.decimals)))
                ELSE 0
              END AS addval_usd,
              sumval,
              sumval * power(1/10,COALESCE(p.decimals, dp.decimals)) AS sumval_tokens,
              COALESCE(p.price, dp.median_price) * (sumval * power(1/10,COALESCE(p.decimals, dp.decimals))) AS sumval_usd
            FROM
              (
                SELECT
                  *,
                  lead(hour, 1, NOW()) OVER (
                    PARTITION BY contract_address,
                    exchange_contract_address
                    ORDER BY
                      hour asc
                  ) AS next_hour,
                  SUM(val) OVER (
                    PARTITION BY contract_address,
                    exchange_contract_address
                    ORDER BY
                      hour ASC
                  ) AS sumval
                FROM
                  (
                    SELECT
                      hour,
                      contract_address,
                      exchange_contract_address,
                      SUM(val) AS val,
                      SUM(
                        CASE
                          WHEN val > 0 THEN val
                          ELSE 0
                        END
                      ) AS addval --SUM(val) OVER (PARTITION BY contract_address, exchange_contract_address ORDER BY hour ASC) AS sumval
                      --SUM( price*(val/(10^decimals)) ) AS usd_tvl
                    FROM
                      (
                        SELECT
                          DATE_TRUNC('day', e.evt_block_time) AS hour,
                          to AS exchange_contract_address,
                          e.contract_address,
                          SUM(value) AS val
                        FROM
                          erc20_ethereum.evt_Transfer e
                        WHERE
                          to IN (
                            SELECT
                              exchange_contract_address
                            FROM
                              trades
                            GROUP BY
                              1
                          )
                        GROUP BY
                          1,
                          2,
                          3
                        UNION ALL
                        SELECT
                          DATE_TRUNC('day', eb.evt_block_time) AS hour,
                          from AS exchange_contract_address,
                          eb.contract_address,
                          SUM(- value) AS val
                        FROM
                          erc20_ethereum.evt_Transfer eb
                        WHERE
                          from IN (
                            SELECT
                              exchange_contract_address
                            FROM
                              trades
                            GROUP BY
                              1
                          )
                        GROUP BY
                          1,
                          2,
                          3
                      ) tr
                    GROUP BY
                      1,
                      2,
                      3
                  ) mod
              ) c
              INNER JOIN gs ON c.hour <= gs.hour
              AND gs.hour < c.next_hour
              AND gs.token = c.contract_address
              LEFT JOIN prices p ON p.contract_address = gs.token
              AND CASE
                WHEN DATE_TRUNC('day', gs.hour) = DATE_TRUNC('day', NOW()) THEN p.minute = DATE_TRUNC('day', NOW())
                ELSE p.minute = DATE_TRUNC('day', gs.hour) + interval 1 day
              END
              LEFT JOIN dex_price dp ON gs.token = dp.token
              AND dp.hour = gs.hour
              AND p.price IS NULL
            WHERE
              gs.hour > '2021-05-05'
          ) a
        GROUP BY
          1,
          2,
          3
      ) b
  )
SELECT
  tvl.dt,
  --pr.proj_v,
  COALESCE(tr.usd_traded, 0) AS usd_traded,
  tvl.running_tvl,
  COALESCE(tr.num_trades, 0) AS num_trades,
  tvl.delta_tvl,
  tvl.add_tvl,
  '<a href=https://info.uniswap.org/#/pools/' || REPLACE(
    tvl.exchange_contract_address,
    '\\',' 0 ') || ' >' || REPLACE(tvl.exchange_contract_address,'\\','0') || '</a>' AS pool_url,

 CASE WHEN tvl.exchange_contract_address = '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8' THEN '0.30%'
 WHEN tvl.exchange_contract_address = '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640' THEN '0.05%'
 WHEN tvl.exchange_contract_address = '0x7bea39867e4169dbe237d55c8242a8f2fcdcc387' THEN '1%' END AS uni_pool,
SUM(usd_traded) OVER (PARTITION BY tvl.exchange_contract_address ORDER BY tvl.dt ASC) AS running_usd,
SUM(num_trades) OVER (PARTITION BY tvl.exchange_contract_address ORDER BY tvl.dt ASC) AS running_trades,
 pr.pair,
 pr.pool,
 pr.token_bought_address, 
 pr.token_sold_address, 
 pr.token_bought_symbol, 
 pr.token_sold_symbol

FROM tvl
INNER JOIN projects pr
ON pr.exchange_contract_address = tvl.exchange_contract_address
LEFT JOIN trades tr
ON tr.exchange_contract_address = tvl.exchange_contract_address
AND tr.dt = tvl.dt

ORDER BY dt DESC --this is important for counters

第二种算法:根据trades中的 amount_usd进行计算

WITH
  trades as (  -- uniswap_v3 trades data 
      select
       block_date as dt
      ,project
      ,version
      ,project_contract_address
      ,token_bought_address
      ,token_sold_address
      ,token_bought_symbol
      ,token_sold_symbol
      ,token_pair as pair
      ,sum(token_bought_amount) as token_bought_amount
      ,sum(token_sold_amount) as token_b_amount
      ,sum(amount_usd) as usd_traded
      ,count(amount_usd) as num_trades
      ,count(distinct taker) as num_users
    from uniswap_v3_ethereum.trades
    where  block_date >= '2021-05-05'
      and project_contract_address in (
        '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8',
        '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',
        '0x7bea39867e4169dbe237d55c8242a8f2fcdcc387'
      ) 
    group by
       block_date
      ,project
      ,version
      ,project_contract_address
      ,token_bought_address
      ,token_sold_address
      ,token_bought_symbol
      ,token_sold_symbol
      ,token_pair
  ) ,
  dex_price as(
      select 
              date_trunc('hour', block_time) as hour,
              contract_address,
              decimals,
              symbol,
              (PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY price)) AS median_price,
              count(1) AS sample_size
      from 
      (

          select
              t1.token_bought_address as contract_address
              ,t2.decimals
              ,t2.symbol
              ,coalesce(t1.amount_usd/t1.token_bought_amount, t1.amount_usd/(t1.token_bought_amount_raw*power(10,t2.decimals))) as price
              ,t1.block_time
          from (
                  select 
                          block_time
                         ,token_bought_address
                         ,token_bought_amount
                         ,amount_usd
                         ,token_bought_amount_raw
                  from dex.trades
                  where amount_usd>0  and token_bought_amount_raw > 0
          ) t1
          left join 
          (
                select 
                     blockchain
                    ,contract_address
                    ,decimals
                    ,symbol
               from  tokens.erc20
          ) t2 
          on(t1.token_bought_address = t2.contract_address)
          left join 
          (
                  select distinct contract_address
                  from prices.usd
                  where minute > now() - interval '10 minutes'
          )t3
          on(t1.token_bought_address = t3.contract_address)
          where t3.contract_address is null

          union all

          select
              t4.token_sold_address as contract_address,
              t5.decimals,
              t5.symbol,
              coalesce(t4.amount_usd/t4.token_sold_amount, t4.amount_usd/(t4.token_sold_amount_raw*power(10,t5.decimals))) as price,
              t4.block_time
          from (
                  select 
                          block_time
                         ,token_sold_address
                         ,token_sold_amount
                         ,amount_usd
                         ,token_sold_amount_raw
                  from dex.trades
                  where amount_usd>0  and token_sold_amount_raw > 0
          ) t4
          left join 
          (
                select 
                     blockchain
                    ,contract_address
                    ,decimals
                    ,symbol
               from  tokens.erc20
          ) t5
          on(t4.token_sold_address = t5.contract_address)
          left join 
          (
                  select distinct contract_address
                  from prices.usd
                  where minute > now() - interval '10 minutes'
          )t6
          on(t4.token_sold_address = t6.contract_address)
          where t6.contract_address is  null
      ) t7
      group by date_trunc('hour', block_time),contract_address,decimals,symbol

  ),
  tvl as (
select 
          dt,
          exchange_contract_address,
          sum(delta_tvl) over (partition by dt, exchange_contract_address) as delta_tvl,
          sum(add_tvl) over (partition by dt, exchange_contract_address) AS add_tvl,
          last_value(running_tvl) over (partition by  dt,exchange_contract_address
           order by hour  asc rows between unbounded preceding and unbounded following) as running_tvl
    from (
          select 
                    hour,
                    DATE_TRUNC('day', hour) as dt,
                    exchange_contract_address,
                    coalesce(sum(val_usd), 0) as delta_tvl,
                    coalesce(sum(addval_usd), 0) as add_tvl,
                    coalesce(sum(sumval_usd), 0) as running_tvl

          from( 
             -- 根据不同时间 不同的价格计算 以太币转换为  美元价值
              select
                            hour,
                            exchange_contract_address,
                            val,
                            0 as val_tokens,
                            val_usd,
                            addval,
                            0 as addval_tokens,
                            addval_usd,
                            sumval,
                            0 as sumval_tokens,
                            sumval_usd
              from( 
                   select 
                          hour,
                          contract_address,
                          exchange_contract_address,
                          val,
                          addval, -- 交易流入
                          val_usd,
                          addval_usd,
                          lead(hour, 1, now()) over (partition by contract_address,exchange_contract_address order by hour asc) as next_hour,
                          sum(val) over (partition by contract_address,exchange_contract_address order by hour asc) as sumval,  -- 累计求和值 
                          sum(val_usd) over (partition by contract_address,exchange_contract_address order by hour asc) as sumval_usd
                  from (
                    select
                          t1.hour,
                          t1.contract_address,
                          t1.exchange_contract_address,
                          sum(t1.val) as val,
                          sum(case when t1.val > 0 then t1.val else 0 end) as addval,
                          sum(t1.amount_usd) as val_usd,
                          sum(case when t1.amount_usd > 0 then t1.amount_usd else 0 end) as addval_usd
                    from
                    (
                      select
                           DATE_TRUNC('day', block_time) as hour,
                            tx_to as exchange_contract_address,
                            project_contract_address as contract_address,
                            sum(amount_usd) as val,
                            sum(amount_usd) as amount_usd
                      from uniswap_v3_ethereum.trades
                      where  block_date >= '2021-05-05'
                      and project_contract_address in (
                         '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8',
                         '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',
                         '0x7bea39867e4169dbe237d55c8242a8f2fcdcc387'
                         ) 
                        group by DATE_TRUNC('day', block_time), tx_to,contract_address

                    ) t1
                    group by t1.hour,
                             t1.exchange_contract_address,
                             t1.contract_address

                    union all 

                    select
                          t3.hour,
                          t3.contract_address,
                          t3.exchange_contract_address,
                          sum(t3.val) AS val,
                          sum(case when t3.val > 0 then t3.val else 0 end) as addval,
                          sum(t3.amount_usd) AS val_usd,
                          sum(case when t3.amount_usd > 0 then t3.amount_usd else 0 end) as addval_usd
                    from
                    (
                      select
                            DATE_TRUNC('day', block_time) as hour,
                            tx_from as exchange_contract_address,
                            project_contract_address as contract_address,
                            sum(-amount_usd) as val,
                            sum(-amount_usd) as amount_usd
                      from uniswap_v3_ethereum.trades
                      where  block_date >= '2021-05-05'
                      and project_contract_address in (
                         '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8',
                         '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',
                         '0x7bea39867e4169dbe237d55c8242a8f2fcdcc387'
                         ) 
                        group by DATE_TRUNC('day', block_time), tx_from,contract_address
                  ) t3
                    group by t3.hour,
                             t3.exchange_contract_address,
                             t3.contract_address
                )t4
              )t5
          ) t16
            group by  hour,
            DATE_TRUNC('day', hour),
            exchange_contract_address
    )t17 
  )

select
   tvl.dt
  ,coalesce(trades.usd_traded, 0) as usd_traded
  ,tvl.running_tvl
  ,coalesce(trades.num_trades, 0) as num_trades
  ,tvl.delta_tvl
  ,tvl.add_tvl
  ,trades.pair
  ,trades.token_bought_address
  ,trades.token_sold_address
  ,trades.token_bought_symbol
  ,trades.token_sold_symbol
  ,case when tvl.exchange_contract_address = '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8' then '0.30%'
        when tvl.exchange_contract_address = '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640' then '0.05%'
        when tvl.exchange_contract_address = '0x7bea39867e4169dbe237d55c8242a8f2fcdcc387' then '1%' end as uni_pool
  ,sum(trades.usd_traded) over (partition by tvl.exchange_contract_address order by tvl.dt asc) as running_usd
  ,sum(trades.num_trades) over (partition by tvl.exchange_contract_address order by tvl.dt asc) as running_trades
from tvl
left join trades 
on trades.project_contract_address = tvl.exchange_contract_address
and trades.dt = tvl.dt
order by dt desc 
zexianghuang commented 2 years ago

fee 流动性佣金提成 计算:trades.amount_usd * (poolCreated.fee / 1e6) as fees_collected_usd


select
       block_date
      ,sum(fees_collected_usd) as fees
      ,sum(protocol_buySell * (markout5m - swapPrice) * eth_swapped) as pnl_5m
      ,sum(protocol_buySell * (markout24h - swapPrice) * eth_swapped) as pnl_24h
      ,sum(protocol_buySell * (markout1h - swapPrice) * eth_swapped) as pnl_1h
      ,sum(case when protocol_buySell * (markout5m - swapPrice) * eth_swapped >= 0 then swapPrice * eth_swapped else 0 end) as pnl_5m_positive
      ,sum(case when protocol_buySell * (markout5m - swapPrice) * eth_swapped < 0 then swapPrice * eth_swapped else 0 end) as pnl_5m_negative
      ,sum(case when protocol_buySell * (markout1h - swapPrice) * eth_swapped >= 0 then swapPrice * eth_swapped else 0 end) as pnl_1h_positive
      ,sum(case when protocol_buySell * (markout1h - swapPrice) * eth_swapped < 0 then swapPrice * eth_swapped else 0 end) as pnl_1h_negative
      ,sum(swapPrice * eth_swapped) as volume
from (
      select 
                 trades.block_time   
                ,trades.block_date
                ,trades.project_contract_address   -- exchange_contract_address is the v3 LP Position
                ,trades.tx_hash  -- transaction addr (for spot checks)
                ,trades.amount_usd 
                ,trades.swapPrice
                ,trades.protocol_buySell
                ,trades.markout1h
                ,trades.markout5m
                ,trades.markout24h
                ,trades.eth_swapped
                ,poolCreated.fee / 1e6 as fee_tier
                ,trades.amount_usd * (poolCreated.fee / 1e6) as fees_collected_usd

      from 
      (
          select
                 block_time
                ,block_date   
                ,project_contract_address   -- exchange_contract_address is the v3 LP Position
                ,tx_hash  -- transaction addr (for spot checks)
                ,amount_usd 
                ,case  when token_sold_symbol = 'USDC' then token_sold_amount / token_bought_amount
                       when token_sold_symbol = 'WETH' then token_bought_amount / token_sold_amount
                       end as swapPrice
                ,case when token_sold_symbol = 'WETH' then 1 when token_sold_symbol = 'USDC' then -1
                      end as protocol_buySell
                ,last_value( case  when token_sold_symbol = 'USDC' then token_sold_amount / token_bought_amount
                                  when token_sold_symbol = 'WETH' then token_bought_amount / token_sold_amount end
                           ) over (order by  block_time asc range between current row and interval '1' hour following
                 ) as markout1h
                ,last_value(
                  case  when token_sold_symbol = 'USDC' then token_sold_amount / token_bought_amount
                        when token_sold_symbol = 'WETH' then token_bought_amount / token_sold_amount
                  end) over (order by block_time asc range between current row and interval '5' minutes following
                ) as markout5m
                ,last_value(
                  case  when token_sold_symbol = 'USDC' then token_sold_amount / token_bought_amount
                        when token_sold_symbol = 'WETH' then token_bought_amount / token_sold_amount
                  end) OVER (order by block_time asc range between current row  and interval '24' hours following
                ) as markout24h
                ,case
                  when token_sold_symbol = 'WETH' then token_sold_amount
                  when token_bought_symbol = 'WETH' then token_bought_amount
                  else 0
                  end as eth_swapped
          from uniswap_v3_ethereum.trades
          where  block_date >= '2021-05-05'
            and (token_bought_address = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
                and token_sold_address = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
                or (token_bought_address = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
                    and token_sold_address = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
                  )
                )
            and case when token_sold_symbol = 'USDC' then token_sold_amount / token_bought_amount
                     when token_sold_symbol = 'WETH' then token_bought_amount / token_sold_amount
            end between 500 and 5000
      ) trades
      left join 
      (
        select
            contract_address,
            evt_block_number,
            evt_block_time,
            evt_index,
            evt_tx_hash,
            fee,
            pool,
            tickSpacing,
            token0,
            token1
        from uniswap_v3_ethereum.Factory_evt_PoolCreated
      ) poolCreated
      on(trades.project_contract_address = poolCreated.pool)
) t1
group by block_date
order by block_date desc 
zhangmaosen commented 2 years ago

https://zhuanlan.zhihu.com/p/426860108 这里面提到了对于价格处理的三种办法,我估计我们要出三个变种计算逻辑了

zexianghuang @.***>于2022年10月12日 周三20:33写道:

tvl计算: select t2.block_date ,t2.hour ,t2.project_contract_address ,t2.exchange_contract_address ,COALESCE(sum(t2.amount_usd), 0) AS delta_tvl ,COALESCE(sum(t2.addval_usd), 0) AS add_tvl ,COALESCE(sum(t2.sumval_usd), 0) AS running_tvl from ( select t1.block_date ,t1.hour ,t1.project_contract_address ,t1.exchange_contract_address ,t1.amount_usd ,case when t1.amount_usd >0 then t1.amount_usd else 0 end as addval_usd ,sum(amount_usd) OVER ( PARTITION BY project_contract_address,exchange_contract_address ORDER BY hour ASC ) AS sumval_usd from ( select block_date ,DATE_TRUNC('day',block_time) AS hour ,project_contract_address ,tx_to as exchange_contract_address ,sum(amount_usd) as amount_usd from uniswap_v3_ethereum.trades group by block_date,DATE_TRUNC('day',block_time),project_contract_address,tx_to

union all

select

    block_date

   ,DATE_TRUNC('day',block_time) AS hour

   ,project_contract_address

   ,tx_from as exchange_contract_address

   ,sum(-amount_usd) as amount_usd

from uniswap_v3_ethereum.trades

group by block_date,DATE_TRUNC('day',block_time),project_contract_address,tx_from

) t1

)t2 group by t2.block_date ,t2.hour ,t2.project_contract_address ,t2.exchange_contract_address

— Reply to this email directly, view it on GitHub https://github.com/Tagee-xyz/tagee/issues/15#issuecomment-1276089103, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAH7YM3ELVKQ7R2NN4TGHFTWC2VZZANCNFSM6AAAAAARB3S7L4 . You are receiving this because you were assigned.Message ID: @.***>

zexianghuang commented 2 years ago

https://zhuanlan.zhihu.com/p/426860108 这里面提到了对于价格处理的三种办法,我估计我们要出三个变种计算逻辑了 zexianghuang @.>于2022年10月12日 周三20:33写道: tvl计算: select t2.block_date ,t2.hour ,t2.project_contract_address ,t2.exchange_contract_address ,COALESCE(sum(t2.amount_usd), 0) AS delta_tvl ,COALESCE(sum(t2.addval_usd), 0) AS add_tvl ,COALESCE(sum(t2.sumval_usd), 0) AS running_tvl from ( select t1.block_date ,t1.hour ,t1.project_contract_address ,t1.exchange_contract_address ,t1.amount_usd ,case when t1.amount_usd >0 then t1.amount_usd else 0 end as addval_usd ,sum(amount_usd) OVER ( PARTITION BY project_contract_address,exchange_contract_address ORDER BY hour ASC ) AS sumval_usd from ( select block_date ,DATE_TRUNC('day',block_time) AS hour ,project_contract_address ,tx_to as exchange_contract_address ,sum(amount_usd) as amount_usd from uniswap_v3_ethereum.trades group by block_date,DATE_TRUNC('day',block_time),project_contract_address,tx_to union all select block_date ,DATE_TRUNC('day',block_time) AS hour ,project_contract_address ,tx_from as exchange_contract_address ,sum(-amount_usd) as amount_usd from uniswap_v3_ethereum.trades group by block_date,DATE_TRUNC('day',block_time),project_contract_address,tx_from ) t1 )t2 group by t2.block_date ,t2.hour ,t2.project_contract_address ,t2.exchange_contract_address — Reply to this email directly, view it on GitHub <#15 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAH7YM3ELVKQ7R2NN4TGHFTWC2VZZANCNFSM6AAAAAARB3S7L4 . You are receiving this because you were assigned.Message ID: @.>

--for tokens where dune doesn't have the price, calculate the avg price in it's last hour of swaps (6 hr if no swaps in the last hour)
--since we don't have an amount field for unmapped tokens, I'll calc it based on... raw amount
-- through a few queries, this gets the unit price of each token (assuming 18 decimals)
-- DO NOT USE THIS FOR KNOWN TOKENS - only for the ones we don't have, we'll assume 18 decimals

对于价格,确实有这几种方式 1.没有价格的代币,计算过去1个小时的平均价格; 2.没有价格的话,用原始金额计算: coalesce(amount_usd/token_sold_amount, amount_usd/(token_sold_amount_raw*power(10,decimals))) AS price, 3.查询几个代币的单价作为参考

zexianghuang commented 2 years ago

明细数据查询:

      select 
                 trades.block_time   
                ,trades.block_date
                ,trades.project_contract_address   -- exchange_contract_address is the v3 LP Position
                ,trades.tx_hash  -- transaction addr (for spot checks)
                ,trades.amount_usd 
                ,trades.swapPrice
                ,trades.eth_swapped
                ,poolCreated.fee / 1e6 as fee_tier
                ,trades.amount_usd * (poolCreated.fee / 1e6) as fees_collected_usd
                ,gas.tx_fee_native
                ,gas.tx_fee_usd
                ,(trades.swapPrice * trades.eth_swapped)as volume
                ,trades.token_sold_symbol
                ,trades.token_bought_symbol
                ,trades.token_bought_amount
                ,trades.token_sold_amount
                ,trades.token_bought_address
                ,trades.token_sold_address

      from  (
          select
                 block_time
                ,block_date   
                ,project_contract_address   -- exchange_contract_address is the v3 LP Position
                ,tx_hash  -- transaction addr (for spot checks)
                ,amount_usd 
                ,case  when token_sold_symbol = 'USDC' then token_sold_amount / token_bought_amount
                       when token_sold_symbol = 'WETH' then token_bought_amount / token_sold_amount
                       end as swapPrice
                ,case
                  when token_sold_symbol = 'WETH' then token_sold_amount
                  when token_bought_symbol = 'WETH' then token_bought_amount
                  else 0
                  end as eth_swapped
                ,token_sold_symbol
                ,token_bought_symbol
                ,token_bought_amount
                ,token_sold_amount
                ,token_bought_address
                ,token_sold_address
          from uniswap_v3_ethereum.trades
          where  block_date >= '2021-05-05'
            and (token_bought_address = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
                and token_sold_address = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
                or (token_bought_address = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
                    and token_sold_address = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
                  )
                )
            and case when token_sold_symbol = 'USDC' then token_sold_amount / token_bought_amount
                     when token_sold_symbol = 'WETH' then token_bought_amount / token_sold_amount
            end between 500 and 5000
      )trades
      left join 
      (
        select
            contract_address,
            evt_block_number,
            evt_block_time,
            evt_index,
            evt_tx_hash,
            fee,
            pool,
            tickSpacing,
            token0,
            token1
        from uniswap_v3_ethereum.Factory_evt_PoolCreated
      ) poolCreated
      on(trades.project_contract_address = poolCreated.pool)
      left join 
      (
        select  
             tx_hash
            ,tx_fee_native
            ,tx_fee_usd
        from gas.fees 
      ) gas
      on(trades.tx_hash = gas.tx_hash)
zexianghuang commented 2 years ago

汇总统计:

WITH
  trades AS (
    SELECT
      DATE_TRUNC('day', t.block_time) AS dt,
      t.block_time,
      project,
      version,
      CASE
        WHEN version = '1' THEN project
        ELSE project || ' V' || version || ' - ' || (ROUND(p.fee / 1e4, 2)) || '%'
      END as proj_v,
      t.project_contract_address as exchange_contract_address,
      t.token_bought_address,
      t.token_sold_address,
      t.token_bought_symbol,
      t.token_sold_symbol,
      t.token_bought_symbol || '/' || t.token_sold_symbol AS pair,
      t.token_bought_symbol || '/' || t.token_sold_symbol || ' - ' || (ROUND(p.fee / 1e4, 2))  || '%' AS pool,
      SUM(t.token_bought_amount) AS token_bought_amount,
      SUM(t.token_sold_amount) AS token_sold_amount,
      SUM(t.amount_usd) AS usd_traded,
      COUNT(t.amount_usd) AS num_trades,
      COUNT(DISTINCT t.taker) AS num_users,
      SUM(t.amount_usd * (p.fee / 1e6)) as fees_collected_usd,
      sum(case  when token_sold_symbol = 'USDC' then token_sold_amount / token_bought_amount
                when token_sold_symbol = 'WETH' then token_bought_amount / token_sold_amount
                end 
          * 
          case when token_sold_symbol = 'WETH' then token_sold_amount
               when token_bought_symbol = 'WETH' then token_bought_amount
               else 0 end 
          ) as volume
      --SUM(tx_fee_native) as gas_fee_native,
      ---SUM(tx_fee_usd) as gas_fee_usd
    FROM
      uniswap_v3_ethereum.trades t
      INNER JOIN uniswap_v3_ethereum.Factory_evt_PoolCreated p ON t.project_contract_address = p.pool
    WHERE
      t.block_date >= '2021-05-05'
      and t.project_contract_address in (
        '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8',
        '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',
        '0x7bea39867e4169dbe237d55c8242a8f2fcdcc387'
      ) 
    GROUP BY
      1,
      2,
      3,
      4,
      5,
      6,
      7,
      8,
      9,
      10,
      11,
      12
  ),
  prices AS (
    SELECT
      *
    FROM
      prices.usd p
    WHERE
      p.minute > '2021-05-05'
      AND (
        contract_address IN (
          SELECT
            token_sold_address
          FROM
            trades
          GROUP BY
            1
        )
        OR contract_address IN (
          SELECT
            token_bought_address
          FROM
            trades
          GROUP BY
            1
        )
      )
  ),
  projects AS (
    SELECT
      DISTINCT exchange_contract_address,
      proj_v,
      pair,
      pool,
      token_bought_address,
      token_sold_address,
      token_bought_symbol,
      token_sold_symbol
    FROM
      trades
  ),
  gs AS (
    SELECT
      DISTINCT d.token_bought_address AS token,
      gs.hour
    FROM
      trades d
      INNER JOIN (
        SELECT
          explode(sequence(to_date('2021-05-05'), date_trunc('day', now()), interval 1 day)) as hour
      ) gs -- Generate all days since the first contract
      ON 1 = 1
    UNION
    SELECT
      DISTINCT d.token_sold_address AS token,
      gs.hour
    FROM
      trades d
      INNER JOIN (
        SELECT
          explode(sequence(to_date('2021-05-05'), date_trunc('day', now()), interval 1 day)) as hour
      ) gs -- Generate all days since the first contract
      ON 1 = 1
  ),
  tokens_in_prices_usd AS (
        SELECT DISTINCT contract_address
        FROM prices.usd
        WHERE minute > now() - interval 10 minutes
    ),
  dex_trades as(
        SELECT
            token_bought_address as contract_address,
            coalesce(amount_usd/token_bought_amount, amount_usd/(token_bought_amount_raw*power(1/10,decimals))) AS price,
            block_time
        FROM dex.trades
        LEFT JOIN tokens.erc20 ON contract_address = token_bought_address
        WHERE 1=1
        AND amount_usd  > 0
        AND token_bought_amount_raw > 0
        AND token_bought_address NOT IN (SELECT contract_address FROM tokens_in_prices_usd)

        UNION ALL

        SELECT
            token_sold_address as contract_address,
            coalesce(amount_usd/token_sold_amount, amount_usd/(token_sold_amount_raw*power(1/10,decimals))) AS price,
            block_time
        FROM dex.trades
        LEFT JOIN tokens.erc20 ON contract_address = token_sold_address
        WHERE 1=1
        AND amount_usd  > 0
        AND token_sold_amount_raw > 0
        AND token_sold_address NOT IN (SELECT contract_address FROM tokens_in_prices_usd)
  ),
  view_token_prices as (
    SELECT
        date_trunc('hour', block_time) as hour,
        contract_address,
        (PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY price)) AS median_price,
        count(1) AS sample_size
    FROM dex_trades
    GROUP BY 1, 2

  ),
  dex_price AS(
    --for tokens where dune doesn't have the price, calculate the avg price in it's last hour of swaps (6 hr if no swaps in the last hour)
    --since we don't have an amount field for unmapped tokens, I'll calc it based on... raw amount
    -- through a few queries, this gets the unit price of each token (assuming 18 decimals)
    -- DO NOT USE THIS FOR KNOWN TOKENS - only for the ones we don't have, we'll assume 18 decimals
    SELECT
      gs.hour,
      gs.token,
      symbol,
      decimals,
      median_price
    FROM
      (
        SELECT
          *,
          lead(hour, 1, NOW()) OVER (
            PARTITION BY token
            ORDER BY
              hour asc
          ) AS next_hour
        FROM
          (
            SELECT
              DATE_TRUNC('day', hour) AS hour,
              t.contract_address AS token,
              ea.symbol AS symbol,
              ea.decimals AS decimals,
              percentile_cont(0.5) WITHIN GROUP (
                ORDER BY
                  median_price
              ) AS median_price
            FROM
              view_token_prices t
              INNER JOIN (
                SELECT
                  token
                FROM
                  gs
                GROUP BY
                  1
              ) c ON t.contract_address = c.token
              INNER JOIN tokens.erc20 ea --both need to have known decimals, we're not going to assume anything.
              ON ea.contract_address = t.contract_address
            WHERE
              (ea.decimals IS NOT NULL)
              AND median_price > 0
              AND DATE_TRUNC('day', hour) > '2021-05-05' 
              AND DATE_TRUNC('day', hour) <= NOW() + interval 1 day
            GROUP BY
              1,
              2,
              3,
              4
          ) b
      ) c
      INNER JOIN gs ON c.hour <= gs.hour
      AND gs.hour < c.next_hour
      AND gs.token = c.token   
  ),
  tvl AS (
    SELECT
      DISTINCT dt,
      evt_block_time as block_time,
      exchange_contract_address,
      SUM(delta_tvl) OVER (PARTITION BY dt, exchange_contract_address) AS delta_tvl,
      SUM(add_tvl) OVER (PARTITION BY dt, exchange_contract_address) AS add_tvl,
      LAST_VALUE(running_tvl) OVER (
        PARTITION BY dt,
        exchange_contract_address
        ORDER BY
          hour  ASC ROWS BETWEEN UNBOUNDED PRECEDING
          AND UNBOUNDED FOLLOWING
      ) AS running_tvl
    FROM
      (
        SELECT
          hour,
          DATE_TRUNC('day', hour) AS dt,
          evt_block_time,
          exchange_contract_address,
          COALESCE(SUM(val_usd), 0) AS delta_tvl,
          COALESCE(SUM(addval_usd), 0) AS add_tvl,
          COALESCE(SUM(sumval_usd), 0) AS running_tvl
        FROM
          (
            SELECT
              gs.hour AS hour,
              gs.token,
              evt_block_time,
              exchange_contract_address,
              CASE
                WHEN gs.hour = c.hour THEN val
                ELSE 0
              END AS val,
              CASE
                WHEN gs.hour = c.hour THEN val*power(1/10,COALESCE(p.decimals, dp.decimals))
                ELSE 0
              END AS val_tokens,
              CASE
                WHEN gs.hour = c.hour THEN COALESCE(p.price, dp.median_price) * (val *power(1/10, COALESCE(p.decimals, dp.decimals)))
                ELSE 0
              END AS val_usd,
              CASE
                WHEN gs.hour = c.hour THEN addval
                ELSE 0
              END AS addval,
              CASE
                WHEN gs.hour = c.hour THEN addval * power(1/10 ,COALESCE(p.decimals, dp.decimals))
                ELSE 0
              END AS addval_tokens,
              CASE
                WHEN gs.hour = c.hour THEN COALESCE(p.price, dp.median_price) * (addval  * power(1/10,COALESCE(p.decimals, dp.decimals)))
                ELSE 0
              END AS addval_usd,
              sumval,
              sumval * power(1/10,COALESCE(p.decimals, dp.decimals)) AS sumval_tokens,
              COALESCE(p.price, dp.median_price) * (sumval * power(1/10,COALESCE(p.decimals, dp.decimals))) AS sumval_usd
            FROM
              (
                SELECT
                  *,
                  lead(hour, 1, NOW()) OVER (
                    PARTITION BY contract_address,
                    exchange_contract_address
                    ORDER BY
                      hour asc
                  ) AS next_hour,
                  SUM(val) OVER (
                    PARTITION BY contract_address,
                    exchange_contract_address
                    ORDER BY
                      hour ASC
                  ) AS sumval
                FROM
                  (
                    SELECT
                      hour,
                      evt_block_time,
                      contract_address,
                      exchange_contract_address,
                      SUM(val) AS val,
                      SUM(
                        CASE
                          WHEN val > 0 THEN val
                          ELSE 0
                        END
                      ) AS addval --SUM(val) OVER (PARTITION BY contract_address, exchange_contract_address ORDER BY hour ASC) AS sumval
                      --SUM( price*(val/(10^decimals)) ) AS usd_tvl
                    FROM
                      (
                        SELECT
                          DATE_TRUNC('day', e.evt_block_time) AS hour,
                          e.evt_block_time,
                          to AS exchange_contract_address,
                          e.contract_address,
                          SUM(value) AS val
                        FROM
                          erc20_ethereum.evt_Transfer e
                        WHERE
                          to IN (
                            SELECT
                              exchange_contract_address
                            FROM
                              trades
                            GROUP BY
                              1
                          )
                        GROUP BY
                          1,
                          2,
                          3,
                          4
                        UNION ALL
                        SELECT
                          DATE_TRUNC('day', eb.evt_block_time) AS hour,
                          eb.evt_block_time,
                          from AS exchange_contract_address,
                          eb.contract_address,
                          SUM(- value) AS val
                        FROM
                          erc20_ethereum.evt_Transfer eb
                        WHERE
                          from IN (
                            SELECT
                              exchange_contract_address
                            FROM
                              trades
                            GROUP BY
                              1
                          )
                        GROUP BY
                          1,
                          2,
                          3,
                          4
                      ) tr
                    GROUP BY
                      1,
                      2,
                      3,
                      4
                  ) mod
              ) c
              INNER JOIN gs ON c.hour <= gs.hour
              AND gs.hour < c.next_hour
              AND gs.token = c.contract_address
              LEFT JOIN prices p ON p.contract_address = gs.token
              AND CASE
                WHEN DATE_TRUNC('day', gs.hour) = DATE_TRUNC('day', NOW()) THEN p.minute = DATE_TRUNC('day', NOW())
                ELSE p.minute = DATE_TRUNC('day', gs.hour) + interval 1 day
              END
              LEFT JOIN dex_price dp ON gs.token = dp.token
              AND dp.hour = gs.hour
              AND p.price IS NULL
            WHERE
              gs.hour > '2021-05-05'
          ) a
        GROUP BY
          1,
          2,
          3,
          4
      ) b
  )
SELECT
   tvl.block_time,
   pr.pair,
   pr.pool,
   tvl.exchange_contract_address,
   pr.token_bought_address, 
   pr.token_sold_address, 
   pr.token_bought_symbol, 
   pr.token_sold_symbol,
 CASE WHEN tvl.exchange_contract_address = '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8' THEN '0.30%'
 WHEN tvl.exchange_contract_address = '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640' THEN '0.05%'
 WHEN tvl.exchange_contract_address = '0x7bea39867e4169dbe237d55c8242a8f2fcdcc387' THEN '1%' END AS uni_pool,
  COALESCE(tr.usd_traded, 0) AS usd_traded,
  tvl.running_tvl,
  COALESCE(tr.num_trades, 0) AS num_trades,
  tvl.delta_tvl,
  tvl.add_tvl,
  tr.fees_collected_usd,
  tr.volume,
  SUM(tr.usd_traded) OVER (PARTITION BY tvl.exchange_contract_address ORDER BY tvl.dt ASC) AS running_usd,
  SUM(tr.num_trades) OVER (PARTITION BY tvl.exchange_contract_address ORDER BY tvl.dt ASC) AS running_trades
FROM tvl
INNER JOIN projects pr
ON pr.exchange_contract_address = tvl.exchange_contract_address
LEFT JOIN trades tr
ON tr.exchange_contract_address = tvl.exchange_contract_address
AND tr.dt = tvl.dt
ORDER BY dt DESC --this is important for counters