mirror of
https://github.com/FlipsideCrypto/tools.git
synced 2026-02-06 10:46:47 +00:00
uniswap v3 calculator code for public view
This commit is contained in:
parent
62cf960de9
commit
c3f1039095
169
uniswap_v3/global.R
Normal file
169
uniswap_v3/global.R
Normal file
@ -0,0 +1,169 @@
|
||||
library(data.table)
|
||||
library(shiny)
|
||||
library(ggplot2)
|
||||
library(scales)
|
||||
library(shinyWidgets)
|
||||
library(plotly)
|
||||
library(bslib)
|
||||
require(reshape2)
|
||||
require(dplyr)
|
||||
require(RPostgreSQL)
|
||||
library(RJSONIO)
|
||||
library(stringr)
|
||||
library(showtext)
|
||||
font_add_google(name = "Roboto Condensed", family = "roboto-condensed")
|
||||
font_add_google(name = "Roboto Mono", family = "roboto-mono")
|
||||
showtext_auto()
|
||||
|
||||
|
||||
swap.sd.constant <- 6
|
||||
|
||||
smartRound <- function(x) {
|
||||
|
||||
if(x == 0) return(0)
|
||||
|
||||
if(x < 5 & x > 0.1) {
|
||||
return(round(x, 4))
|
||||
} else {
|
||||
|
||||
return(round(x, which(x * (10^(-2:20)) > 1)[1] - 2))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
round_any <- function(x, accuracy, f=round){f(x/ accuracy) * accuracy}
|
||||
|
||||
|
||||
# liquidity funcitons, translated from from the uniswap nf position manager contract:
|
||||
getLiquidityForAmount0 <- function(amount0, lower, upper) {
|
||||
|
||||
amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))
|
||||
|
||||
}
|
||||
|
||||
|
||||
getLiquidityForAmount1 <- function(amount1, lower, upper) {
|
||||
|
||||
amount1 / (sqrt(upper) - sqrt(lower))
|
||||
|
||||
}
|
||||
|
||||
getLiquidityForAmounts <- function(amount0, amount1, cprice, lower, upper) {
|
||||
|
||||
sqrtRatioX96 <- cprice
|
||||
sqrtRatioAX96 <- lower
|
||||
sqrtRatioBX96 <- upper
|
||||
|
||||
# if the current price is below the lower bound:
|
||||
#if (sqrtRatioX96 <= sqrtRatioAX96) {
|
||||
if(cprice <= lower) {
|
||||
liquidity <- getLiquidityForAmount0(lower, upper, amount0)
|
||||
|
||||
} else if (sqrtRatioX96 < sqrtRatioBX96) {
|
||||
liquidity0 = getLiquidityForAmount0(amount0, sqrtRatioX96, sqrtRatioBX96)
|
||||
liquidity1 = getLiquidityForAmount1(amount1, sqrtRatioAX96, sqrtRatioX96)
|
||||
|
||||
liquidity = ifelse(liquidity0 < liquidity1, liquidity0, liquidity1)
|
||||
} else {
|
||||
liquidity = getLiquidityForAmount1(amount1, sqrtRatioAX96, sqrtRatioBX96);
|
||||
}
|
||||
|
||||
return(liquidity)
|
||||
}
|
||||
|
||||
|
||||
|
||||
#-------------- ggplot THEMEs:
|
||||
theme_univ3 <- function(base_size = 14,
|
||||
bgcolor.dark = "white",
|
||||
bgcolor.light = "white",
|
||||
text.color = "#637381",
|
||||
title.color = "#212B36",
|
||||
lines.color = "#EDEDF3") {
|
||||
half_line <- base_size/2
|
||||
theme(text = element_text(family = 'roboto-mono',
|
||||
face = "plain",
|
||||
colour = text.color, size = base_size,
|
||||
lineheight = 0.9, hjust = 0.5,
|
||||
vjust = 0.5, angle = 0,
|
||||
margin = margin(), debug = FALSE),
|
||||
|
||||
plot.background = element_rect(color = NA, fill = "transparent"),
|
||||
plot.title = element_text(size = rel(1.2),
|
||||
color = title.color,
|
||||
margin = margin(b = half_line/2)),
|
||||
|
||||
strip.background = element_rect(fill = bgcolor.dark, colour = NA),
|
||||
strip.text = element_text(colour = text.color, size = rel(0.8)),
|
||||
strip.text.x = element_text(margin = margin(t = half_line/2,
|
||||
b = half_line/2)),
|
||||
strip.text.y = element_text(angle = -90,
|
||||
margin = margin(l = half_line/2,
|
||||
r = half_line/2)),
|
||||
|
||||
axis.line = element_blank(),
|
||||
axis.ticks.x = element_blank(),
|
||||
axis.ticks.y = element_blank(),
|
||||
|
||||
axis.text = element_text(color = text.color, size = base_size - 2),
|
||||
|
||||
panel.background = element_rect(fill = "transparent", colour = NA),
|
||||
panel.border = element_blank(),
|
||||
panel.grid.major.x = element_line(colour = 'white', linetype = 1, size = 0.7),
|
||||
panel.grid.major.y = element_line(colour = 'white', linetype = 1, size = 0.7),
|
||||
panel.grid.minor.x = element_blank(),
|
||||
panel.grid.minor.y = element_blank(),
|
||||
|
||||
legend.background = element_rect(colour = NA, fill = "transparent"),
|
||||
legend.key = element_rect(colour = NA, fill = "transparent"))
|
||||
}
|
||||
|
||||
# same theme but smaller
|
||||
theme_univ3_small <- function(base_size = 12,
|
||||
bgcolor.dark = "white",
|
||||
bgcolor.light = "white",
|
||||
text.color = "#637381",
|
||||
title.color = "#212B36",
|
||||
lines.color = "#EDEDF3") {
|
||||
half_line <- base_size/2
|
||||
theme(text = element_text(family = 'roboto-mono',
|
||||
face = "plain",
|
||||
colour = text.color, size = base_size,
|
||||
lineheight = 0.9, hjust = 0.5,
|
||||
vjust = 0.5, angle = 0,
|
||||
margin = margin(), debug = FALSE),
|
||||
|
||||
plot.background = element_rect(color = NA, fill = "transparent"),
|
||||
plot.title = element_text(size = rel(1.2),
|
||||
color = title.color,
|
||||
margin = margin(b = half_line/2)),
|
||||
|
||||
strip.background = element_rect(fill = bgcolor.dark, colour = NA),
|
||||
strip.text = element_text(colour = text.color, size = rel(0.8)),
|
||||
strip.text.x = element_text(margin = margin(t = half_line/2,
|
||||
b = half_line/2)),
|
||||
strip.text.y = element_text(angle = -90,
|
||||
margin = margin(l = half_line/2,
|
||||
r = half_line/2)),
|
||||
|
||||
axis.line = element_blank(),
|
||||
axis.ticks.x = element_blank(),
|
||||
axis.ticks.y = element_blank(),
|
||||
|
||||
axis.text = element_text(color = text.color, size = base_size - 2),
|
||||
|
||||
panel.background = element_rect(fill = "transparent", colour = NA),
|
||||
panel.border = element_blank(),
|
||||
panel.grid.major.x = element_line(colour = 'white', linetype = 1, size = 0.7),
|
||||
panel.grid.major.y = element_line(colour = 'white', linetype = 1, size = 0.7),
|
||||
panel.grid.minor.x = element_blank(),
|
||||
panel.grid.minor.y = element_blank(),
|
||||
|
||||
legend.background = element_rect(colour = NA, fill = "transparent"),
|
||||
legend.key = element_rect(colour = NA, fill = "transparent"),
|
||||
|
||||
axis.title.x=element_blank(),
|
||||
axis.text.x=element_blank(),
|
||||
axis.title.y=element_blank(),
|
||||
axis.text.y=element_blank())
|
||||
}
|
||||
564
uniswap_v3/server.R
Normal file
564
uniswap_v3/server.R
Normal file
@ -0,0 +1,564 @@
|
||||
server <- function(input, output, session) {
|
||||
load("data.RData")
|
||||
|
||||
output$poolselect <- renderUI({
|
||||
selectInput(inputId = 'poolname', label = "Select Pool",
|
||||
selected = "USDC-USDT 3000 60",
|
||||
choices = pool.choices)
|
||||
})
|
||||
|
||||
# reverse token prices depending on which token price is larger
|
||||
revTokes <- reactive({
|
||||
ifelse(pool.stats[pool_name == input$poolname][1]$price_0_1 <
|
||||
pool.stats[pool_name == input$poolname][1]$price_1_0,
|
||||
TRUE, FALSE)
|
||||
})
|
||||
|
||||
poolToken1 <- reactive({
|
||||
ifelse(revTokes(),
|
||||
strsplit(strsplit(input$poolname, " ", fixed = TRUE)[[1]][1], "-", fixed = TRUE)[[1]][1],
|
||||
strsplit(strsplit(input$poolname, " ", fixed = TRUE)[[1]][1], "-", fixed = TRUE)[[1]][2])
|
||||
})
|
||||
|
||||
poolToken2 <- reactive({
|
||||
ifelse(revTokes(),
|
||||
strsplit(strsplit(input$poolname, " ", fixed = TRUE)[[1]][1], "-", fixed = TRUE)[[1]][2],
|
||||
strsplit(strsplit(input$poolname, " ", fixed = TRUE)[[1]][1], "-", fixed = TRUE)[[1]][1])
|
||||
|
||||
})
|
||||
|
||||
output$n_open_positions <- renderText({
|
||||
paste0(nrow(current.positions[pool_name == input$poolname]), " Open Positions")
|
||||
})
|
||||
|
||||
output$poolfee <- renderText({
|
||||
paste0(current.positions[pool_name == input$poolname]$fee[1] / 10^6 * 100, "%")
|
||||
})
|
||||
|
||||
output$moveprice <- renderUI({
|
||||
|
||||
if(revTokes()) {
|
||||
tmp.min <- swapSDs()$lower_bound_10
|
||||
tmp.max <- swapSDs()$upper_bound_10
|
||||
} else {
|
||||
tmp.min <- swapSDs()$lower_bound_01
|
||||
tmp.max <- swapSDs()$upper_bound_01
|
||||
}
|
||||
sliderInput(
|
||||
inputId = "moveprice", "Move Active Price Assumption",
|
||||
min = ifelse(tmp.min > 10, floor(tmp.min), smartRound(tmp.min)),
|
||||
max = ifelse(tmp.max > 10, ceiling(tmp.max), smartRound(tmp.max)),
|
||||
value = getCurrentPrice(),
|
||||
step = smartRound(abs(tmp.max - tmp.min) / 1000)
|
||||
)
|
||||
})
|
||||
|
||||
output$lcurveselect <- renderUI({
|
||||
|
||||
if(revTokes()) {
|
||||
tmp.min2 <- swapSDs()$lower_2sd_10
|
||||
tmp.max2 <- swapSDs()$upper_2sd_10
|
||||
|
||||
tmp.min <- swapSDs()$lower_bound_10
|
||||
tmp.max <- swapSDs()$upper_bound_10
|
||||
|
||||
} else {
|
||||
tmp.min2 <- swapSDs()$lower_2sd_01
|
||||
tmp.max2 <- swapSDs()$upper_2sd_01
|
||||
|
||||
tmp.min <- swapSDs()$lower_bound_01
|
||||
tmp.max <- swapSDs()$upper_bound_01
|
||||
}
|
||||
|
||||
sliderInput(inputId = "lcurveslider", "Select Liquidity Bounds",
|
||||
min = ifelse(tmp.min > 10, floor(tmp.min), smartRound(tmp.min)),
|
||||
max = ifelse(tmp.max > 10, ceiling(tmp.max), smartRound(tmp.max)),
|
||||
value = c(ifelse(tmp.min > 10, floor(tmp.min2), smartRound(tmp.min)),
|
||||
ifelse(tmp.max > 10, ceiling(tmp.max2), smartRound(tmp.max))),
|
||||
step = smartRound(abs(tmp.max - tmp.min) / 1000))
|
||||
|
||||
})
|
||||
|
||||
output$dates <- renderUI({
|
||||
sliderInput("daterange",
|
||||
"Select Swaps Date Range",
|
||||
min = as.Date(min(swaps$block_timestamp),"%Y-%m-%d"),
|
||||
max = as.Date(max(swaps$block_timestamp),"%Y-%m-%d"),
|
||||
value = c(as.Date(min(swaps$block_timestamp),"%Y-%m-%d"),
|
||||
max = as.Date(max(swaps$block_timestamp),"%Y-%m-%d")),
|
||||
timeFormat="%Y-%m-%d",
|
||||
dragRange = TRUE)
|
||||
})
|
||||
|
||||
|
||||
getCurrentPrice <- reactive({
|
||||
max.swap <- max(swaps[pool_name == input$poolname]$block_id)
|
||||
c.price <- ifelse(revTokes(),
|
||||
swaps[pool_name == input$poolname & block_id == max.swap]$price_1_0[1],
|
||||
swaps[pool_name == input$poolname & block_id == max.swap]$price_0_1[1])
|
||||
ifelse(c.price > 1, round(c.price, 2), smartRound(c.price))
|
||||
|
||||
return(c.price)
|
||||
})
|
||||
|
||||
output$current_price <- renderText({
|
||||
paste0("Current Price: ", getCurrentPrice(), " ", poolToken2(), " per ", poolToken1())
|
||||
})
|
||||
|
||||
output$last_update <- renderText({
|
||||
paste0("Last Pool Update: ", gsub(x = gsub(x = as.character(max(pool.stats[pool_name == input$poolname]$block_timestamp)), "T", " ", fixed = T), "Z", "", fixed = TRUE), " UTC")
|
||||
})
|
||||
|
||||
output$swapvol24h <- renderText({
|
||||
usd.last.day <- swaps[block_timestamp > max(formattedSwapData()$block_timestamp) - (60*60*24) & pool_name == input$poolname,
|
||||
ifelse(amount0_usd < 0, abs(amount0_usd), abs(amount1_usd))]
|
||||
paste0("24 Hour Swap Volume: $", formatC(round(sum(abs(usd.last.day))), format="f", digits=0, big.mark=","))
|
||||
|
||||
})
|
||||
|
||||
output$swapvol24h2 <- renderText({
|
||||
usd.last.day <- formattedSwapData()[block_timestamp > max(formattedSwapData()$block_timestamp) - (60*60*24)]$amount0_usd
|
||||
formatC(round(sum(abs(usd.last.day))), format="f", digits=0, big.mark=",")
|
||||
|
||||
})
|
||||
|
||||
|
||||
output$pool_token1 <- renderText({
|
||||
paste0("Token 1: ", poolToken1())
|
||||
})
|
||||
|
||||
output$pool_token2 <- renderText({
|
||||
paste0("Token 2: ", poolToken2())
|
||||
})
|
||||
|
||||
poolAddy <- reactive({
|
||||
current.positions[pool_name == input$poolname]$pool_address[1]
|
||||
})
|
||||
|
||||
poolStats <- reactive({
|
||||
pool.stats[pool_name == input$poolname]
|
||||
})
|
||||
|
||||
formattedSwapData <- reactive({
|
||||
|
||||
swaps[pool_name == input$poolname &
|
||||
block_timestamp >= as.POSIXct(input$daterange[1]) &
|
||||
block_timestamp <= as.POSIXct(input$daterange[2] + 1)]
|
||||
|
||||
})
|
||||
|
||||
# get the boundaries for whatever sd's away from mean swaps:
|
||||
swapSDs <- reactive({
|
||||
data.table(lower_bound_01 = max(0.0000001, mean(swaps[pool_name == input$poolname]$price_0_1) + (-swap.sd.constant)*sd(swaps[pool_name == input$poolname]$price_0_1)),
|
||||
lower_2sd_01 = max(0.0000001, mean(swaps[pool_name == input$poolname]$price_0_1) + (-swap.sd.constant/2)*sd(swaps[pool_name == input$poolname]$price_0_1)),
|
||||
upper_bound_01 = mean(swaps[pool_name == input$poolname]$price_0_1) + (swap.sd.constant)*sd(swaps[pool_name == input$poolname]$price_0_1),
|
||||
upper_2sd_01 = mean(swaps[pool_name == input$poolname]$price_0_1) + (swap.sd.constant/2)*sd(swaps[pool_name == input$poolname]$price_0_1),
|
||||
|
||||
lower_bound_10 = max(0.0000001, mean(swaps[pool_name == input$poolname]$price_1_0) + (-swap.sd.constant)*sd(swaps[pool_name == input$poolname]$price_1_0)),
|
||||
lower_2sd_10 = max(0.0000001, mean(swaps[pool_name == input$poolname]$price_1_0) + (-swap.sd.constant/2)*sd(swaps[pool_name == input$poolname]$price_1_0)),
|
||||
upper_bound_10 = mean(swaps[pool_name == input$poolname]$price_1_0) + (swap.sd.constant)*sd(swaps[pool_name == input$poolname]$price_1_0),
|
||||
upper_2sd_10 = mean(swaps[pool_name == input$poolname]$price_1_0) + (swap.sd.constant/2)*sd(swaps[pool_name == input$poolname]$price_1_0)
|
||||
)
|
||||
})
|
||||
|
||||
# only keep positions that overlap with the avg last 3 days of trading +/- 5sd
|
||||
goodPoolPos <- reactive({
|
||||
|
||||
if(revTokes()) {
|
||||
this.pool.good.pos <- current.positions[pool_name == input$poolname & liquidity_adjusted > 0 &
|
||||
(price_upper_1_0 >= swapSDs()$lower_bound_10 &
|
||||
price_lower_1_0 <= swapSDs()$upper_bound_10)]
|
||||
} else {
|
||||
this.pool.good.pos <- current.positions[pool_name == input$poolname & liquidity_adjusted > 0 &
|
||||
(price_upper_0_1 >= swapSDs()$lower_bound_01 &
|
||||
price_lower_0_1 <= swapSDs()$upper_bound_01)]
|
||||
}
|
||||
return(this.pool.good.pos)
|
||||
|
||||
})
|
||||
|
||||
# make data to do the existing liquidity plot
|
||||
# we'll "spread out" each position over its ticks
|
||||
summarizeLiquidity <- reactive({
|
||||
|
||||
n.price.bins <- swap.sd.constant * 20
|
||||
tick.dec.adj <- poolStats()[1]$pool_dec_adj
|
||||
|
||||
if(revTokes()) {
|
||||
closest.lower.tick <- floor( log( swapSDs()$lower_bound_10 * (10^tick.dec.adj), 1.0001))
|
||||
closest.upper.tick <- ceiling( log( swapSDs()$upper_bound_10 * (10^tick.dec.adj), 1.0001) )
|
||||
} else {
|
||||
closest.lower.tick <- floor( log( 1 / swapSDs()$upper_bound_01 * (10^tick.dec.adj), 1.0001) )
|
||||
closest.upper.tick <- ceiling( log( 1 / swapSDs()$lower_bound_01 * (10^tick.dec.adj), 1.0001) )
|
||||
}
|
||||
|
||||
|
||||
unique.positions <- goodPoolPos()
|
||||
|
||||
bin.size <- ifelse(revTokes(),
|
||||
(swapSDs()$upper_bound_10 - swapSDs()$lower_bound_10) / (swap.sd.constant * 20),
|
||||
(swapSDs()$upper_bound_01 - swapSDs()$lower_bound_01) / (swap.sd.constant * 20))
|
||||
|
||||
unique.positions[, bin_size := bin.size]
|
||||
|
||||
if(revTokes()) {
|
||||
unique.positions[, avg_chunk_liq := liquidity_adjusted / ((price_upper_1_0 - price_lower_1_0) / bin_size)]
|
||||
} else {
|
||||
unique.positions[, avg_chunk_liq := liquidity_adjusted / ((price_upper_0_1 - price_lower_0_1) / bin_size)]
|
||||
}
|
||||
|
||||
unique.positions <- unique.positions[, list(liquidity_by_pos = sum(avg_chunk_liq),
|
||||
liquidity_max = sum(liquidity_adjusted)),
|
||||
by = "tick_lower,tick_upper,price_lower_0_1,price_upper_0_1"]
|
||||
|
||||
# the tick to price, undo it and get the right dec adjustment:
|
||||
tick.spacing <- ceiling(abs((closest.upper.tick - closest.lower.tick)/n.price.bins))
|
||||
by.tick <- data.table(tick = seq(closest.lower.tick, closest.upper.tick,
|
||||
by = tick.spacing))
|
||||
|
||||
if(revTokes()) {
|
||||
extra.tick.price <- (((1.0001^(closest.upper.tick + tick.spacing)) / (10^tick.dec.adj)))
|
||||
} else {
|
||||
extra.tick.price <- 1 / (((1.0001^(ifelse(closest.lower.tick < 0, closest.upper.tick, closest.lower.tick) + tick.spacing)) / (10^tick.dec.adj)))
|
||||
}
|
||||
|
||||
# merge trick:
|
||||
unique.positions[, m := 1]
|
||||
by.tick[, m := 1]
|
||||
|
||||
by.tick <- merge(by.tick, unique.positions, by = "m", all = TRUE, allow.cartesian = TRUE)
|
||||
|
||||
if(revTokes()) {
|
||||
by.tick[, tick_price := (((1.0001^tick) / (10^tick.dec.adj)))]
|
||||
} else {
|
||||
by.tick[, tick_price := 1 / (((1.0001^tick) / (10^tick.dec.adj)))]
|
||||
}
|
||||
|
||||
by.tick <- by.tick[ (tick >= tick_lower) & (tick <= tick_upper)]
|
||||
|
||||
|
||||
if(revTokes()) {
|
||||
liquidity.by.tick <- by.tick[, list(max_liquidity = sum(liquidity_max),
|
||||
avg_liquidity = sum(liquidity_by_pos, na.rm = TRUE)),
|
||||
by = "tick,tick_price"] %>%
|
||||
.[tick_price > swapSDs()$lower_bound_10 & tick_price < swapSDs()$upper_bound_10]
|
||||
|
||||
liquidity.by.tick[, tick_end := c(liquidity.by.tick$tick_price[2:length(liquidity.by.tick$tick_price)], extra.tick.price)]
|
||||
} else {
|
||||
liquidity.by.tick <- by.tick[, list(max_liquidity = sum(liquidity_max),
|
||||
avg_liquidity = sum(liquidity_by_pos, na.rm = TRUE)),
|
||||
by = "tick,tick_price"] %>%
|
||||
.[tick_price > swapSDs()$lower_bound_01 & tick_price < swapSDs()$upper_bound_01]
|
||||
|
||||
liquidity.by.tick[, tick_end := c(liquidity.by.tick$tick_price[2:length(liquidity.by.tick$tick_price)], extra.tick.price)]
|
||||
}
|
||||
|
||||
liquidity.by.tick
|
||||
|
||||
})
|
||||
|
||||
|
||||
output$lcurve_plot <- renderPlot({
|
||||
|
||||
tmp.data <- formattedSwapData()
|
||||
|
||||
if(revTokes()) {
|
||||
tmp.data[, covered := ifelse(price_1_0 >= input$lcurveslider[1] & price_1_0 <= input$lcurveslider[2],
|
||||
"covered", "out")]
|
||||
|
||||
lcurve.plot <- ggplot() +
|
||||
theme_univ3() +
|
||||
geom_line(data = tmp.data,
|
||||
aes(x = price_1_0, y = price_0_1, alpha = covered), size = 6, color = "#01c3b3") +
|
||||
geom_point(data = tmp.data,
|
||||
aes(x = price_1_0, y = price_0_1)) +
|
||||
geom_point(data = data.table(price_1_0 = input$moveprice, price_0_1 = (1/input$moveprice), covered = 'covered'),
|
||||
aes(x = price_1_0, y = price_0_1),
|
||||
size = 5, color = 'white') +
|
||||
labs(y = paste0("\n", poolToken1(), " per ", poolToken2()),
|
||||
x = paste0(poolToken2(), " per ", poolToken1(), "\n")) +
|
||||
theme(legend.position = 'none') +
|
||||
scale_alpha_manual(values = c(1, 0))
|
||||
|
||||
} else {
|
||||
tmp.data[, covered := ifelse(price_0_1 >= input$lcurveslider[1] & price_0_1 <= input$lcurveslider[2],
|
||||
"covered", "out")]
|
||||
|
||||
lcurve.plot <- ggplot() +
|
||||
theme_univ3() +
|
||||
geom_line(data = tmp.data,
|
||||
aes(x = price_0_1, y = price_1_0, alpha = covered), size = 6, color = "#01c3b3") +
|
||||
geom_point(data = tmp.data,
|
||||
aes(x = price_0_1, y = price_1_0)) +
|
||||
geom_point(data = data.table(price_0_1 = input$moveprice, price_1_0 = (1/input$moveprice), covered = 'covered'),
|
||||
aes(x = price_0_1, y = price_1_0),
|
||||
size = 5, color = 'white') +
|
||||
labs(y = paste0("\n", poolToken1(), " per ", poolToken2()),
|
||||
x = paste0(poolToken2(), " per ", poolToken1(), "\n")) +
|
||||
theme(legend.position = 'none') +
|
||||
scale_alpha_manual(values = c(1, 0))
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
return(lcurve.plot)
|
||||
|
||||
}, bg = "transparent")
|
||||
|
||||
output$concentration_view_plot <- renderPlot({
|
||||
|
||||
ggplot() +
|
||||
geom_rect(data = summarizeLiquidity(),
|
||||
aes(xmin = tick_price, xmax = tick_end, ymin = 0, ymax = avg_liquidity),
|
||||
fill = "grey80") +
|
||||
geom_rect(data = summarizeLiquidity()[tick_price >= input$lcurveslider[1] &
|
||||
tick_end <= input$lcurveslider[2]],
|
||||
aes(xmin = tick_price, xmax = tick_end, ymin = 0, ymax = avg_liquidity),
|
||||
fill = "#01c3b3") +
|
||||
geom_vline(xintercept = input$moveprice, color = "black", linetype = 2) +
|
||||
# geom_vline(xintercept = input$lcurveslider[1], color = "#F8ABA7") +
|
||||
# geom_vline(xintercept = input$lcurveslider[2], color = "#F8ABA7") +
|
||||
theme_univ3() +
|
||||
geom_text(data = data.table(x = input$moveprice, y = 0,
|
||||
label = " active swap price", xmin = 1, xmax = 1, ymin = 1, ymax = 1),
|
||||
aes(x = x, y = y, label = label),
|
||||
hjust = 0, vjust = -1, family = "roboto-mono") +
|
||||
labs(x = paste0(poolToken2(), " per ", poolToken1(), "\n"), y = "Existing Liquidity")
|
||||
|
||||
|
||||
|
||||
|
||||
}, bg = "transparent")
|
||||
|
||||
output$timepriceplot <- renderPlot({
|
||||
|
||||
plot.data <- swaps[pool_name == input$poolname &
|
||||
as.Date(block_timestamp) >= min(input$daterange) &
|
||||
as.Date(block_timestamp) <= max(input$daterange) ]
|
||||
|
||||
if(revTokes()) {
|
||||
|
||||
min.max.data <- rbind(plot.data[which.min(plot.data$price_1_0), list(block_timestamp, price = smartRound(price_1_0))],
|
||||
plot.data[which.max(plot.data$price_1_0), list(block_timestamp, price = smartRound(price_1_0))])
|
||||
|
||||
plot.data$price_0_1 <- plot.data$price_1_0
|
||||
|
||||
.limits <- c(swapSDs()$lower_bound_10*.99, swapSDs()$upper_bound_10*1.01)
|
||||
|
||||
} else {
|
||||
|
||||
min.max.data <- rbind(plot.data[which.min(plot.data$price_0_1), list(block_timestamp, price = smartRound(price_0_1))],
|
||||
plot.data[which.max(plot.data$price_0_1), list(block_timestamp, price = smartRound(price_0_1))])
|
||||
|
||||
.limits <- c(swapSDs()$lower_bound_01*.99, swapSDs()$upper_bound_01*1.01)
|
||||
}
|
||||
|
||||
ggplot() +
|
||||
geom_rect(data = data.table(xmin = min(plot.data$block_timestamp),
|
||||
xmax = max(plot.data$block_timestamp),
|
||||
ymin = input$lcurveslider[1],
|
||||
ymax = input$lcurveslider[2]),
|
||||
aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
|
||||
fill = "#01c3b3", alpha = 0.5) +
|
||||
geom_point(data = plot.data,
|
||||
aes(x = block_timestamp, y = price_0_1), size = 0.6) +
|
||||
geom_label(data = min.max.data,
|
||||
aes(x = block_timestamp, y = price, label = price),
|
||||
color = "black", fill = "#e7d3f2", family = "roboto-mono", hjust = 0, vjust = 1, size = 6) +
|
||||
theme_univ3_small() +
|
||||
scale_y_continuous(limits = .limits) +
|
||||
theme(axis.title.x = element_blank(),
|
||||
axis.text.x = element_blank()) +
|
||||
scale_x_datetime(expand = c(0, 0))
|
||||
|
||||
},
|
||||
height = 130,
|
||||
bg = "transparent")
|
||||
|
||||
|
||||
output$swaps5days <- renderPlot({
|
||||
|
||||
plot.data <- swaps[pool_name == input$poolname &
|
||||
swap_date >= Sys.Date() - 4 &
|
||||
swap_date <= Sys.Date() &
|
||||
!is.na(amount0_usd) &
|
||||
!is.na(amount1_usd),
|
||||
sum(ifelse(amount0_usd < 0, abs(amount0_usd), abs(amount1_usd))),
|
||||
by = swap_date]
|
||||
plot.data[, label := round(V1/1000000, 2)]
|
||||
|
||||
tmp.plot <- ggplot(plot.data,
|
||||
aes(x = swap_date, y = V1)) +
|
||||
geom_bar(stat = "identity", fill = "#01c3b3") +
|
||||
geom_text(aes(x = swap_date, y = V1, label = label),
|
||||
color = "black", family = "roboto-mono", size = 7, vjust = 1.25, color = "white") +
|
||||
labs(y = NULL, x = NULL) +
|
||||
theme_univ3_small()
|
||||
|
||||
return(tmp.plot)
|
||||
|
||||
},
|
||||
height = 130,
|
||||
bg = "transparent")
|
||||
|
||||
output$lcurve_prop_covered <- renderText({
|
||||
|
||||
if(revTokes()) {
|
||||
prop <- round( (sum(abs(formattedSwapData()[price_1_0 >= input$lcurveslider[1] & price_1_0 <= input$lcurveslider[2]]$amount1_adjusted)) /
|
||||
sum(abs(formattedSwapData()$amount1_adjusted))) *100)
|
||||
} else {
|
||||
prop <- round( (sum(abs(formattedSwapData()[price_0_1 >= input$lcurveslider[1] & price_0_1 <= input$lcurveslider[2]]$amount0_adjusted)) /
|
||||
sum(abs(formattedSwapData()$amount0_adjusted))) *100)
|
||||
}
|
||||
paste(prop, "% Swap Volume Covered")
|
||||
|
||||
})
|
||||
|
||||
# figure out the token amounts
|
||||
# this is also taken from the position manager contract
|
||||
tokenAmounts <- reactive({
|
||||
|
||||
token0.price <- poolStats()$token0_balance_usd / poolStats()$token0_balance_adjusted
|
||||
token1.price <- poolStats()$token1_balance_usd / poolStats()$token1_balance_adjusted
|
||||
|
||||
if(!revTokes()) {
|
||||
price.sqrt <- sqrt(1 / input$moveprice)
|
||||
price.upper.sqrt <- sqrt(1 / input$lcurveslider[1])
|
||||
price.lower.sqrt <- sqrt(1 / input$lcurveslider[2])
|
||||
} else {
|
||||
price.sqrt <- sqrt(input$moveprice)
|
||||
price.lower.sqrt <- sqrt(input$lcurveslider[1])
|
||||
price.upper.sqrt <- sqrt(input$lcurveslider[2])
|
||||
}
|
||||
|
||||
relation <- (price.sqrt - price.lower.sqrt) / ( (1 / price.sqrt) - (1 / price.upper.sqrt) )
|
||||
|
||||
token0_amt <- input$lpamt / (token0.price + relation * token1.price)
|
||||
token1_amt <- token0_amt * relation
|
||||
|
||||
tmp <- data.table(token0.price = token0.price,
|
||||
token1.price = token1.price,
|
||||
token0_amt = token0_amt,
|
||||
token1_amt = token1_amt)
|
||||
|
||||
print(tmp)
|
||||
print(token0_amt * token0.price + token1_amt * token1.price)
|
||||
|
||||
return(tmp)
|
||||
})
|
||||
|
||||
# total liquidity for the selected position
|
||||
posTotalVL <- reactive({
|
||||
|
||||
if(!revTokes()) {
|
||||
pos.total.vl <- getLiquidityForAmounts(tokenAmounts()$token1_amt, tokenAmounts()$token0_amt, input$moveprice, input$lcurveslider[1], input$lcurveslider[2])
|
||||
} else {
|
||||
pos.total.vl <- getLiquidityForAmounts(tokenAmounts()$token0_amt, tokenAmounts()$token1_amt, input$moveprice, input$lcurveslider[1], input$lcurveslider[2])
|
||||
}
|
||||
|
||||
return(pos.total.vl)
|
||||
|
||||
})
|
||||
|
||||
findLPexisting <- reactive({
|
||||
|
||||
if(revTokes()) {
|
||||
in.positions <- current.positions[pool_name == input$poolname & price_lower_1_0 <= input$moveprice & price_upper_1_0 >= input$moveprice]
|
||||
} else {
|
||||
in.positions <- current.positions[pool_name == input$poolname & price_lower_0_1 <= input$moveprice & price_upper_0_1 >= input$moveprice]
|
||||
}
|
||||
#in.positions <- current.positions[pool_name == input$poolname]
|
||||
|
||||
return(sum(in.positions$liquidity_adjusted))
|
||||
|
||||
})
|
||||
|
||||
output$investamt <- renderUI({
|
||||
shinyWidgets::numericInputIcon("lpamt",
|
||||
NULL,
|
||||
value = 777,
|
||||
step= 0.01)
|
||||
})
|
||||
|
||||
output$simpledailyfee <- renderText({
|
||||
|
||||
if(input$lcurveslider[1] > input$moveprice |
|
||||
input$lcurveslider[2] < input$moveprice) {
|
||||
tmp <- "$0 (0%)"
|
||||
|
||||
} else {
|
||||
|
||||
tmp.vol.usd <- swaps[pool_name == input$poolname & block_timestamp > max(swaps[pool_name == input$poolname]$block_timestamp) - (60*60*24)] %>%
|
||||
.[, ifelse(amount0_usd > 0, abs(amount1_usd), abs(amount0_usd))] %>%
|
||||
sum()
|
||||
pool.fee <- poolStats()$fee[1] / 10^6
|
||||
est.fee <- round(( posTotalVL() / (posTotalVL() + findLPexisting()) ) * tmp.vol.usd * pool.fee, 2)
|
||||
annual.prop <- round((est.fee*365 / input$lpamt) * 100, 2)
|
||||
|
||||
tmp <- paste0("$", est.fee, " (", annual.prop, "%)")
|
||||
}
|
||||
|
||||
|
||||
|
||||
tmp
|
||||
})
|
||||
|
||||
output$simpledailyfee2 <- renderText({
|
||||
|
||||
if(input$lcurveslider[1] > input$moveprice |
|
||||
input$lcurveslider[2] < input$moveprice) {
|
||||
tmp <- "$0 (0%)"
|
||||
|
||||
} else {
|
||||
|
||||
tmp.vol.usd <- formattedSwapData()[block_timestamp > max(formattedSwapData()$block_timestamp) - (60*60*24)] %>%
|
||||
.[, ifelse(amount0_usd > 0, abs(amount1_usd), abs(amount0_usd))] %>%
|
||||
sum()
|
||||
pool.fee <- poolStats()$fee[1] / 10^6
|
||||
est.fee <- round(( posTotalVL() / (posTotalVL() + findLPexisting()) ) * tmp.vol.usd * pool.fee, 2)
|
||||
annual.prop <- round((est.fee*365 / input$lpamt) * 100, 2)
|
||||
|
||||
tmp <- paste0("$", est.fee, " (", annual.prop, "%)")
|
||||
}
|
||||
|
||||
|
||||
|
||||
tmp
|
||||
})
|
||||
|
||||
output$posVLProp <- renderText({
|
||||
paste0(round(posTotalVL() / (posTotalVL() + findLPexisting())*100, 5), "% of Total Virtual Liquidity")
|
||||
})
|
||||
|
||||
output$yourVL <- renderText({
|
||||
posTotalVL()
|
||||
})
|
||||
|
||||
output$alltickVL <- renderText({
|
||||
findLPexisting()
|
||||
})
|
||||
|
||||
output$token0amt <- renderText({
|
||||
paste0(round(tokenAmounts()$token0_amt, 2), " ", ifelse(!revTokes(), poolToken2(), poolToken1()),
|
||||
" ($", round(tokenAmounts()$token0_amt * tokenAmounts()$token0.price, 2), ")")
|
||||
})
|
||||
|
||||
output$token1amt <- renderText({
|
||||
paste0(round(tokenAmounts()$token1_amt, 2), " ", ifelse(!revTokes(), poolToken1(), poolToken2()),
|
||||
" ($", round(tokenAmounts()$token1_amt * tokenAmounts()$token1.price, 2), ")")
|
||||
})
|
||||
|
||||
output$link2uniswap <- renderUI({
|
||||
# copy this link:
|
||||
#https://app.uniswap.org/#/add/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2/3000
|
||||
HTML(paste0('<a href = "',
|
||||
paste0("https://info.uniswap.org/#/pools/", poolAddy()),
|
||||
'" target = "_blank"> 🦄 </a>'))
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
159
uniswap_v3/ui.R
Normal file
159
uniswap_v3/ui.R
Normal file
@ -0,0 +1,159 @@
|
||||
|
||||
fluidPage(
|
||||
title = "Flipside: Uniswap V3 Fee Calculator",
|
||||
theme = bs_theme(
|
||||
version = version_default(),
|
||||
bootswatch = NULL,
|
||||
bg = "#f2defe", fg = "#110403", primary = "#01c3b3", secondary = "#f2defe",
|
||||
base_font = font_google("Roboto Mono"),
|
||||
code_font = font_google("Roboto Mono"),
|
||||
heading_font = font_google("Roboto Mono")
|
||||
),
|
||||
tags$head(
|
||||
tags$link(rel = "stylesheet", type = "text/css", href = "shinycss.css"),
|
||||
tags$link(rel = "icon", href = "fliptrans.png")
|
||||
),
|
||||
tags$style(type="text/css",
|
||||
".shiny-output-error { visibility: hidden; }",
|
||||
".shiny-output-error:before { visibility: hidden; }"
|
||||
),
|
||||
fluidRow(
|
||||
column(6,
|
||||
div(h2(a(href = "http://www.flipsidecrypto.com", target = "_blank", img(src = 'fliptrans.png', width = '50px')), "Uniswap V3 Fee Calculator"))),
|
||||
column(
|
||||
6,
|
||||
div(h5(
|
||||
a(href = "http://app.flipsidecrypto.com/",
|
||||
target = "_blank", "Data from Velocity by Flipside Crypto"),
|
||||
a(href = "https://twitter.com/flipsidecrypto", target = "_blank",
|
||||
img(src = 'twitter.png', width = '30px')),
|
||||
a(href = "https://flipsidecrypto.com/discord", target = "_blank",
|
||||
img(src = 'discord.png', width = '35px')),
|
||||
style = "font-style: italic; font-weight: 150; text-align: right;"),
|
||||
style = "padding-top: 21px;")
|
||||
),
|
||||
column(12,div("change the inputs on the left, see current swaps & liqudity + estimate fee income on the right, read disclaimer at the bottom")
|
||||
) #close column
|
||||
), # close intro row
|
||||
hr(),
|
||||
fluidRow(
|
||||
column(4, class = 'leftinputs',
|
||||
a(target = "_blank", uiOutput("poolselect")),
|
||||
div(id = 'lildate',
|
||||
p("To search, select the dropdown, hit backspace/delete and type"),
|
||||
textOutput('last_update')),
|
||||
div(class = 'smallstats', textOutput('n_open_positions')),
|
||||
div(class = 'smallstats', textOutput('swapvol24h')),
|
||||
div(class = 'smallstats', textOutput('current_price')),
|
||||
hr(),
|
||||
a(target = "_blank", uiOutput("moveprice")),
|
||||
hr(),
|
||||
a(target = "_blank", uiOutput("lcurveselect")),
|
||||
hr(),
|
||||
uiOutput("dates")
|
||||
|
||||
), # close inputs column
|
||||
|
||||
# this is the results / graphs column
|
||||
column(8,
|
||||
fluidRow(h4("Simple Daily Fee Estimate:")),
|
||||
fluidRow(class = 'feedesc',
|
||||
column(4,class = 'feedesc', "Investment $ Here"),
|
||||
column(4,class = 'feedesc', "24h Fee $ (annual %)"),
|
||||
column(2, "GO ↯")
|
||||
),
|
||||
fluidRow(class = 'feedesc',
|
||||
column(4, class = 'centerit',
|
||||
a(target = "_blank",
|
||||
uiOutput('investamt'),
|
||||
)),
|
||||
column(4, div(id = "fee", textOutput('simpledailyfee'))),
|
||||
column(2, div(id = 'uni', uiOutput("link2uniswap")))
|
||||
),
|
||||
hr(),
|
||||
fluidRow(
|
||||
column(6,h4("Swap Curve"),
|
||||
textOutput("lcurve_prop_covered")),
|
||||
column(6,
|
||||
h4("Liquidity Positions"),
|
||||
textOutput("posVLProp"))
|
||||
),
|
||||
fluidRow(
|
||||
column(6,plotOutput("lcurve_plot", height = '350px')),
|
||||
column(6,plotOutput("concentration_view_plot",height = '350px'))
|
||||
),
|
||||
fluidRow(
|
||||
column(6,
|
||||
h5("Prices Last 5 Days"),
|
||||
plotOutput("timepriceplot", height = "130px")),
|
||||
column(6,
|
||||
h5("5 Day Swap Volume ($MM)"),
|
||||
plotOutput("swaps5days", height = "130px"))
|
||||
)
|
||||
|
||||
) # results columns
|
||||
|
||||
|
||||
), # close inputs/results row
|
||||
fluidRow(
|
||||
column(12,
|
||||
hr(),
|
||||
div(
|
||||
id = 'disclaimer',
|
||||
"This is not investment advice, use it at your own risk. ",
|
||||
a(href = "http://velocity-app.flipsidecrypto.com",
|
||||
target = "_blank",
|
||||
"DYOR Here with Velocity"),
|
||||
br(),
|
||||
"Questions? Join our ",
|
||||
a(href = "https://flipsidecrypto.com/discord", "discord", target = "_blank"), " and let's chat.",
|
||||
hr(),
|
||||
"This tool presents a simple point-in-time estimate of how much you could
|
||||
potentially earn in fees for providing liquidity in Uniswap V3.
|
||||
It assumes no changes to swap price, swap volumes or liquidity positions for 24 hours,
|
||||
which is not realistic. It does not account for Impermanent Loss.",
|
||||
br(),
|
||||
"Use this tool to get an idea of where you want to invest,
|
||||
and directionally what your fee income could be. We strive to provide accurate information about the present and the past but make no guarantees about the future."
|
||||
),
|
||||
hr()
|
||||
)
|
||||
),
|
||||
fluidRow(
|
||||
column(
|
||||
6,
|
||||
div(h5("How Fee Income is Estimated:")),
|
||||
div("Basic Formula (L = liquidity): (L_you / L_others) * (24h_swap_volume * pool_fee_rate)"),
|
||||
div("Calculation Details for This Position"),
|
||||
div("Liquidity for This Position: ", textOutput("yourVL")),
|
||||
div("All Other Existing Liquidity (liquidity for all other positions crossing the current price tick): ", textOutput("alltickVL")),
|
||||
div("24 Hour Swap Volume (across all ticks):", textOutput("swapvol24h2")),
|
||||
div("Pool Fee: ", textOutput("poolfee")),
|
||||
div("Fee Income: ", textOutput('simpledailyfee2')),
|
||||
div("Token Amounts Needed:"),
|
||||
div(textOutput("token0amt")),
|
||||
div(textOutput("token1amt"))
|
||||
),
|
||||
column(
|
||||
6,
|
||||
div(h5("More on Liquidity:")),
|
||||
div("The liquidity amount is calculated from the following numbers that describe a position: amount of token 0 (amt0), amount of token 1 (amt1), price (as x token 1's per token 0) at the upper limit of the position (upper), price at the lower limit of the position (lower) and the current swap price (cprice). Then liquidity for a position is calculated as follows:"),
|
||||
div("Case 1: cprice <= lower"),
|
||||
div("liquidity = amt0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))"),
|
||||
div("Case 2: lower < cprice <= upper"),
|
||||
div("liquidity is the min of the following two calculations:"),
|
||||
div("amt0 * (sqrt(upper) * sqrt(cprice)) / (sqrt(upper) - sqrt(cprice))"),
|
||||
div("amt1 / (sqrt(cprice) - sqrt(lower))"),
|
||||
div("Case 3: upper < cprice"),
|
||||
div("liquidity = amt1 / (sqrt(upper) - sqrt(lower))")
|
||||
),
|
||||
br()
|
||||
)
|
||||
) # close fluid page
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
165
uniswap_v3/update_data.R
Normal file
165
uniswap_v3/update_data.R
Normal file
@ -0,0 +1,165 @@
|
||||
|
||||
max.blocks <- c(QuerySnowflake("SELECT max(block_id) FROM uniswapv3.swaps"),
|
||||
QuerySnowflake("SELECT max(block_id) FROM uniswapv3.pool_stats"),
|
||||
QuerySnowflake("SELECT max(block_id) FROM uniswapv3.positions"))
|
||||
max.pull.block <- min(unlist(max.blocks))
|
||||
|
||||
|
||||
current.positions <- QuerySnowflake(paste0("
|
||||
WITH positions AS (
|
||||
SELECT * FROM uniswapv3.positions WHERE block_id <= ", max.pull.block, "
|
||||
),
|
||||
|
||||
max_blocks AS (
|
||||
SELECT
|
||||
max(block_id) AS block_id,
|
||||
pool_address,
|
||||
liquidity_provider,
|
||||
nf_token_id
|
||||
|
||||
FROM positions
|
||||
|
||||
GROUP BY
|
||||
pool_address,
|
||||
liquidity_provider,
|
||||
nf_token_id
|
||||
)
|
||||
|
||||
|
||||
SELECT
|
||||
blockchain,
|
||||
p.block_id,
|
||||
block_timestamp,
|
||||
tx_id,
|
||||
fee_percent,
|
||||
liquidity_adjusted,
|
||||
p.liquidity_provider,
|
||||
nf_position_manager_address,
|
||||
p.nf_token_id,
|
||||
p.pool_address,
|
||||
p.pool_name,
|
||||
p.tick_lower,
|
||||
p.tick_upper,
|
||||
p.price_lower_0_1,
|
||||
p.price_lower_0_1_usd,
|
||||
p.price_upper_0_1,
|
||||
p.price_upper_0_1_usd,
|
||||
p.price_lower_1_0,
|
||||
p.price_lower_1_0_usd,
|
||||
p.price_upper_1_0,
|
||||
p.price_upper_1_0_usd
|
||||
|
||||
FROM positions p
|
||||
|
||||
INNER JOIN max_blocks mb ON
|
||||
mb.block_id = p.block_id AND
|
||||
mb.pool_address = p.pool_address AND
|
||||
mb.liquidity_provider = p.liquidity_provider AND
|
||||
mb.nf_token_id = p.nf_token_id
|
||||
|
||||
WHERE liquidity_adjusted > 0
|
||||
|
||||
"))
|
||||
current.positions[, fee := as.numeric(strsplit(pool_name, " ", fixed = TRUE)[[1]][2]), by = pool_name ]
|
||||
current.positions[, tick_spacing := as.numeric(strsplit(pool_name, " ", fixed = TRUE)[[1]][3]), by = pool_name ]
|
||||
|
||||
|
||||
pool.stats <- QuerySnowflake(paste0("
|
||||
WITH max_blocks AS (
|
||||
SELECT
|
||||
max(block_id) AS block_id,
|
||||
pool_address
|
||||
FROM
|
||||
uniswapv3.pool_stats
|
||||
WHERE block_id <= ", max.pull.block, "
|
||||
GROUP BY
|
||||
pool_address)
|
||||
|
||||
SELECT
|
||||
ps.block_id,
|
||||
block_timestamp,
|
||||
ps.pool_address,
|
||||
pool_name,
|
||||
tick,
|
||||
price_0_1,
|
||||
price_1_0,
|
||||
virtual_liquidity_adjusted,
|
||||
virtual_reserves_token0_adjusted,
|
||||
virtual_reserves_token1_adjusted,
|
||||
token0_balance_adjusted,
|
||||
token1_balance_adjusted,
|
||||
token0_balance_usd,
|
||||
token1_balance_usd,
|
||||
token0_balance,
|
||||
token1_balance
|
||||
|
||||
FROM
|
||||
uniswapv3.pool_stats ps
|
||||
|
||||
JOIN
|
||||
max_blocks mb
|
||||
ON
|
||||
mb.block_id = ps.block_id
|
||||
AND
|
||||
mb.pool_address = ps.pool_address
|
||||
"))
|
||||
|
||||
|
||||
#tx_id, block_timestamp, pool_address, pool_name, price_1_0, price_0_1, tick, amount0_adjusted, amount1_adjusted, amount0_usd, amount1_usd
|
||||
swaps <- QuerySnowflake(paste0("SELECT
|
||||
*
|
||||
FROM
|
||||
uniswapv3.swaps sw
|
||||
WHERE
|
||||
block_timestamp > getdate() - interval '5 days' AND block_id <= ", max.pull.block))
|
||||
swaps[, swap_date := as.Date(substr(block_timestamp, 1, 10))]
|
||||
|
||||
# what is the price
|
||||
pool.stats[, token0_dec := log(token0_balance / token0_balance_adjusted, 10)]
|
||||
pool.stats[, token1_dec := log(token1_balance / token1_balance_adjusted, 10)]
|
||||
|
||||
pool.stats[, fee := as.numeric(strsplit(pool_name, " ", fixed = TRUE)[[1]][2]), by = pool_name ]
|
||||
pool.stats[, tick_spacing := as.numeric(strsplit(pool_name, " ", fixed = TRUE)[[1]][3])]
|
||||
|
||||
|
||||
pool.stats[, pool_dec_adj := token1_dec - token0_dec]
|
||||
|
||||
pool.stats <- pool.stats[!is.na(pool_dec_adj)]
|
||||
|
||||
swaps <- merge(swaps,
|
||||
pool.stats[, list(pool_address, pool_dec_adj)],
|
||||
by = "pool_address",
|
||||
all = FALSE)
|
||||
|
||||
#swaps[, price_0_1 := ifelse(tick >= 0, ((1.0001^tick) / (10^pool_dec_adj)), 1/((1.0001^tick) / (10^pool_dec_adj)))]
|
||||
#swaps[, price_1_0 := ifelse(tick >= 0, 1 / ((1.0001^tick) / (10^pool_dec_adj)), ((1.0001^tick) / (10^pool_dec_adj)) )]
|
||||
|
||||
swaps[, price_0_1 := 1 / ((1.0001^tick) / (10^pool_dec_adj))]
|
||||
swaps[, price_1_0 := ((1.0001^tick) / (10^pool_dec_adj))]
|
||||
|
||||
keep.pools <- swaps[, list(n_swaps = .N), by = pool_address][n_swaps > 50]$pool_address
|
||||
|
||||
|
||||
current.positions <- current.positions[pool_address %in% keep.pools]
|
||||
swaps <- swaps[pool_address %in% keep.pools]
|
||||
pool.stats <- pool.stats[pool_address %in% keep.pools]
|
||||
|
||||
pool.choices <- swaps[, .N, by = pool_name][order(-N)]
|
||||
pool.choices[,fancy_name := paste0(
|
||||
strsplit(pool_name, " ", fixed = TRUE)[[1]][1]," ",
|
||||
as.numeric(strsplit(pool_name, " ", fixed = TRUE)[[1]][2]) / 10000,
|
||||
"% fee",collapse = ""), by = pool_name ]
|
||||
pool.choices <- {
|
||||
.vec <- unique(pool.choices$pool_name)
|
||||
names(.vec) <- unique(pool.choices$fancy_name)
|
||||
.vec
|
||||
}
|
||||
|
||||
special.addresses <- QuerySnowflake("select address as pool_address,
|
||||
meta:token0 as token0,
|
||||
meta:token1 as token1
|
||||
from silver.ethereum_contracts where address in (select pool_address from uniswapv3.pools)")
|
||||
|
||||
save(pool.choices, current.positions, swaps, pool.stats, file = "shiny_location")
|
||||
|
||||
|
||||
BIN
uniswap_v3/www/Background_03.png
Normal file
BIN
uniswap_v3/www/Background_03.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 176 KiB |
BIN
uniswap_v3/www/fliplogo.jpg
Normal file
BIN
uniswap_v3/www/fliplogo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
128
uniswap_v3/www/shinycss.css
Normal file
128
uniswap_v3/www/shinycss.css
Normal file
@ -0,0 +1,128 @@
|
||||
|
||||
body {
|
||||
background: url('Background_03.png');
|
||||
background-attachment:fixed;
|
||||
background-size: auto 100%;
|
||||
background-position: center;
|
||||
padding-left: 10%;
|
||||
padding-right: 10%;
|
||||
}
|
||||
|
||||
#lildate {
|
||||
margin-top: -15px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 10px;
|
||||
color: #8A9391;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.smallstats {
|
||||
color: #8A9391;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.selectize-input {
|
||||
margin-bottom: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
.selectize-control {
|
||||
padding-bottom: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
|
||||
#fee {
|
||||
text-align: center;
|
||||
|
||||
height: calc(1.5em + .75rem + 2px);
|
||||
padding: .375rem .75rem;
|
||||
font-size: 1.03rem;
|
||||
font-weight: 900;
|
||||
line-height: 1.5;
|
||||
color: #9933cc;
|
||||
background-color: #e7d3f2;
|
||||
background-clip: padding-box;
|
||||
border: 2px solid #815c99;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
|
||||
.irs-handle {
|
||||
background-color: #acf3e4 !important;
|
||||
border: 1px solid #acf3e4 !important;
|
||||
}
|
||||
|
||||
#dates {
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#lcurveselect {
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#lpamt {
|
||||
width: 90%;
|
||||
text-align: center;
|
||||
|
||||
height: calc(1.5em + .75rem + 2px);
|
||||
padding: .375rem .75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
color: #55454E;
|
||||
background-color: #acf3e4;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #98879A;
|
||||
border-radius: .25rem;
|
||||
|
||||
}
|
||||
#moveprice {
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fa {
|
||||
vertical-align: top;
|
||||
size: 0.25em !important;
|
||||
}
|
||||
|
||||
.irs-bar--single {
|
||||
background-color: #e7d3f2 !important;
|
||||
border: 1px solid #d3bfdb !important;
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
|
||||
#disclaimer {
|
||||
text-align: justify;
|
||||
color: #5c5760;
|
||||
}
|
||||
|
||||
.centerit {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
#equals {
|
||||
size: 6em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-top: 0px !important;
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
#uni {
|
||||
font-size: 1.9em !important;
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user