2023-05-07 14:43:55 -07:00
|
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
|
2023-05-05 18:41:17 -07:00
|
|
|
use crate::server_error::ServerError;
|
|
|
|
use actix_web::{get, web, HttpResponse, Scope};
|
|
|
|
use futures::join;
|
|
|
|
use openbook_candles::{
|
|
|
|
database::fetch::{fetch_coingecko_24h_high_low, fetch_coingecko_24h_volume},
|
2023-05-06 18:06:42 -07:00
|
|
|
structs::{
|
2023-05-07 14:43:55 -07:00
|
|
|
coingecko::{
|
|
|
|
CoinGecko24HourVolume, CoinGeckoOrderBook, CoinGeckoPair, CoinGeckoTicker,
|
|
|
|
PgCoinGecko24HighLow,
|
|
|
|
},
|
|
|
|
slab::{get_best_bids_and_asks, get_orderbooks_with_depth},
|
2023-05-05 18:41:17 -07:00
|
|
|
},
|
|
|
|
utils::WebContext,
|
|
|
|
};
|
2023-05-07 14:43:55 -07:00
|
|
|
use serde::Deserialize;
|
2023-05-05 18:41:17 -07:00
|
|
|
use solana_client::nonblocking::rpc_client::RpcClient;
|
|
|
|
|
|
|
|
pub fn service() -> Scope {
|
|
|
|
web::scope("/coingecko")
|
|
|
|
.service(pairs)
|
|
|
|
.service(tickers)
|
|
|
|
.service(orderbook)
|
|
|
|
}
|
|
|
|
|
2023-05-07 14:43:55 -07:00
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct OrderBookParams {
|
|
|
|
pub ticker_id: String, // market_name
|
|
|
|
pub depth: usize,
|
|
|
|
}
|
|
|
|
|
2023-05-05 18:41:17 -07:00
|
|
|
#[get("/pairs")]
|
|
|
|
pub async fn pairs(context: web::Data<WebContext>) -> Result<HttpResponse, ServerError> {
|
|
|
|
let markets = context.markets.clone();
|
|
|
|
|
|
|
|
let pairs = markets
|
|
|
|
.iter()
|
|
|
|
.map(|m| CoinGeckoPair {
|
|
|
|
ticker_id: m.name.clone(),
|
|
|
|
base: m.base_mint_key.clone(),
|
|
|
|
target: m.quote_mint_key.clone(),
|
|
|
|
pool_id: m.address.clone(),
|
|
|
|
})
|
|
|
|
.collect::<Vec<CoinGeckoPair>>();
|
|
|
|
|
|
|
|
Ok(HttpResponse::Ok().json(pairs))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/tickers")]
|
|
|
|
pub async fn tickers(context: web::Data<WebContext>) -> Result<HttpResponse, ServerError> {
|
2023-05-06 18:06:42 -07:00
|
|
|
let client = RpcClient::new(context.rpc_url.clone());
|
|
|
|
let markets = &context.markets;
|
|
|
|
|
|
|
|
let mut c1 = context.pool.acquire().await.unwrap();
|
|
|
|
let mut c2 = context.pool.acquire().await.unwrap();
|
2023-05-07 14:59:00 -07:00
|
|
|
// let bba_fut = get_best_bids_and_asks(client, markets);
|
2023-05-06 18:06:42 -07:00
|
|
|
let volume_fut = fetch_coingecko_24h_volume(&mut c1);
|
|
|
|
let high_low_fut = fetch_coingecko_24h_high_low(&mut c2);
|
|
|
|
|
2023-05-07 14:59:00 -07:00
|
|
|
let (volume_query, high_low_quey) =
|
|
|
|
join!(volume_fut, high_low_fut,);
|
2023-05-05 18:41:17 -07:00
|
|
|
|
2023-05-06 18:06:42 -07:00
|
|
|
let raw_volumes = match volume_query {
|
2023-05-05 18:41:17 -07:00
|
|
|
Ok(c) => c,
|
|
|
|
Err(_) => return Err(ServerError::DbQueryError),
|
|
|
|
};
|
2023-05-06 18:06:42 -07:00
|
|
|
let high_low = match high_low_quey {
|
2023-05-05 18:41:17 -07:00
|
|
|
Ok(c) => c,
|
|
|
|
Err(_) => return Err(ServerError::DbQueryError),
|
|
|
|
};
|
|
|
|
|
|
|
|
let default_hl = PgCoinGecko24HighLow::default();
|
|
|
|
let default_volume = CoinGecko24HourVolume::default();
|
|
|
|
let volumes: Vec<CoinGecko24HourVolume> = raw_volumes
|
|
|
|
.into_iter()
|
|
|
|
.map(|v| v.convert_to_readable(&markets))
|
|
|
|
.collect();
|
|
|
|
let tickers = markets
|
|
|
|
.iter()
|
2023-05-06 18:06:42 -07:00
|
|
|
.enumerate()
|
2023-05-07 14:59:00 -07:00
|
|
|
.map(|(_index, m)| {
|
2023-05-05 18:41:17 -07:00
|
|
|
let name = m.name.clone();
|
|
|
|
let high_low = high_low
|
|
|
|
.iter()
|
|
|
|
.find(|x| x.market_name == name)
|
|
|
|
.unwrap_or(&default_hl);
|
|
|
|
let volume = volumes
|
|
|
|
.iter()
|
|
|
|
.find(|x| x.market_name == name)
|
|
|
|
.unwrap_or(&default_volume);
|
|
|
|
CoinGeckoTicker {
|
|
|
|
ticker_id: m.name.clone(),
|
|
|
|
base_currency: m.base_mint_key.clone(),
|
|
|
|
target_currency: m.quote_mint_key.clone(),
|
2023-05-07 14:43:55 -07:00
|
|
|
last_price: high_low.close.to_string(),
|
|
|
|
base_volume: volume.base_volume.to_string(),
|
|
|
|
target_volume: volume.target_volume.to_string(),
|
|
|
|
high: high_low.high.to_string(),
|
|
|
|
low: high_low.low.to_string(),
|
2023-05-05 18:41:17 -07:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<CoinGeckoTicker>>();
|
|
|
|
|
|
|
|
Ok(HttpResponse::Ok().json(tickers))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/orderbook")]
|
2023-05-07 14:43:55 -07:00
|
|
|
pub async fn orderbook(
|
|
|
|
info: web::Query<OrderBookParams>,
|
|
|
|
context: web::Data<WebContext>,
|
|
|
|
) -> Result<HttpResponse, ServerError> {
|
2023-05-05 18:41:17 -07:00
|
|
|
let client = RpcClient::new(context.rpc_url.clone());
|
2023-05-07 14:43:55 -07:00
|
|
|
let market_name = &info.ticker_id;
|
|
|
|
let market = context
|
|
|
|
.markets
|
|
|
|
.iter()
|
|
|
|
.find(|m| m.name == *market_name)
|
|
|
|
.ok_or(ServerError::MarketNotFound)?;
|
|
|
|
let depth = info.depth;
|
|
|
|
|
|
|
|
let now = SystemTime::now();
|
|
|
|
let timestamp = now.duration_since(UNIX_EPOCH).unwrap().as_millis();
|
|
|
|
let (bid_levels, ask_levels) = get_orderbooks_with_depth(client, market, depth).await;
|
|
|
|
let result = CoinGeckoOrderBook {
|
|
|
|
timestamp: timestamp.to_string(),
|
|
|
|
ticker_id: market.name.clone(),
|
|
|
|
bids: bid_levels,
|
|
|
|
asks: ask_levels,
|
|
|
|
};
|
|
|
|
Ok(HttpResponse::Ok().json(result))
|
2023-05-05 18:41:17 -07:00
|
|
|
}
|