feat: can query traders by quote volume
This commit is contained in:
parent
c01e53b41c
commit
4abadc9589
102
sqlx-data.json
102
sqlx-data.json
|
@ -1,5 +1,39 @@
|
|||
{
|
||||
"db": "PostgreSQL",
|
||||
"21b633d5aec33394129b051ea1df0ee9ab097626d74d8943f6323f9fb42723b5": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "open_orders_owner",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "raw_ask_size!",
|
||||
"ordinal": 1,
|
||||
"type_info": "Numeric"
|
||||
},
|
||||
{
|
||||
"name": "raw_bid_size!",
|
||||
"ordinal": 2,
|
||||
"type_info": "Numeric"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
null
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Timestamptz",
|
||||
"Timestamptz"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT \n open_orders_owner, \n sum(\n native_qty_received * CASE bid WHEN true THEN 0 WHEN false THEN 1 END\n ) as \"raw_ask_size!\",\n sum(\n native_qty_paid * CASE bid WHEN true THEN 1 WHEN false THEN 0 END\n ) as \"raw_bid_size!\"\n FROM fills\n WHERE market = $1\n AND time >= $2\n AND time < $3\n GROUP BY open_orders_owner\n ORDER BY \n sum(native_qty_received * CASE bid WHEN true THEN 0 WHEN false THEN 1 END) \n + \n sum(native_qty_paid * CASE bid WHEN true THEN 1 WHEN false THEN 0 END) \nDESC \nLIMIT 10000"
|
||||
},
|
||||
"35e8220c601aca620da1cfcb978c8b7a64dcbf15550521b418cf509015cd88d8": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
|
@ -269,6 +303,40 @@
|
|||
},
|
||||
"query": "SELECT \n start_time as \"start_time!\",\n end_time as \"end_time!\",\n resolution as \"resolution!\",\n market_name as \"market_name!\",\n open as \"open!\",\n close as \"close!\",\n high as \"high!\",\n low as \"low!\",\n volume as \"volume!\",\n complete as \"complete!\"\n from candles\n where market_name = $1\n and resolution = $2\n and start_time >= $3\n and end_time <= $4\n and complete = true\n ORDER BY start_time asc"
|
||||
},
|
||||
"aee3a3e04f837bd62e263452bfbaf5d7dff271799c80d5efd22a54954ac212c4": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "open_orders_owner",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "raw_ask_size!",
|
||||
"ordinal": 1,
|
||||
"type_info": "Numeric"
|
||||
},
|
||||
{
|
||||
"name": "raw_bid_size!",
|
||||
"ordinal": 2,
|
||||
"type_info": "Numeric"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
null
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Timestamptz",
|
||||
"Timestamptz"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT \n open_orders_owner, \n sum(\n native_qty_paid * CASE bid WHEN true THEN 0 WHEN false THEN 1 END\n ) as \"raw_ask_size!\",\n sum(\n native_qty_received * CASE bid WHEN true THEN 1 WHEN false THEN 0 END\n ) as \"raw_bid_size!\"\n FROM fills\n WHERE market = $1\n AND time >= $2\n AND time < $3\n GROUP BY open_orders_owner\n ORDER BY \n sum(native_qty_paid * CASE bid WHEN true THEN 0 WHEN false THEN 1 END) \n + \n sum(native_qty_received * CASE bid WHEN true THEN 1 WHEN false THEN 0 END) \nDESC \nLIMIT 10000"
|
||||
},
|
||||
"b259d64b388eb675b727ee511529472177b59ea616041360217afc2d928f33ed": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
|
@ -331,40 +399,6 @@
|
|||
},
|
||||
"query": "SELECT \n time as \"time!\",\n bid as \"bid!\",\n maker as \"maker!\",\n native_qty_paid as \"native_qty_paid!\",\n native_qty_received as \"native_qty_received!\",\n native_fee_or_rebate as \"native_fee_or_rebate!\" \n from fills \n where market = $1\n and time >= $2\n and time < $3 \n and maker = true\n ORDER BY time asc"
|
||||
},
|
||||
"dc367af7bb8da9d57033833da06567f3f3cfdcff72e5b140ccd3355f91b4c5a7": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "open_orders_owner",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "raw_ask_size!",
|
||||
"ordinal": 1,
|
||||
"type_info": "Numeric"
|
||||
},
|
||||
{
|
||||
"name": "raw_bid_size!",
|
||||
"ordinal": 2,
|
||||
"type_info": "Numeric"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
null
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Timestamptz",
|
||||
"Timestamptz"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT \n open_orders_owner, \n sum(\n native_qty_paid * CASE bid WHEN true THEN 0 WHEN false THEN 1 END\n ) as \"raw_ask_size!\",\n sum(\n native_qty_received * CASE bid WHEN true THEN 1 WHEN false THEN 0 END\n ) as \"raw_bid_size!\"\n FROM public.\"fills\"\n WHERE market = $1\n AND time >= $2\n AND time < $3\n GROUP BY open_orders_owner\n ORDER BY \n sum(native_qty_paid * CASE bid WHEN true THEN 0 WHEN false THEN 1 END) \n + \n sum(native_qty_received * CASE bid WHEN true THEN 1 WHEN false THEN 0 END) \nDESC "
|
||||
},
|
||||
"dc7c7c04b6870b9617e1e869aa4b7027baddaeeb22f2792f2e9c40f643f863c7": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
|
|
|
@ -2,11 +2,7 @@ use chrono::{DateTime, Utc};
|
|||
use sqlx::{Pool, Postgres};
|
||||
|
||||
use crate::{
|
||||
structs::{
|
||||
candle::Candle,
|
||||
openbook::{PgOpenBookFill, PgTrader},
|
||||
resolution::Resolution,
|
||||
},
|
||||
structs::{candle::Candle, openbook::PgOpenBookFill, resolution::Resolution, trader::PgTrader},
|
||||
utils::AnyhowWrap,
|
||||
};
|
||||
|
||||
|
@ -198,7 +194,7 @@ pub async fn fetch_tradingview_candles(
|
|||
.map_err_anyhow()
|
||||
}
|
||||
|
||||
pub async fn fetch_top_traders_by_volume_from(
|
||||
pub async fn fetch_top_traders_by_base_volume_from(
|
||||
pool: &Pool<Postgres>,
|
||||
market_address_string: &str,
|
||||
start_time: DateTime<Utc>,
|
||||
|
@ -214,7 +210,7 @@ pub async fn fetch_top_traders_by_volume_from(
|
|||
sum(
|
||||
native_qty_received * CASE bid WHEN true THEN 1 WHEN false THEN 0 END
|
||||
) as "raw_bid_size!"
|
||||
FROM public."fills"
|
||||
FROM fills
|
||||
WHERE market = $1
|
||||
AND time >= $2
|
||||
AND time < $3
|
||||
|
@ -223,7 +219,8 @@ pub async fn fetch_top_traders_by_volume_from(
|
|||
sum(native_qty_paid * CASE bid WHEN true THEN 0 WHEN false THEN 1 END)
|
||||
+
|
||||
sum(native_qty_received * CASE bid WHEN true THEN 1 WHEN false THEN 0 END)
|
||||
DESC "#,
|
||||
DESC
|
||||
LIMIT 10000"#,
|
||||
market_address_string,
|
||||
start_time,
|
||||
end_time
|
||||
|
@ -233,4 +230,38 @@ DESC "#,
|
|||
.map_err_anyhow()
|
||||
}
|
||||
|
||||
// pub async fn fetch_traders_above_x_dollars
|
||||
pub async fn fetch_top_traders_by_quote_volume_from(
|
||||
pool: &Pool<Postgres>,
|
||||
market_address_string: &str,
|
||||
start_time: DateTime<Utc>,
|
||||
end_time: DateTime<Utc>,
|
||||
) -> anyhow::Result<Vec<PgTrader>> {
|
||||
sqlx::query_as!(
|
||||
PgTrader,
|
||||
r#"SELECT
|
||||
open_orders_owner,
|
||||
sum(
|
||||
native_qty_received * CASE bid WHEN true THEN 0 WHEN false THEN 1 END
|
||||
) as "raw_ask_size!",
|
||||
sum(
|
||||
native_qty_paid * CASE bid WHEN true THEN 1 WHEN false THEN 0 END
|
||||
) as "raw_bid_size!"
|
||||
FROM fills
|
||||
WHERE market = $1
|
||||
AND time >= $2
|
||||
AND time < $3
|
||||
GROUP BY open_orders_owner
|
||||
ORDER BY
|
||||
sum(native_qty_received * CASE bid WHEN true THEN 0 WHEN false THEN 1 END)
|
||||
+
|
||||
sum(native_qty_paid * CASE bid WHEN true THEN 1 WHEN false THEN 0 END)
|
||||
DESC
|
||||
LIMIT 10000"#,
|
||||
market_address_string,
|
||||
start_time,
|
||||
end_time
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.map_err_anyhow()
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use actix_web::{
|
||||
get,
|
||||
middleware::Logger,
|
||||
web::{self, Data},
|
||||
App, HttpResponse, HttpServer, Responder,
|
||||
App, HttpServer,
|
||||
};
|
||||
use candles::get_candles;
|
||||
use dotenv;
|
||||
|
@ -12,14 +11,14 @@ use openbook_candles::{
|
|||
structs::markets::load_markets,
|
||||
utils::{Config, WebContext},
|
||||
};
|
||||
use sqlx::{Pool, Postgres};
|
||||
use traders::get_top_traders_by_base_volume;
|
||||
|
||||
use crate::traders::get_top_traders_by_quote_volume;
|
||||
|
||||
mod candles;
|
||||
mod server_error;
|
||||
mod traders;
|
||||
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
dotenv::dotenv().ok();
|
||||
|
@ -56,7 +55,7 @@ async fn main() -> std::io::Result<()> {
|
|||
web::scope("/api")
|
||||
.service(get_candles)
|
||||
.service(get_top_traders_by_base_volume)
|
||||
// .service(get_top_traders_by_quote_volume)
|
||||
.service(get_top_traders_by_quote_volume),
|
||||
)
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use crate::server_error::ServerError;
|
||||
use openbook_candles::{
|
||||
database::fetch::fetch_top_traders_by_volume_from,
|
||||
structs::openbook::{calculate_trader_volume, Trader},
|
||||
database::fetch::{
|
||||
fetch_top_traders_by_base_volume_from, fetch_top_traders_by_quote_volume_from,
|
||||
},
|
||||
structs::trader::{calculate_trader_volume, Trader, TraderResponse, VolumeType},
|
||||
utils::{to_timestampz, WebContext},
|
||||
};
|
||||
use {
|
||||
|
@ -29,19 +31,67 @@ pub async fn get_top_traders_by_base_volume(
|
|||
let from = to_timestampz(info.from);
|
||||
let to = to_timestampz(info.to);
|
||||
|
||||
let raw_traders =
|
||||
match fetch_top_traders_by_volume_from(&context.pool, &selected_market.address, from, to)
|
||||
.await
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(_) => return Err(ServerError::DbQueryError),
|
||||
};
|
||||
let raw_traders = match fetch_top_traders_by_base_volume_from(
|
||||
&context.pool,
|
||||
&selected_market.address,
|
||||
from,
|
||||
to,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(_) => return Err(ServerError::DbQueryError),
|
||||
};
|
||||
|
||||
let traders = raw_traders
|
||||
.into_iter()
|
||||
.map(|t| calculate_trader_volume(t, selected_market.base_decimals))
|
||||
.collect::<Vec<Trader>>();
|
||||
|
||||
// TODO: add start and end in response?
|
||||
Ok(HttpResponse::Ok().json(traders))
|
||||
let response = TraderResponse {
|
||||
start_time: info.from,
|
||||
end_time: info.to,
|
||||
traders: traders,
|
||||
volume_type: VolumeType::Base.to_string(),
|
||||
};
|
||||
Ok(HttpResponse::Ok().json(response))
|
||||
}
|
||||
|
||||
#[get("/traders/quote-volume")]
|
||||
pub async fn get_top_traders_by_quote_volume(
|
||||
info: web::Query<Params>,
|
||||
context: web::Data<WebContext>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
let selected_market = context.markets.iter().find(|x| x.name == info.market_name);
|
||||
if selected_market.is_none() {
|
||||
return Err(ServerError::MarketNotFound);
|
||||
}
|
||||
let selected_market = selected_market.unwrap();
|
||||
let from = to_timestampz(info.from);
|
||||
let to = to_timestampz(info.to);
|
||||
|
||||
let raw_traders = match fetch_top_traders_by_quote_volume_from(
|
||||
&context.pool,
|
||||
&selected_market.address,
|
||||
from,
|
||||
to,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(_) => return Err(ServerError::DbQueryError),
|
||||
};
|
||||
|
||||
let traders = raw_traders
|
||||
.into_iter()
|
||||
.map(|t| calculate_trader_volume(t, selected_market.quote_decimals))
|
||||
.collect::<Vec<Trader>>();
|
||||
|
||||
let response = TraderResponse {
|
||||
start_time: info.from,
|
||||
end_time: info.to,
|
||||
traders: traders,
|
||||
volume_type: VolumeType::Quote.to_string(),
|
||||
};
|
||||
Ok(HttpResponse::Ok().json(response))
|
||||
}
|
||||
|
|
|
@ -2,4 +2,5 @@ pub mod candle;
|
|||
pub mod markets;
|
||||
pub mod openbook;
|
||||
pub mod resolution;
|
||||
pub mod trader;
|
||||
pub mod tradingview;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use anchor_lang::{event, AnchorDeserialize, AnchorSerialize};
|
||||
use chrono::{DateTime, Utc};
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
use serde::Serialize;
|
||||
use num_traits::FromPrimitive;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use sqlx::types::Decimal;
|
||||
|
||||
|
@ -33,19 +32,6 @@ pub struct PgOpenBookFill {
|
|||
pub native_fee_or_rebate: Decimal,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PgTrader {
|
||||
pub open_orders_owner: String,
|
||||
pub raw_ask_size: Decimal,
|
||||
pub raw_bid_size: Decimal,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
pub struct Trader {
|
||||
pub pubkey: String,
|
||||
pub volume_base_units: f64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, AnchorDeserialize)]
|
||||
#[cfg_attr(target_endian = "little", derive(Debug))]
|
||||
#[repr(packed)]
|
||||
|
@ -129,17 +115,6 @@ pub fn calculate_fill_price_and_size(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn calculate_trader_volume(trader: PgTrader, base_decimals: u8) -> Trader {
|
||||
let bid_size = trader.raw_bid_size / token_factor(base_decimals);
|
||||
let ask_size = trader.raw_ask_size / token_factor(base_decimals);
|
||||
|
||||
Trader {
|
||||
pubkey: trader.open_orders_owner,
|
||||
volume_base_units: (bid_size + ask_size).to_f64().unwrap(),
|
||||
// TODO: quote volume
|
||||
}
|
||||
}
|
||||
|
||||
fn token_factor(decimals: u8) -> Decimal {
|
||||
pub fn token_factor(decimals: u8) -> Decimal {
|
||||
Decimal::from_u64(10u64.pow(decimals as u32)).unwrap()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
use std::fmt;
|
||||
|
||||
use num_traits::ToPrimitive;
|
||||
use serde::Serialize;
|
||||
use sqlx::types::Decimal;
|
||||
|
||||
use super::openbook::token_factor;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PgTrader {
|
||||
pub open_orders_owner: String,
|
||||
pub raw_ask_size: Decimal,
|
||||
pub raw_bid_size: Decimal,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
pub enum VolumeType {
|
||||
Base,
|
||||
Quote,
|
||||
}
|
||||
impl fmt::Display for VolumeType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
VolumeType::Base => write!(f, "Base"),
|
||||
VolumeType::Quote => write!(f, "Quote"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
pub struct Trader {
|
||||
pub pubkey: String,
|
||||
pub volume: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
pub struct TraderResponse {
|
||||
pub start_time: u64,
|
||||
pub end_time: u64,
|
||||
pub volume_type: String,
|
||||
pub traders: Vec<Trader>,
|
||||
}
|
||||
|
||||
pub fn calculate_trader_volume(trader: PgTrader, decimals: u8) -> Trader {
|
||||
let bid_size = trader.raw_bid_size / token_factor(decimals);
|
||||
let ask_size = trader.raw_ask_size / token_factor(decimals);
|
||||
|
||||
Trader {
|
||||
pubkey: trader.open_orders_owner,
|
||||
volume: (bid_size + ask_size).to_f64().unwrap(),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue