use serde::Deserialize;
use super::ExtraFields;
use crate::model::NumberString;
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SpreadOrderUpdate {
#[serde(default)]
pub sprd_id: String,
#[serde(default)]
pub ord_id: String,
#[serde(default)]
pub cl_ord_id: String,
#[serde(default)]
pub tag: String,
#[serde(default)]
pub px: NumberString,
#[serde(default)]
pub sz: NumberString,
#[serde(default)]
pub ord_type: String,
#[serde(default)]
pub side: String,
#[serde(default)]
pub fill_sz: NumberString,
#[serde(default)]
pub fill_px: NumberString,
#[serde(default)]
pub trade_id: String,
#[serde(default)]
pub acc_fill_sz: NumberString,
#[serde(default)]
pub pending_fill_sz: NumberString,
#[serde(default)]
pub pending_settle_sz: NumberString,
#[serde(default)]
pub canceled_sz: NumberString,
#[serde(default)]
pub avg_px: NumberString,
#[serde(default)]
pub state: String,
#[serde(default)]
pub cancel_source: String,
#[serde(default)]
pub req_id: String,
#[serde(default)]
pub amend_result: String,
#[serde(default)]
pub code: String,
#[serde(default)]
pub msg: String,
#[serde(default)]
pub c_time: NumberString,
#[serde(default)]
pub u_time: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SpreadTradeLeg {
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub px: NumberString,
#[serde(default)]
pub sz: NumberString,
#[serde(default)]
pub side: String,
#[serde(default)]
pub fill_pnl: NumberString,
#[serde(default)]
pub fee: NumberString,
#[serde(default)]
pub sz_cont: NumberString,
#[serde(default)]
pub fee_ccy: String,
#[serde(default)]
pub trade_id: String,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SpreadTradeUpdate {
#[serde(default)]
pub sprd_id: String,
#[serde(default)]
pub trade_id: String,
#[serde(default)]
pub ord_id: String,
#[serde(default)]
pub cl_ord_id: String,
#[serde(default)]
pub tag: String,
#[serde(default)]
pub fill_px: NumberString,
#[serde(default)]
pub fill_sz: NumberString,
#[serde(default)]
pub side: String,
#[serde(default)]
pub state: String,
#[serde(default)]
pub exec_type: String,
#[serde(default)]
pub ts: NumberString,
#[serde(default)]
pub legs: Vec<SpreadTradeLeg>,
#[serde(default)]
pub code: String,
#[serde(default)]
pub msg: String,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SpreadPublicTradeUpdate {
#[serde(default)]
pub sprd_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 ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SpreadTickerUpdate {
#[serde(default)]
pub sprd_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 vol24h: 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 SpreadPlaceOrderResult {
#[serde(default)]
pub cl_ord_id: String,
#[serde(default)]
pub ord_id: String,
#[serde(default)]
pub tag: String,
#[serde(default)]
pub s_code: String,
#[serde(default)]
pub s_msg: String,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SpreadAmendOrderResult {
#[serde(default)]
pub cl_ord_id: String,
#[serde(default)]
pub ord_id: String,
#[serde(default)]
pub req_id: String,
#[serde(default)]
pub s_code: String,
#[serde(default)]
pub s_msg: String,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SpreadCancelOrderResult {
#[serde(default)]
pub cl_ord_id: String,
#[serde(default)]
pub ord_id: String,
#[serde(default)]
pub s_code: String,
#[serde(default)]
pub s_msg: String,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SpreadMassCancelResult {
#[serde(default)]
pub result: bool,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_operation_result() {
let row: SpreadAmendOrderResult = serde_json::from_str(
r#"{"ordId":"1","clOrdId":"c","reqId":"r","sCode":"0","sMsg":""}"#,
)
.unwrap();
assert_eq!(row.s_code, "0");
}
#[test]
fn parses_spread_order_update() {
let row: SpreadOrderUpdate = serde_json::from_str(
r#"{
"sprdId":"BTC-USDT_BTC-USDT-SWAP","ordId":"312269865356374016","clOrdId":"b1",
"tag":"","px":"999","sz":"3","ordType":"limit","side":"buy","fillSz":"0",
"fillPx":"","tradeId":"","accFillSz":"0","pendingFillSz":"2","pendingSettleSz":"1",
"canceledSz":"1","state":"live","avgPx":"0","cancelSource":"","uTime":"1597026383085",
"cTime":"1597026383085","code":"0","msg":"","reqId":"","amendResult":"",
"instId":"ignored","pTime":"ignored"
}"#,
)
.unwrap();
assert_eq!(row.sprd_id, "BTC-USDT_BTC-USDT-SWAP");
assert_eq!(row.amend_result, "");
assert!(row.extra.contains_key("instId"));
assert!(row.extra.contains_key("pTime"));
}
#[test]
fn parses_spread_trade_update() {
let row: SpreadTradeUpdate = serde_json::from_str(
r#"{
"sprdId":"BTC-USDT-SWAP_BTC-USDT-200329","tradeId":"123","ordId":"123445",
"clOrdId":"b16","tag":"","fillPx":"999","fillSz":"3","state":"filled","side":"buy",
"execType":"M","ts":"1597026383085","legs":[],"code":"","msg":""
}"#,
)
.unwrap();
assert_eq!(row.sprd_id, "BTC-USDT-SWAP_BTC-USDT-200329");
assert_eq!(row.exec_type, "M");
assert_eq!(row.code, "");
assert_eq!(row.msg, "");
}
#[test]
fn parses_spread_public_trade_update() {
let row: SpreadPublicTradeUpdate = serde_json::from_str(
r#"{"sprdId":"BTC-USDT_BTC-USDT-SWAP","tradeId":"2499206329160695808","px":"-10","sz":"0.001","side":"sell","ts":"1726801105519"}"#,
).unwrap();
assert_eq!(row.sprd_id, "BTC-USDT_BTC-USDT-SWAP");
assert_eq!(row.px.as_str(), "-10");
assert_eq!(row.sz.as_str(), "0.001");
}
#[test]
fn parses_spread_ticker_update() {
let row: SpreadTickerUpdate = serde_json::from_str(
r#"{
"sprdId":"BTC-USDT_BTC-USDT-SWAP","last":"4","lastSz":"0.01","askPx":"19.7",
"askSz":"5.79","bidPx":"5.9","bidSz":"5.79","open24h":"-7","high24h":"19.6",
"low24h":"-7","vol24h":"9.87","ts":"1715247061026"
}"#,
)
.unwrap();
assert_eq!(row.sprd_id, "BTC-USDT_BTC-USDT-SWAP");
assert_eq!(row.last.as_str(), "4");
assert_eq!(row.ts.as_str(), "1715247061026");
}
}