Fix orderbook decimals

This commit is contained in:
Riordan Panayides 2023-01-11 15:41:19 +00:00
parent f45e614765
commit c838c58ca6
2 changed files with 110 additions and 63 deletions

View File

@ -15,12 +15,16 @@ use solana_sdk::{
use std::{ use std::{
borrow::BorrowMut, borrow::BorrowMut,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
time::{SystemTime, UNIX_EPOCH}, mem::size_of, mem::size_of,
time::{SystemTime, UNIX_EPOCH},
}; };
use crate::metrics::MetricU64; use crate::metrics::MetricU64;
use anchor_lang::AccountDeserialize; use anchor_lang::AccountDeserialize;
use mango_v4::{state::{BookSide, OrderTreeType}, serum3_cpi::OrderBookStateHeader}; use mango_v4::{
serum3_cpi::OrderBookStateHeader,
state::{BookSide, OrderTreeType},
};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum OrderbookSide { pub enum OrderbookSide {
@ -40,24 +44,7 @@ impl Serialize for OrderbookSide {
} }
} }
#[derive(Clone, Debug)] pub type OrderbookLevel = [f64; 2];
pub struct OrderbookLevel {
pub price: f64,
pub size: f64,
}
impl Serialize for OrderbookLevel {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("OrderbookLevel", 2)?;
state.serialize_field("price", &self.price)?;
state.serialize_field("size", &self.size)?;
state.end()
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct OrderbookUpdate { pub struct OrderbookUpdate {
@ -121,12 +108,57 @@ pub struct MarketConfig {
pub asks: Pubkey, pub asks: Pubkey,
pub base_decimals: u8, pub base_decimals: u8,
pub quote_decimals: u8, pub quote_decimals: u8,
pub base_lot_size: i64,
pub quote_lot_size: i64,
} }
pub fn native_to_ui(native: i64, decimals: u8) -> f64 { pub fn base_lots_to_ui(native: i64, base_decimals: u8, base_lot_size: i64) -> f64 {
native as f64 / (10u64.pow(decimals.into())) as f64 let decimals: u32 = 3;
let res = native as f64 / (10i64.pow(decimals.into()) as f64);
//info!("res {} native {} base_d {} base ls {}", res, native, base_decimals, base_lot_size);
res
} }
pub fn base_lots_to_ui_perp(native: i64, base_decimals: u8, base_lot_size: i64) -> f64 {
let decimals: u32 = 4;
let res = native as f64 / (10i64.pow(decimals.into()) as f64);
//info!("res {} native {} base_d {} base ls {}", res, native, base_decimals, base_lot_size);
res
}
pub fn price_lots_to_ui(
native: i64,
base_decimals: u8,
quote_decimals: u8,
) -> f64 {
let decimals = base_decimals - quote_decimals;
// let res = native as f64
// * ((10u64.pow(decimals.into()) * quote_lot_size as u64) as f64 / base_lot_size as f64)
// as f64;
let res = native as f64
/ (10u64.pow(decimals.into()))
as f64;
res
}
pub fn price_lots_to_ui_perp(
native: i64,
base_decimals: u8,
quote_decimals: u8,
base_lot_size: i64,
quote_lot_size: i64,
) -> f64 {
let decimals = base_decimals - quote_decimals;
let res = native as f64
* ((10u64.pow(decimals.into()) * quote_lot_size as u64) as f64 / base_lot_size as f64)
as f64;
// let res = native as f64
// / (10u64.pow(decimals.into()))
// as f64;
res
}
fn publish_changes( fn publish_changes(
slot: u64, slot: u64,
write_version: u64, write_version: u64,
@ -143,14 +175,12 @@ fn publish_changes(
for previous_order in previous_bookside.iter() { for previous_order in previous_bookside.iter() {
let peer = current_bookside let peer = current_bookside
.iter() .iter()
.find(|level| previous_order.price == level.price); .find(|level| previous_order[0] == level[0]);
match peer { match peer {
None => { None => {
update.push(OrderbookLevel { info!("removed level {}", previous_order[0]);
price: previous_order.price, update.push([previous_order[0], 0f64]);
size: 0f64,
});
} }
_ => continue, _ => continue,
} }
@ -160,21 +190,18 @@ fn publish_changes(
for current_order in current_bookside { for current_order in current_bookside {
let peer = previous_bookside let peer = previous_bookside
.iter() .iter()
.find(|item| item.price == current_order.price); .find(|item| item[0] == current_order[0]);
match peer { match peer {
Some(previous_order) => { Some(previous_order) => {
if previous_order.size == current_order.size { if previous_order[1] == current_order[1] {
continue; continue;
} }
debug!( info!("size changed {} -> {}", previous_order[1], current_order[1]);
"size changed {} -> {}",
previous_order.size, current_order.size
);
update.push(current_order.clone()); update.push(current_order.clone());
} }
None => { None => {
debug!("new level {},{}", current_order.price, current_order.size); info!("new level {},{}", current_order[0], current_order[1]);
update.push(current_order.clone()) update.push(current_order.clone())
} }
} }
@ -232,14 +259,12 @@ fn publish_changes_serum(
for previous_order in previous_bookside.iter() { for previous_order in previous_bookside.iter() {
let peer = current_bookside let peer = current_bookside
.iter() .iter()
.find(|level| previous_order.price == level.price); .find(|level| previous_order[0] == level[0]);
match peer { match peer {
None => { None => {
update.push(OrderbookLevel { info!("removed level s {}", previous_order[0]);
price: previous_order.price, update.push([previous_order[0], 0f64]);
size: 0f64,
});
} }
_ => continue, _ => continue,
} }
@ -249,21 +274,18 @@ fn publish_changes_serum(
for current_order in current_bookside { for current_order in current_bookside {
let peer = previous_bookside let peer = previous_bookside
.iter() .iter()
.find(|item| item.price == current_order.price); .find(|item| item[0] == current_order[0]);
match peer { match peer {
Some(previous_order) => { Some(previous_order) => {
if previous_order.size == current_order.size { if previous_order[1] == current_order[1] {
continue; continue;
} }
debug!( info!("size changed {} -> {}", previous_order[1], current_order[1]);
"size changed {} -> {}",
previous_order.size, current_order.size
);
update.push(current_order.clone()); update.push(current_order.clone());
} }
None => { None => {
debug!("new level {},{}", current_order.price, current_order.size); info!("new level {},{}", current_order[0], current_order[1]);
update.push(current_order.clone()) update.push(current_order.clone())
} }
} }
@ -412,14 +434,26 @@ pub async fn init(
.map(|item| (item.node.price_data() as i64, item.node.quantity)) .map(|item| (item.node.price_data() as i64, item.node.quantity))
.group_by(|(price, _)| *price) .group_by(|(price, _)| *price)
.into_iter() .into_iter()
.map(|(price, group)| OrderbookLevel { .map(|(price, group)| {
price: native_to_ui(price, mkt.1.quote_decimals), [
size: native_to_ui(group price_lots_to_ui_perp(
.map(|(_, quantity)| quantity) price,
.fold(0, |acc, x| acc + x), mkt.1.base_decimals), mkt.1.base_decimals,
mkt.1.quote_decimals,
mkt.1.base_lot_size,
mkt.1.quote_lot_size,
),
base_lots_to_ui_perp(
group
.map(|(_, quantity)| quantity)
.fold(0, |acc, x| acc + x),
mkt.1.base_decimals,
mkt.1.base_lot_size,
),
]
}) })
.collect(); .collect();
let other_bookside = bookside_cache.get(&other_side_pk.to_string()); let other_bookside = bookside_cache.get(&other_side_pk.to_string());
match bookside_cache.get(&side_pk_string) { match bookside_cache.get(&side_pk_string) {
@ -471,14 +505,26 @@ pub async fn init(
let bookside: Vec<OrderbookLevel> = slab let bookside: Vec<OrderbookLevel> = slab
.iter(side == 0) .iter(side == 0)
.map(|item| (u64::from(item.price()) as i64, item.quantity() as i64)) .map(|item| {
(u64::from(item.price()) as i64, item.quantity() as i64)
})
.group_by(|(price, _)| *price) .group_by(|(price, _)| *price)
.into_iter() .into_iter()
.map(|(price, group)| OrderbookLevel { .map(|(price, group)| {
price: native_to_ui(price, mkt.1.quote_decimals), [
size: native_to_ui(group price_lots_to_ui(
.map(|(_, quantity)| quantity) price,
.fold(0, |acc, x| acc + x), mkt.1.base_decimals), mkt.1.base_decimals,
mkt.1.quote_decimals,
),
base_lots_to_ui(
group
.map(|(_, quantity)| quantity)
.fold(0, |acc, x| acc + x),
mkt.1.base_decimals,
mkt.1.base_lot_size,
),
]
}) })
.collect(); .collect();
@ -504,10 +550,7 @@ pub async fn init(
_ => info!("bookside_cache could not find {}", side_pk_string), _ => info!("bookside_cache could not find {}", side_pk_string),
} }
serum_bookside_cache.insert( serum_bookside_cache.insert(side_pk_string.clone(), bookside);
side_pk_string.clone(),
bookside,
);
} }
Err(_) => info!("chain_cache could not find {}", side_pk), Err(_) => info!("chain_cache could not find {}", side_pk),
} }

View File

@ -307,6 +307,8 @@ async fn main() -> anyhow::Result<()> {
asks: context.market.asks, asks: context.market.asks,
base_decimals: context.market.base_decimals, base_decimals: context.market.base_decimals,
quote_decimals, quote_decimals,
base_lot_size: context.market.base_lot_size,
quote_lot_size: context.market.quote_lot_size,
}, },
) )
}) })
@ -325,13 +327,15 @@ async fn main() -> anyhow::Result<()> {
None => panic!("token not found for market") // todo: default to 6 for usdc? None => panic!("token not found for market") // todo: default to 6 for usdc?
}; };
( (
context.address, context.market.serum_market_external,
MarketConfig { MarketConfig {
name: context.market.name().to_owned(), name: context.market.name().to_owned(),
bids: context.bids, bids: context.bids,
asks: context.asks, asks: context.asks,
base_decimals, base_decimals,
quote_decimals, quote_decimals,
base_lot_size: context.pc_lot_size as i64,
quote_lot_size: context.coin_lot_size as i64,
}, },
) )
}) })