mirror of
https://github.com/FlipsideCrypto/ethusdc-retroactive.git
synced 2026-02-06 10:47:07 +00:00
Duplicated for ETHWBTC - Initial Summary Tables Identified
This commit is contained in:
parent
fa0cbd0e17
commit
0abfe4a5df
@ -154,7 +154,6 @@ saveRDS(filled_eth_prices, "eth_prices.rds")
|
||||
```{r, message = FALSE, warning = FALSE}
|
||||
library(gmp) # large numbers
|
||||
library(reactable) # clean tables
|
||||
library(plotly) # graphs
|
||||
library(dplyr) # data manipulation
|
||||
options(scipen = 10)
|
||||
|
||||
@ -1049,7 +1048,7 @@ id_fees_fixed <- lapply(1:length(id_fees_list),
|
||||
})
|
||||
|
||||
# cleanup
|
||||
rm(closed_lp_actions, closed_fees, closed_lp_actions, id_fees_list)
|
||||
rm(closed_lp_actions, closed_fees, id_fees_list)
|
||||
|
||||
|
||||
```
|
||||
@ -1059,12 +1058,6 @@ rm(closed_lp_actions, closed_fees, closed_lp_actions, id_fees_list)
|
||||
Now with the fixed fee list organized by unique IDs and lp_actions list organized by unique IDs
|
||||
we can create `r length(id_fees_fixed)` accounting tables.
|
||||
|
||||
```{r}
|
||||
# fix this
|
||||
accounting(id_lp_actions = id_lp_list[[68]],id_fees = id_fees_fixed[[68]])
|
||||
|
||||
```
|
||||
|
||||
```{r}
|
||||
|
||||
id_accounting <- lapply(1:length(id_fees_fixed),
|
||||
@ -1107,7 +1100,7 @@ strategy_results <- bind_rows(id_strat, .id = "unique_id")
|
||||
ethusdc_results <- merge(pnl_results, hodl_results, by = "unique_id")
|
||||
ethusdc_results <- merge(ethusdc_results, strategy_results, by = "unique_id")
|
||||
|
||||
saveRDS(ethusdc_results)
|
||||
saveRDS(ethusdc_results, file = "ethusdc_results.rds")
|
||||
|
||||
reactable(
|
||||
ethusdc_results[1:20, ]
|
||||
|
||||
91
ethbtc/collect_ethbtc_data.R
Normal file
91
ethbtc/collect_ethbtc_data.R
Normal file
@ -0,0 +1,91 @@
|
||||
library(shroomDK)
|
||||
library(zoo) # infill NAs & rolling Median
|
||||
source("../key_functions.R")
|
||||
# LP Actions
|
||||
|
||||
|
||||
|
||||
ethbtc_lp_actions <- auto_paginate_query(
|
||||
query = "
|
||||
SELECT
|
||||
BLOCK_NUMBER, BLOCK_TIMESTAMP,
|
||||
TX_HASH, ACTION,
|
||||
NF_TOKEN_ID,
|
||||
AMOUNT0_ADJUSTED, AMOUNT1_ADJUSTED,
|
||||
LIQUIDITY,
|
||||
TOKEN0_SYMBOL, TOKEN1_SYMBOL,
|
||||
TICK_LOWER, TICK_UPPER,
|
||||
PRICE_LOWER_0_1, PRICE_UPPER_0_1,
|
||||
LIQUIDITY_PROVIDER,
|
||||
NF_POSITION_MANAGER_ADDRESS
|
||||
FROM ethereum.uniswapv3.ez_lp_actions
|
||||
WHERE POOL_ADDRESS = '0xcbcdf9626bc03e24f779434178a73a0b4bad62ed' AND
|
||||
BLOCK_NUMBER <= 15576600
|
||||
ORDER BY BLOCK_NUMBER DESC
|
||||
",
|
||||
api_key = readLines("api_key.txt")
|
||||
)
|
||||
|
||||
|
||||
# Collected Fees
|
||||
# Known Issue where Closure of positions mix withdrawn tokens as if they were collected fees
|
||||
# subtraction will be sorted out
|
||||
ethbtc_fees <- auto_paginate_query(
|
||||
query = "
|
||||
SELECT
|
||||
BLOCK_NUMBER, BLOCK_TIMESTAMP,
|
||||
TX_HASH, NF_TOKEN_ID,
|
||||
AMOUNT0_ADJUSTED, AMOUNT1_ADJUSTED,
|
||||
TICK_LOWER, TICK_UPPER,
|
||||
PRICE_LOWER, PRICE_UPPER,
|
||||
LIQUIDITY_PROVIDER,
|
||||
NF_POSITION_MANAGER_ADDRESS
|
||||
FROM ethereum.uniswapv3.ez_position_collected_fees
|
||||
WHERE POOL_ADDRESS = '0xcbcdf9626bc03e24f779434178a73a0b4bad62ed' AND
|
||||
BLOCK_NUMBER <= 15576600
|
||||
ORDER BY BLOCK_NUMBER DESC
|
||||
",
|
||||
api_key = readLines("api_key.txt")
|
||||
)
|
||||
|
||||
# Historical BTC prices at all LP_ACTIONS blocks
|
||||
|
||||
min_block = min(ethbtc_lp_actions$BLOCK_NUMBER) - 101
|
||||
|
||||
blocks = c(min_block, min_block + 1e6,
|
||||
min_block + 2e6, min_block + 3e6,
|
||||
min_block + 4e6)
|
||||
|
||||
ethbtc_price <- list()
|
||||
for(i in 1:length(blocks)){
|
||||
ethbtc_price[[i]] <- get_ethbtc_price(min_block = blocks[i],
|
||||
max_block = blocks[i] + 1e6,
|
||||
api_key = readLines('api_key.txt'))
|
||||
|
||||
}
|
||||
|
||||
all_ethbtc_prices <- do.call(rbind, ethbtc_price)
|
||||
all_ethbtc_prices <- all_ethbtc_prices[order(all_ethbtc_prices$BLOCK_NUMBER),]
|
||||
|
||||
# if a block has no trades, infill the BLOCK_NUMBER and persist the most recent
|
||||
# ETH Weighted Average Price, with 0 VOLUME and 0 NUM_SWAPS
|
||||
infill <- data.frame(
|
||||
BLOCK_NUMBER = min(all_ethbtc_prices$BLOCK_NUMBER):max(all_ethbtc_prices$BLOCK_NUMBER)
|
||||
)
|
||||
|
||||
filled_ethbtc_prices <- merge(all_ethbtc_prices, infill, all.x = TRUE, all.y = TRUE)
|
||||
|
||||
filled_ethbtc_prices[is.na(filled_ethbtc_prices$"BTC_VOLUME"), c("BTC_VOLUME","NUM_SWAPS")] <- 0
|
||||
|
||||
# Improves analysis speed to front-load these calculations and is more smoothed
|
||||
filled_ethbtc_prices$BTC_WAVG_PRICE <- zoo::na.locf(filled_ethbtc_prices$BTC_WAVG_PRICE)
|
||||
BTC_MARKET_PRICE <- zoo::rollmedian(x = filled_ethbtc_prices$BTC_WAVG_PRICE, k = 99, align = "left")
|
||||
diff_median <- nrow(filled_ethbtc_prices) - length(BTC_MARKET_PRICE)
|
||||
BTC_MARKET_PRICE <- c(filled_ethbtc_prices$BTC_WAVG_PRICE[1:diff_median], BTC_MARKET_PRICE)
|
||||
|
||||
filled_ethbtc_prices$BTC_MARKET_PRICE <- BTC_MARKET_PRICE
|
||||
|
||||
# R Save Format
|
||||
saveRDS(ethbtc_lp_actions, "ethbtc_lp_actions.rds")
|
||||
saveRDS(ethbtc_fees, "ethbtc_fees.rds")
|
||||
saveRDS(filled_ethbtc_prices, "ethbtc_prices.rds")
|
||||
286
ethbtc/ethbtc_retroactive.Rmd
Normal file
286
ethbtc/ethbtc_retroactive.Rmd
Normal file
@ -0,0 +1,286 @@
|
||||
---
|
||||
title: "eth_btc_retroactive"
|
||||
author: "Charliemarketplace"
|
||||
date: "`r Sys.Date()`"
|
||||
output:
|
||||
html_document:
|
||||
code_folding: hide
|
||||
toc: yes
|
||||
toc_float:
|
||||
collapsed: false
|
||||
editor_options:
|
||||
chunk_output_type: console
|
||||
---
|
||||
|
||||
# Intro
|
||||
|
||||
This is a Profit and Loss calculation including potential divergent loss (DL) of all closed positions in the ETH-WBTC 0.3% Uniswap v3 pool from inception of the pool up to the block height of 15,576,600 (September 20, 2022). It reviews *all* changes in liquidity and accumulated fees to identify the profit and loss of every unique position across 3 accountings:
|
||||
|
||||
- Direct Profit & Loss, accounting for price changes in the underlying assets
|
||||
- Opportunity Cost, i.e., HODL Reference Value: what assets deposited would have been worth as of the block where the position was closed.
|
||||
- Realized Value, i.e., Strategy Reference Value: what the assets withdrawn were worth as of the block where the position was closed including accumulated fees.
|
||||
|
||||
Direct P&L is useful for understanding gains in the context of changing prices, here, P&L is measured in both ETH and BTC terms.
|
||||
|
||||
Divergent Loss (sometimes called Impermanent Loss) is the difference between the results of using a strategy (here, fees and automatic market making in Uniswap v3) and the counter factual: the amount of assets if one were *not* to have used the strategy.
|
||||
A more in depth view on definitions and calculations is available in the ETH USDC Retroactive over the same time range.
|
||||
|
||||
# Data Collection
|
||||
|
||||
The `collect_ethbtc_data.R` script included in this repo uses the Flipside Crypto's shroomDK API to pull all the relevant data. For brevity, this markdown reads from a saved RDS copy *not* available in the repo. To reproduce this analysis, you can run the `collect_ethbtc_data.R` script using your own shroomDK API key available for free.
|
||||
|
||||
```{r}
|
||||
library(gmp) # large numbers
|
||||
library(reactable) # clean tables
|
||||
library(dplyr) # data manipulation
|
||||
options(scipen = 10)
|
||||
|
||||
source("../key_functions.R")
|
||||
|
||||
lp_actions <- readRDS("ethbtc_lp_actions.rds")
|
||||
|
||||
# remove actions that don't remove liquidity, these are fee collection events
|
||||
lp_actions <- lp_actions %>% filter(LIQUIDITY != 0)
|
||||
|
||||
fees <- readRDS("ethbtc_fees.rds")
|
||||
btc_prices <- readRDS("ethbtc_prices.rds")
|
||||
btc_prices <- btc_prices[,c("BLOCK_NUMBER", "BTC_MARKET_PRICE")]
|
||||
colnames(btc_prices) <- c("BLOCK_NUMBER","btc_price")
|
||||
|
||||
```
|
||||
|
||||
### Closed Positions at Block Height
|
||||
|
||||
```{r}
|
||||
lp_actions$unique_id <- lp_actions$NF_TOKEN_ID
|
||||
|
||||
custom_index <- which(is.na(lp_actions$unique_id))
|
||||
|
||||
lp_actions[custom_index, "unique_id"] <- paste0(lp_actions[custom_index, "NF_POSITION_MANAGER_ADDRESS"],
|
||||
"--",
|
||||
lp_actions[custom_index, "TICK_LOWER"],
|
||||
"--",
|
||||
lp_actions[custom_index, "TICK_UPPER"])
|
||||
|
||||
# adding BTC Market Price from btc_prices to lp_actions via merge for later
|
||||
lp_actions <- merge(lp_actions, y = btc_prices, all.x = TRUE, all.y = FALSE, by = "BLOCK_NUMBER")
|
||||
|
||||
# manual insertion of first price
|
||||
lp_actions$btc_price[is.na(lp_actions$btc_price)] <- 16.07226
|
||||
|
||||
# while here do fees too
|
||||
fees$unique_id <- fees$NF_TOKEN_ID
|
||||
custom_index <- which(is.na(fees$unique_id))
|
||||
fees[custom_index,"unique_id"] <- paste0(fees[custom_index, "NF_POSITION_MANAGER_ADDRESS"],
|
||||
"--",
|
||||
fees[custom_index, "TICK_LOWER"],
|
||||
"--",
|
||||
fees[custom_index, "TICK_UPPER"])
|
||||
|
||||
# also while here, ensure only 1 fee collection row per tx_hash, i.e., just add them
|
||||
|
||||
fees <- fees %>%
|
||||
group_by(across(c(-AMOUNT0_ADJUSTED, -AMOUNT1_ADJUSTED))) %>%
|
||||
summarise(
|
||||
AMOUNT0_ADJUSTED = sum(AMOUNT0_ADJUSTED),
|
||||
AMOUNT1_ADJUSTED = sum(AMOUNT1_ADJUSTED), .groups = "drop") %>%
|
||||
relocate(c(NF_TOKEN_ID, AMOUNT0_ADJUSTED, AMOUNT1_ADJUSTED), .after = NF_TOKEN_ID) %>%
|
||||
as.data.frame()
|
||||
|
||||
|
||||
liquidity_at_timestamp <- lp_actions %>% mutate(
|
||||
liquidity_signed = ifelse(ACTION == "DECREASE_LIQUIDITY", LIQUIDITY * -1, LIQUIDITY)
|
||||
) %>% group_by(unique_id) %>%
|
||||
summarise(sumliq = sum(liquidity_signed))
|
||||
|
||||
# due to some large number precision errors, some liquidity may be technically negative
|
||||
# but effectively 0 for our purposes.
|
||||
closed_positions <- liquidity_at_timestamp %>% filter(sumliq <= 0)
|
||||
|
||||
```
|
||||
|
||||
There are `r nrow(closed_positions)` ETH-BTC 0.3% closed positions, including vaults, as of Sept 20, 2022.
|
||||
|
||||
## Functions
|
||||
|
||||
```{r}
|
||||
price_col = "btc_price"
|
||||
|
||||
pnl <- function(id_accounting, t0col, t1col, price_col, price_base = "t1/t0"){
|
||||
|
||||
# allow user to choose the base that is easier to read
|
||||
# e.g., usdc/eth may be easier to read than eth/usdc; which means t0/t1 instead of t1/t0
|
||||
# while ETH/BTC may be easier than BTC/ETH; which is t1/t0 the default!
|
||||
|
||||
if(price_base == 't1/t0'){
|
||||
pnl = data.frame(
|
||||
pnl_t0_terms = sum(id_accounting$token0, (id_accounting$token1/id_accounting[[price_col]])),
|
||||
pnl_t1_terms = sum(id_accounting$token1, (id_accounting$token0*id_accounting[[price_col]]))
|
||||
)
|
||||
} else if(price_base == 't0/t1'){
|
||||
pnl = data.frame(
|
||||
pnl_t0_terms = sum(id_accounting$token0, (id_accounting$token1*id_accounting[[price_col]])),
|
||||
pnl_t1_terms = sum(id_accounting$token1, (id_accounting$token0/id_accounting[[price_col]]))
|
||||
)
|
||||
} else {
|
||||
stop("price_base must be 't1/t0' (default) or 't0/t1'")
|
||||
}
|
||||
|
||||
|
||||
colnames(pnl) <- c(t0col, t1col)
|
||||
|
||||
return(pnl)
|
||||
}
|
||||
|
||||
hodl_reference <- function(id_accounting, t0col, t1col, price_col, price_base = "t1/t0"){
|
||||
|
||||
price_at_close_block = id_accounting[[price_col]][id_accounting$BLOCK_NUMBER == max(id_accounting$BLOCK_NUMBER)][1]
|
||||
|
||||
if(price_base == 't1/t0'){
|
||||
hodl <- id_accounting %>%
|
||||
filter(accounting == 'cost_basis') %>%
|
||||
summarise(
|
||||
hodl_t0_terms = abs(sum(token0) + sum(token1 / price_at_close_block)),
|
||||
hodl_t1_terms = abs(sum(token1) + sum(token0 * price_at_close_block)),
|
||||
)
|
||||
} else if(price_base == 't0/t1'){
|
||||
hodl <- id_accounting %>%
|
||||
filter(accounting == 'cost_basis') %>%
|
||||
summarise(
|
||||
hodl_t0_terms = abs(sum(token0) + sum(token1 * price_at_close_block)),
|
||||
hodl_t1_terms = abs(sum(token1) + sum(token0 / price_at_close_block)),
|
||||
)
|
||||
} else {
|
||||
stop("price_base must be 't1/t0' (default) or 't0/t1'")
|
||||
}
|
||||
|
||||
colnames(hodl) <- c(t0col, t1col)
|
||||
|
||||
return(hodl)
|
||||
}
|
||||
|
||||
strategy_reference <- function(id_accounting, t0col, t1col, price_col, price_base = "t1/t0"){
|
||||
|
||||
price_at_close_block = id_accounting[[price_col]][id_accounting$BLOCK_NUMBER == max(id_accounting$BLOCK_NUMBER)][1]
|
||||
|
||||
if(price_base == 't1/t0'){
|
||||
strat <- id_accounting %>%
|
||||
filter(accounting != 'cost_basis') %>%
|
||||
summarise(
|
||||
strategy_t0_terms = abs(sum(token0) + sum(token1 / price_at_close_block)),
|
||||
strategy_t1_terms = abs(sum(token1) + sum(token0 * price_at_close_block)),
|
||||
)
|
||||
} else if(price_base == 't0/t1'){
|
||||
strat <- id_accounting %>%
|
||||
filter(accounting != 'cost_basis') %>%
|
||||
summarise(
|
||||
strategy_t0_terms = abs(sum(token0) + sum(token1 * price_at_close_block)),
|
||||
strategy_t1_terms = abs(sum(token1) + sum(token0 / price_at_close_block)),
|
||||
)
|
||||
} else {
|
||||
stop("price_base must be 't1/t0' (default) or 't0/t1'")
|
||||
}
|
||||
|
||||
colnames(strat) <- c(t0col, t1col)
|
||||
return(strat)
|
||||
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
# Fee Correction
|
||||
|
||||
```{r}
|
||||
|
||||
closed_fees <- fees %>%
|
||||
filter(unique_id %in% closed_positions$unique_id) %>%
|
||||
arrange(BLOCK_NUMBER)
|
||||
|
||||
closed_lp_actions <- lp_actions %>%
|
||||
filter(unique_id %in% closed_positions$unique_id)
|
||||
|
||||
closed_lp_w_fees <- closed_lp_actions %>% filter(
|
||||
unique_id %in% closed_fees$unique_id
|
||||
) %>% arrange(BLOCK_NUMBER) %>%
|
||||
select(BLOCK_NUMBER, TX_HASH, unique_id, ACTION, AMOUNT0_ADJUSTED, AMOUNT1_ADJUSTED, btc_price)
|
||||
|
||||
|
||||
# split automatically alphabetizes by ID #
|
||||
id_fees_list <- split(closed_fees, f = closed_fees$unique_id)
|
||||
id_lp_list <- split(closed_lp_w_fees, f = closed_lp_w_fees$unique_id)
|
||||
|
||||
# If the order of IDs don't exactly match note the problem
|
||||
if(
|
||||
! identical(names(id_lp_list), names(id_fees_list))
|
||||
) {
|
||||
stop("List of IDs are not aligned")
|
||||
}
|
||||
|
||||
# otherwise run through the list of fees and correct them
|
||||
# NOTE fee_correction only safe to use when split by unique_id
|
||||
# Possible for a tx_hash to include multiple position interactions
|
||||
|
||||
id_fees_fixed <- lapply(1:length(id_fees_list),
|
||||
FUN = function(i){
|
||||
fee_correction(id_fees_list[[i]], id_lp_list[[i]])
|
||||
})
|
||||
```
|
||||
|
||||
### Accounting
|
||||
|
||||
```{r}
|
||||
aa <- accounting(id_lp_actions = id_lp_list[[100]],
|
||||
id_fees = id_fees_fixed[[100]],
|
||||
price_col = "btc_price")
|
||||
|
||||
pnl(aa, "pnl_btc_terms", "pnl_eth_terms","btc_price")
|
||||
hodl_reference(aa, "hodl_btc_terms", "hodl_eth_terms","btc_price")
|
||||
strategy_reference(aa, "strat_btc_terms", "strat_eth_terms","btc_price")
|
||||
|
||||
```
|
||||
|
||||
```{r}
|
||||
|
||||
id_accounting <- lapply(1:length(id_lp_list),
|
||||
FUN = function(i){
|
||||
tryCatch(
|
||||
accounting(id_lp_actions = id_lp_list[[i]],
|
||||
id_fees = id_fees_fixed[[i]],price_col = "btc_price"),
|
||||
error = function(e){
|
||||
print(paste0("Error found in position: ", i))
|
||||
})
|
||||
})
|
||||
|
||||
names(id_accounting) <- names(id_lp_list)
|
||||
|
||||
# to simplify segmentation of analysis saving this
|
||||
saveRDS(id_accounting, "post_ethbtc_accounting.rds")
|
||||
|
||||
# for brevity read id_accounting to continue here
|
||||
id_accounting <- readRDS("post_ethbtc_accounting.rds")
|
||||
|
||||
id_pnl <- lapply(id_accounting, function(i){
|
||||
pnl(i, "pnl_btc_terms", "pnl_eth_terms","btc_price")
|
||||
})
|
||||
pnl_results <- bind_rows(id_pnl, .id = "unique_id")
|
||||
|
||||
id_hodl <- lapply(id_accounting, function(i){
|
||||
hodl_reference(i, "hodl_btc_terms", "hodl_eth_terms","btc_price")
|
||||
})
|
||||
|
||||
hodl_results <- bind_rows(id_hodl, .id = "unique_id")
|
||||
|
||||
id_strat <- lapply(id_accounting, function(i){
|
||||
strategy_reference(i, "strat_btc_terms", "strat_eth_terms","btc_price")
|
||||
})
|
||||
|
||||
strategy_results <- bind_rows(id_strat, .id = "unique_id")
|
||||
|
||||
ethbtc_results <- merge(pnl_results, hodl_results, by = "unique_id")
|
||||
ethbtc_results <- merge(ethbtc_results, strategy_results, by = "unique_id")
|
||||
|
||||
saveRDS(ethbtc_results, file = "ethbtc_results.rds")
|
||||
|
||||
|
||||
```
|
||||
20
ethbtc_initial_summary_results.R
Normal file
20
ethbtc_initial_summary_results.R
Normal file
@ -0,0 +1,20 @@
|
||||
library(dplyr)
|
||||
options(scipen = 10)
|
||||
|
||||
ethbtc_results <- readRDS("ethbtc/ethbtc_results.rds")
|
||||
|
||||
n = nrow(ethbtc_results)
|
||||
|
||||
table(ethbtc_results$pnl_btc_terms > 0)/n
|
||||
table(ethbtc_results$pnl_eth_terms > 0)/n
|
||||
|
||||
table(ethbtc_results$pnl_btc_terms > 0 & ethbtc_results$pnl_eth_terms > 0)/n
|
||||
|
||||
table(ethbtc_results$strat_btc_terms > ethbtc_results$hodl_btc_terms)/n
|
||||
|
||||
table(ethbtc_results$strat_btc_terms > ethbtc_results$hodl_btc_terms &
|
||||
ethbtc_results$strat_eth_terms > ethbtc_results$hodl_eth_terms)/n
|
||||
|
||||
|
||||
|
||||
|
||||
20
ethusdc_initial_summary_results.R
Normal file
20
ethusdc_initial_summary_results.R
Normal file
@ -0,0 +1,20 @@
|
||||
library(dplyr)
|
||||
options(scipen = 10)
|
||||
|
||||
ethusdc_results <- readRDS("ethusdc_results.rds")
|
||||
|
||||
n = nrow(ethusdc_results)
|
||||
|
||||
table(ethusdc_results$pnl_usd_terms > 0)/n
|
||||
table(ethusdc_results$pnl_eth_terms > 0)/n
|
||||
|
||||
table(ethusdc_results$pnl_usd_terms > 0 & ethusdc_results$pnl_eth_terms > 0)/n
|
||||
|
||||
table(ethusdc_results$strategy_usd_terms > ethusdc_results$hodl_usd_terms)/n
|
||||
|
||||
table(ethusdc_results$strategy_usd_terms > ethusdc_results$hodl_usd_terms &
|
||||
ethusdc_results$strategy_eth_terms > ethusdc_results$hodl_eth_terms)/n
|
||||
|
||||
|
||||
|
||||
|
||||
130
key_functions.R
130
key_functions.R
@ -1,5 +1,90 @@
|
||||
library(gmp) # big int math
|
||||
|
||||
fee_correction <- function(id_fees, id_lp_actions){
|
||||
|
||||
# fee-collection problem only occurs in decrease_liquidity
|
||||
# sometimes in the same transaction the same exact liquidity is added and immediately removed
|
||||
# occurs in poorly written NF_POSITION_MANAGERS.
|
||||
|
||||
id_lp_actions <- id_lp_actions %>% filter(ACTION == "DECREASE_LIQUIDITY")
|
||||
|
||||
fee_tx_in_lp = (id_fees$TX_HASH %in% id_lp_actions$TX_HASH)
|
||||
lp_tx_in_fee = (id_lp_actions$TX_HASH %in% id_fees$TX_HASH)
|
||||
amount_cols = c("AMOUNT0_ADJUSTED", "AMOUNT1_ADJUSTED")
|
||||
|
||||
# if there are no tx hashes in both tables move on
|
||||
if(sum(fee_tx_in_lp) == 0){
|
||||
return(id_fees)
|
||||
|
||||
} else {
|
||||
# if it perfectly lines up that ALL tx hashes in
|
||||
# both tables have fees collected double counting withdrawals;
|
||||
# fix by subtracting the double counting
|
||||
# note: it is assumed transactions are ordered by block number for alignment to work
|
||||
if(sum(fee_tx_in_lp) == sum(lp_tx_in_fee)){
|
||||
if(mean(id_fees[fee_tx_in_lp, amount_cols] >= id_lp_actions[lp_tx_in_fee, amount_cols]) == 1){
|
||||
id_fees[fee_tx_in_lp, amount_cols] <- {
|
||||
id_fees[fee_tx_in_lp, amount_cols] - id_lp_actions[lp_tx_in_fee, amount_cols]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# else go 1 by one to overlapping tx and check again
|
||||
|
||||
for(i in id_fees$TX_HASH[fee_tx_in_lp]){
|
||||
if(
|
||||
mean(
|
||||
id_fees[id_fees$TX_HASH == i, amount_cols] >=
|
||||
id_lp_actions[id_lp_actions$TX_HASH == i, amount_cols]
|
||||
) == 1
|
||||
){
|
||||
id_fees[id_fees$TX_HASH == i, amount_cols] <- {
|
||||
id_fees[id_fees$TX_HASH == i, amount_cols] -
|
||||
id_lp_actions[id_lp_actions$TX_HASH == i, amount_cols]
|
||||
}
|
||||
} else next()
|
||||
}
|
||||
}
|
||||
|
||||
return(id_fees)
|
||||
}
|
||||
}
|
||||
|
||||
accounting <- function(id_lp_actions, id_fees, price_col){
|
||||
|
||||
# cost basis negative for subtraction
|
||||
cost_basis <- id_lp_actions %>% filter(ACTION == "INCREASE_LIQUIDITY") %>%
|
||||
mutate(
|
||||
token0 = -1*AMOUNT0_ADJUSTED,
|
||||
token1 = -1*AMOUNT1_ADJUSTED,
|
||||
accounting = "cost_basis"
|
||||
) %>%
|
||||
select(BLOCK_NUMBER, accounting, token0, token1, all_of(price_col))
|
||||
|
||||
withdrawals <- id_lp_actions %>% filter(ACTION == "DECREASE_LIQUIDITY") %>%
|
||||
mutate(
|
||||
token0 = AMOUNT0_ADJUSTED,
|
||||
token1 = AMOUNT1_ADJUSTED,
|
||||
accounting = "withdrawal"
|
||||
) %>%
|
||||
select(BLOCK_NUMBER, accounting, token0, token1, all_of(price_col))
|
||||
|
||||
fees <- data.frame(
|
||||
BLOCK_NUMBER = max(withdrawals$BLOCK_NUMBER),
|
||||
accounting = "fee revenue",
|
||||
token0 = sum(id_fees$AMOUNT0_ADJUSTED),
|
||||
token1 = sum(id_fees$AMOUNT1_ADJUSTED)
|
||||
)
|
||||
|
||||
# price fees at position closure, i.e., last withdrawal
|
||||
fees[[price_col]] <- tail(withdrawals[[price_col]], n = 1)
|
||||
|
||||
accounting_tbl <- rbind(cost_basis, withdrawals, fees)
|
||||
|
||||
return(accounting_tbl)
|
||||
|
||||
}
|
||||
|
||||
|
||||
get_eth_price <- function(min_block, max_block, api_key){
|
||||
price_query <- {
|
||||
"
|
||||
@ -49,6 +134,51 @@ ORDER BY BLOCK_NUMBER DESC
|
||||
prices = auto_paginate_query(price_query, api_key = api_key)
|
||||
}
|
||||
|
||||
get_ethbtc_price <- function(min_block, max_block, api_key){
|
||||
|
||||
price_query <- {
|
||||
"
|
||||
with uniswap_btc_eth_swaps AS (
|
||||
SELECT * FROM ethereum.uniswapv3.ez_swaps WHERE POOL_ADDRESS IN (
|
||||
'0xcbcdf9626bc03e24f779434178a73a0b4bad62ed', -- wbtc ETH 0.3%
|
||||
'0x4585fe77225b41b697c938b018e2ac67ac5a20c0' -- wbtc ETH 0.05%
|
||||
) AND
|
||||
BLOCK_NUMBER >= _min_block_ AND
|
||||
BLOCK_NUMBER <= _max_block_
|
||||
),
|
||||
|
||||
btc_eth_price AS (
|
||||
SELECT BLOCK_NUMBER, BLOCK_TIMESTAMP,
|
||||
IFF(TOKEN1_SYMBOL = 'WBTC', ABS(DIV0(AMOUNT0_ADJUSTED, AMOUNT1_ADJUSTED)),
|
||||
ABS(DIV0(AMOUNT1_ADJUSTED, AMOUNT0_ADJUSTED))
|
||||
) as btc_eth_trade_price,
|
||||
IFF(TOKEN1_SYMBOL = 'WBTC', ABS(AMOUNT1_ADJUSTED), ABS(AMOUNT0_ADJUSTED)) as btc_volume,
|
||||
IFF(TOKEN1_SYMBOL = 'WBTC', TOKEN0_SYMBOL, TOKEN1_SYMBOL) as eth
|
||||
FROM uniswap_btc_eth_swaps
|
||||
WHERE ABS(AMOUNT0_ADJUSTED) > 1e-8 AND ABS(AMOUNT1_ADJUSTED) > 1e-8
|
||||
),
|
||||
|
||||
btc_block_price AS (
|
||||
SELECT BLOCK_NUMBER, BLOCK_TIMESTAMP,
|
||||
div0(SUM(btc_eth_trade_price * btc_volume),sum(btc_volume)) as btc_wavg_price,
|
||||
SUM(btc_volume) as btc_volume,
|
||||
COUNT(*) as num_swaps
|
||||
FROM btc_eth_price
|
||||
GROUP BY BLOCK_NUMBER, BLOCK_TIMESTAMP
|
||||
)
|
||||
|
||||
SELECT * FROM btc_block_price
|
||||
ORDER BY BLOCK_NUMBER DESC
|
||||
"
|
||||
}
|
||||
|
||||
price_query <- gsub("_min_block_", min_block, price_query)
|
||||
price_query <- gsub("_max_block_", max_block, price_query)
|
||||
|
||||
prices = auto_paginate_query(price_query, api_key = api_key)
|
||||
}
|
||||
|
||||
|
||||
market_eth_price_at_block <- function(eth_prices, block){
|
||||
eth_prices[eth_prices$BLOCK_NUMBER == block, "ETH_MARKET_PRICE"]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user