rustrade_data/exchange/gateio/perpetual/
trade.rs1use super::super::message::GateioMessage;
2use crate::{
3 Identifier,
4 event::{MarketEvent, MarketIter},
5 exchange::ExchangeSub,
6 subscription::trade::PublicTrade,
7};
8use chrono::{DateTime, Utc};
9use rust_decimal::Decimal;
10use rustrade_instrument::{Side, exchange::ExchangeId};
11use rustrade_integration::subscription::SubscriptionId;
12use serde::{Deserialize, Deserializer, Serialize};
13use smol_str::format_smolstr;
14
15fn de_i64_as_decimal<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
18where
19 D: Deserializer<'de>,
20{
21 i64::deserialize(deserializer).map(Decimal::from)
22}
23
24pub type GateioFuturesTrades = GateioMessage<Vec<GateioFuturesTradeInner>>;
27
28#[derive(Clone, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
58pub struct GateioFuturesTradeInner {
59 #[serde(rename = "contract")]
60 pub market: String,
61 #[serde(
62 rename = "create_time_ms",
63 deserialize_with = "rustrade_integration::serde::de::de_u64_epoch_ms_as_datetime_utc"
64 )]
65 pub time: DateTime<Utc>,
66 pub id: u64,
67 #[serde(deserialize_with = "rustrade_integration::serde::de::de_str")]
68 pub price: Decimal,
69 #[serde(rename = "size", deserialize_with = "de_i64_as_decimal")]
70 pub amount: Decimal,
71}
72
73impl Identifier<Option<SubscriptionId>> for GateioFuturesTrades {
74 fn id(&self) -> Option<SubscriptionId> {
75 self.data
76 .first()
77 .map(|trade| ExchangeSub::from((&self.channel, &trade.market)).id())
78 }
79}
80
81impl<InstrumentKey: Clone> From<(ExchangeId, InstrumentKey, GateioFuturesTrades)>
82 for MarketIter<InstrumentKey, PublicTrade>
83{
84 fn from(
85 (exchange, instrument, trades): (ExchangeId, InstrumentKey, GateioFuturesTrades),
86 ) -> Self {
87 trades
88 .data
89 .into_iter()
90 .map(|trade| {
91 Ok(MarketEvent {
92 time_exchange: trade.time,
93 time_received: Utc::now(),
94 exchange,
95 instrument: instrument.clone(),
96 kind: PublicTrade {
97 id: format_smolstr!("{}", trade.id),
98 price: trade.price,
99 amount: trade.amount.abs(),
100 side: Some(if trade.amount.is_sign_positive() {
101 Side::Buy
102 } else {
103 Side::Sell
104 }),
105 },
106 })
107 })
108 .collect()
109 }
110}
111
112#[cfg(test)]
113#[allow(clippy::unwrap_used)] mod tests {
115 use super::*;
116
117 mod de {
118 use super::*;
119
120 #[test]
121 fn test_gateio_message_perpetual_trade() {
122 let input = "{\"time\":1669843487,\"time_ms\":1669843487733,\"channel\":\"perpetual.trades\",\"event\":\"update\",\"result\":[{\"contract\":\"ETH_USDT\",\"create_time\":1669843487,\"create_time_ms\":1669843487724,\"id\":180276616,\"price\":\"1287\",\"size\":3}]}";
123 serde_json::from_str::<GateioFuturesTrades>(input).unwrap();
124 }
125
126 #[test]
127 fn test_gateio_message_futures_trade() {
128 let input = r#"
129 {
130 "channel": "futures.trades",
131 "event": "update",
132 "time": 1541503698,
133 "result": [
134 {
135 "size": -108,
136 "id": 27753479,
137 "create_time": 1545136464,
138 "create_time_ms": 1545136464123,
139 "price": "96.4",
140 "contract": "ETH_USDT_QUARTERLY_20201225"
141 }
142 ]
143 }"#;
144
145 serde_json::from_str::<GateioFuturesTrades>(input).unwrap();
146 }
147 }
148}