use crate::{packed_value, uuid_val};
use bytes::Bytes;
use chrono::prelude::*;
use enumflags2::{bitflags, BitFlags};
use fxhash::FxHashMap;
use netidx::chars::Chars;
use netidx_derive::Pack;
use rust_decimal::Decimal;
use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema, JsonSchema_repr};
use serde_derive::{Deserialize, Serialize};
use serde_json::json;
use std::{borrow::Borrow, cell::RefCell, ops::Deref};
use uuid::Uuid;
macro_rules! uuid_from_str {
($t:ident, $ns:ident) => {
impl<S: AsRef<str>> From<S> for $t {
fn from(s: S) -> Self {
Self(Uuid::new_v5(&$ns, s.as_ref().as_bytes()))
}
}
};
}
static PRODUCT_NS: Uuid = Uuid::from_bytes([
187, 37, 167, 167, 166, 28, 72, 90, 172, 41, 29, 227, 105, 166, 160, 67,
]);
uuid_val!(Product);
uuid_from_str!(Product, PRODUCT_NS);
static VENUE_NS: Uuid = Uuid::from_bytes([
221, 133, 166, 197, 180, 95, 70, 209, 191, 80, 121, 61, 172, 177, 229, 26,
]);
uuid_val!(Venue);
uuid_from_str!(Venue, VENUE_NS);
static ROUTE_NS: Uuid = Uuid::from_bytes([
12, 173, 188, 197, 152, 188, 72, 136, 148, 186, 251, 188, 182, 243, 145, 50,
]);
uuid_val!(Route);
uuid_from_str!(Route, ROUTE_NS);
static QUOTE_SYMBOL_NS: Uuid = Uuid::from_bytes([
56, 204, 134, 10, 56, 85, 79, 159, 186, 211, 188, 108, 94, 222, 114, 40,
]);
uuid_val!(QuoteSymbol);
uuid_from_str!(QuoteSymbol, QUOTE_SYMBOL_NS);
static TRADING_SYMBOL_NS: Uuid = Uuid::from_bytes([
67, 193, 253, 204, 40, 203, 73, 181, 138, 211, 133, 41, 143, 75, 137, 237,
]);
uuid_val!(TradingSymbol);
uuid_from_str!(TradingSymbol, TRADING_SYMBOL_NS);
static TRADABLE_PRODUCT_NS: Uuid = Uuid::from_bytes([
11, 254, 133, 140, 167, 73, 67, 169, 169, 158, 109, 31, 49, 167, 96, 173,
]);
uuid_val!(TradableProduct);
impl TradableProduct {
pub fn from_parts(base: &str, quote: &str, venue: &str, route: &str) -> Self {
thread_local! {
static BUF: RefCell<String> = RefCell::new(String::new());
}
BUF.with(|buf| {
let mut buf = buf.borrow_mut();
buf.clear();
buf.push_str(base);
buf.push('/');
buf.push_str(quote);
buf.push('*');
buf.push_str(venue);
buf.push('/');
buf.push_str(route);
Self(Uuid::new_v5(&TRADABLE_PRODUCT_NS, buf.as_bytes()))
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Pack, JsonSchema)]
pub enum OptionStyle {
American,
European,
}
packed_value!(OptionStyle);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Pack, JsonSchema)]
pub enum OptionDir {
Put,
Call,
}
packed_value!(OptionDir);
#[derive(
Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Pack, JsonSchema,
)]
pub struct TokenInfo {
pub token_address: Bytes,
pub token_decimals: u8,
}
packed_value!(TokenInfo);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Pack)]
#[serde(tag = "type", content = "value")]
pub enum ProductClass {
Coin {
token_info: FxHashMap<Venue, TokenInfo>,
},
Fiat,
Equity,
StableCoin {
fiat: Product,
token_info: FxHashMap<Venue, TokenInfo>,
},
Future {
underlying: Product,
settlement: Product,
expiration: DateTime<Utc>,
},
Option {
underlying: Product,
settlement: Product,
dir: OptionDir,
style: OptionStyle,
expiration: DateTime<Utc>,
strike: Decimal,
},
}
packed_value!(ProductClass);
impl ProductClass {
pub fn kind(&self) -> &'static str {
match self {
ProductClass::Coin { .. } => "Coin",
ProductClass::Fiat => "Fiat",
ProductClass::Equity => "Equity",
ProductClass::StableCoin { .. } => "StableCoin",
ProductClass::Future { .. } => "Future",
ProductClass::Option { .. } => "Option",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Pack)]
pub struct FlushId(Uuid);
packed_value!(FlushId);
impl FlushId {
pub fn new() -> FlushId {
FlushId(Uuid::new_v4())
}
}
#[bitflags]
#[repr(u64)]
#[derive(
Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, JsonSchema_repr,
)]
pub enum TradeFlags {
Disabled,
CancelOnly,
PostOnly,
LimitOnly,
MarginEnabled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Pack)]
pub struct TradeInfo {
pub trading_symbol: TradingSymbol,
pub quote_increment: Decimal,
pub base_increment: Decimal,
pub flags: BitFlags<TradeFlags>,
}
packed_value!(TradeInfo);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Pack)]
pub struct QuoteInfo {
pub primary: QuoteSymbol,
pub implied: Option<QuoteSymbol>,
pub l2: Option<QuoteSymbol>,
}
packed_value!(QuoteInfo);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Pack)]
pub enum SymbologyUpdateKind {
SetPriceInLimitDollars(Product, Decimal),
AddProduct {
name: Chars,
class: ProductClass,
price_in_limit_dollars: Decimal,
},
RemoveProduct(Product),
AddTradableProduct {
base: Product,
quote: Product,
venue: Venue,
route: Route,
quote_info: Option<QuoteInfo>,
trade_info: Option<TradeInfo>,
},
RemoveTradableProduct(TradableProduct),
AddRoute(Chars),
RemoveRoute(Route),
AddVenue(Chars),
RemoveVenue(Venue),
AddQuoteSymbol(Chars),
RemoveQuoteSymbol(QuoteSymbol),
AddTradingSymbol(Chars),
RemoveTradingSymbol(TradingSymbol),
Flush(FlushId),
Snapshot { original_length: usize, compressed: Bytes },
}
impl SymbologyUpdateKind {
pub fn recorded(&self) -> bool {
match self {
Self::SetPriceInLimitDollars(_, _)
| Self::AddProduct { .. }
| Self::RemoveProduct(_)
| Self::AddTradableProduct { .. }
| Self::RemoveTradableProduct(_)
| Self::AddRoute(_)
| Self::RemoveRoute(_)
| Self::AddVenue(_)
| Self::RemoveVenue(_)
| Self::AddQuoteSymbol(_)
| Self::RemoveQuoteSymbol(_)
| Self::AddTradingSymbol(_)
| Self::RemoveTradingSymbol(_)
| Self::Snapshot {..} => true,
Self::Flush(_) => false,
}
}
}
packed_value!(SymbologyUpdateKind);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Pack)]
pub struct SymbologyUpdate {
pub sequence_number: u64,
pub kind: SymbologyUpdateKind,
}
packed_value!(SymbologyUpdate);