use serde::Deserialize;
use crate::model::NumberString;
use super::ExtraFields;
pub(super) fn array_value(values: &[NumberString], index: usize) -> NumberString {
values.get(index).cloned().unwrap_or_default()
}
ws_object! {
TickerUpdate {
inst_type: String,
inst_id: String,
last: NumberString,
last_sz: NumberString,
ask_px: NumberString,
ask_sz: NumberString,
bid_px: NumberString,
bid_sz: NumberString,
open24h: NumberString,
high24h: NumberString,
low24h: NumberString,
vol_ccy24h: NumberString,
vol24h: NumberString,
sod_utc0: NumberString,
sod_utc8: NumberString,
ts: NumberString
}
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(from = "Vec<NumberString>")]
#[non_exhaustive]
pub struct CandleUpdate {
pub ts: NumberString,
pub o: NumberString,
pub h: NumberString,
pub l: NumberString,
pub c: NumberString,
pub volume: NumberString,
pub volume_ccy: NumberString,
pub volume_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),
volume: array_value(&values, 5),
volume_ccy: array_value(&values, 6),
volume_quote: array_value(&values, 7),
confirm: array_value(&values, 8),
}
}
}
ws_object! {
TradeUpdate {
inst_id: String,
trade_id: String,
px: NumberString,
sz: NumberString,
side: String,
source: String,
count: NumberString,
seq_id: NumberString,
ts: NumberString
}
}
ws_object! {
OptionTradeUpdate {
inst_id: String,
inst_family: String,
trade_id: String,
px: NumberString,
sz: NumberString,
side: String,
opt_type: String,
fill_vol: NumberString,
fwd_px: NumberString,
idx_px: NumberString,
mark_px: NumberString,
ts: NumberString
}
}
ws_object! {
CallAuctionDetailsUpdate {
inst_id: String,
eq_px: NumberString,
matched_sz: NumberString,
unmatched_sz: NumberString,
auction_end_time: NumberString,
state: String,
ts: NumberString
}
}
#[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>")]
#[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.confirm.as_str(), "1");
}
}