use serde::Deserialize;
use super::ExtraFields;
use crate::model::NumberString;
pub(super) fn array_value(values: &[NumberString], index: usize) -> NumberString {
values.get(index).cloned().unwrap_or_default()
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct TickerUpdate {
#[serde(default)]
pub inst_type: String,
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub last: NumberString,
#[serde(default)]
pub last_sz: NumberString,
#[serde(default)]
pub ask_px: NumberString,
#[serde(default)]
pub ask_sz: NumberString,
#[serde(default)]
pub bid_px: NumberString,
#[serde(default)]
pub bid_sz: NumberString,
#[serde(default)]
pub open24h: NumberString,
#[serde(default)]
pub high24h: NumberString,
#[serde(default)]
pub low24h: NumberString,
#[serde(default)]
pub vol_ccy24h: NumberString,
#[serde(default)]
pub vol24h: NumberString,
#[serde(default)]
pub sod_utc0: NumberString,
#[serde(default)]
pub sod_utc8: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(from = "Vec<NumberString>", rename_all = "camelCase")]
#[non_exhaustive]
pub struct CandleUpdate {
pub ts: NumberString,
pub o: NumberString,
pub h: NumberString,
pub l: NumberString,
pub c: NumberString,
pub vol: NumberString,
pub vol_ccy: NumberString,
pub vol_ccy_quote: NumberString,
pub confirm: NumberString,
}
impl From<Vec<NumberString>> for CandleUpdate {
fn from(values: Vec<NumberString>) -> Self {
Self {
ts: array_value(&values, 0),
o: array_value(&values, 1),
h: array_value(&values, 2),
l: array_value(&values, 3),
c: array_value(&values, 4),
vol: array_value(&values, 5),
vol_ccy: array_value(&values, 6),
vol_ccy_quote: array_value(&values, 7),
confirm: array_value(&values, 8),
}
}
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct TradeUpdate {
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub trade_id: String,
#[serde(default)]
pub px: NumberString,
#[serde(default)]
pub sz: NumberString,
#[serde(default)]
pub side: String,
#[serde(default)]
pub source: String,
#[serde(default)]
pub count: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AllTradeUpdate {
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub trade_id: String,
#[serde(default)]
pub px: NumberString,
#[serde(default)]
pub sz: NumberString,
#[serde(default)]
pub side: String,
#[serde(default)]
pub source: String,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct OptionTradeUpdate {
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub inst_family: String,
#[serde(default)]
pub trade_id: String,
#[serde(default)]
pub px: NumberString,
#[serde(default)]
pub sz: NumberString,
#[serde(default)]
pub side: String,
#[serde(default)]
pub opt_type: String,
#[serde(default)]
pub fill_vol: NumberString,
#[serde(default)]
pub fwd_px: NumberString,
#[serde(default)]
pub idx_px: NumberString,
#[serde(default)]
pub mark_px: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct CallAuctionDetailsUpdate {
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub eq_px: NumberString,
#[serde(default)]
pub matched_sz: NumberString,
#[serde(default)]
pub unmatched_sz: NumberString,
#[serde(default)]
pub auction_end_time: NumberString,
#[serde(default)]
pub state: String,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct OrderBookUpdate {
#[serde(default)]
pub asks: Vec<BookLevel>,
#[serde(default)]
pub bids: Vec<BookLevel>,
#[serde(default)]
pub checksum: i64,
#[serde(default)]
pub prev_seq_id: i64,
#[serde(default)]
pub seq_id: i64,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(from = "Vec<NumberString>", rename_all = "camelCase")]
#[non_exhaustive]
pub struct BookLevel {
pub price: NumberString,
pub size: NumberString,
pub liquidated_order_count: NumberString,
pub order_count: NumberString,
}
impl From<Vec<NumberString>> for BookLevel {
fn from(values: Vec<NumberString>) -> Self {
Self {
price: array_value(&values, 0),
size: array_value(&values, 1),
liquidated_order_count: array_value(&values, 2),
order_count: array_value(&values, 3),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_ticker_and_retains_new_fields() {
let row: TickerUpdate = serde_json::from_str(
r#"{"instType":"SPOT","instId":"BTC-USDT","last":"1","ts":"2","futureField":"ok"}"#,
)
.unwrap();
assert_eq!(row.inst_id, "BTC-USDT");
assert_eq!(row.extra["futureField"], "ok");
}
#[test]
fn parses_market_candle_array() {
let row: CandleUpdate =
serde_json::from_str(r#"["1","2","3","4","5","6","7","8","1"]"#).unwrap();
assert_eq!(row.ts.as_str(), "1");
assert_eq!(row.vol.as_str(), "6");
assert_eq!(row.confirm.as_str(), "1");
}
#[test]
fn parses_all_trade_update() {
let row: AllTradeUpdate = serde_json::from_str(
r#"{"instId":"BTC-USDT","tradeId":"130639474","px":"42219.9","sz":"0.12060306","side":"buy","source":"0","ts":"1630048897897"}"#,
)
.unwrap();
assert_eq!(row.inst_id, "BTC-USDT");
assert_eq!(row.trade_id, "130639474");
assert_eq!(row.source, "0");
assert_eq!(row.ts.as_str(), "1630048897897");
}
#[test]
fn parses_option_trade_update() {
let row: OptionTradeUpdate = serde_json::from_str(
r#"{"fillVol":"0.5066007836914062","fwdPx":"16469.69928595038","idxPx":"16537.2","instFamily":"BTC-USD","instId":"BTC-USD-230224-18000-C","markPx":"0.04690107010619562","optType":"C","px":"0.045","side":"sell","sz":"2","tradeId":"38","ts":"1672286551080"}"#,
)
.unwrap();
assert_eq!(row.inst_id, "BTC-USD-230224-18000-C");
assert_eq!(row.inst_family, "BTC-USD");
assert_eq!(row.opt_type, "C");
assert_eq!(row.fill_vol.as_str(), "0.5066007836914062");
}
}