Skip to main content

rustrade_data/exchange/gateio/
market.rs

1use super::Gateio;
2use crate::{Identifier, instrument::MarketInstrumentData, subscription::Subscription};
3use chrono::{
4    DateTime, Utc,
5    format::{DelayedFormat, StrftimeItems},
6};
7use rustrade_instrument::{
8    Keyed,
9    instrument::{
10        kind::option::OptionKind,
11        market_data::{MarketDataInstrument, kind::MarketDataInstrumentKind::*},
12    },
13};
14use serde::{Deserialize, Serialize};
15use smol_str::{SmolStr, StrExt, format_smolstr};
16
17/// Type that defines how to translate a Barter [`Subscription`] into a
18/// [`Gateio`] market that can be subscribed to.
19///
20/// See docs: <https://www.okx.com/docs-v5/en/#websocket-api-public-channel>
21#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
22pub struct GateioMarket(pub SmolStr);
23
24impl<Server, Kind> Identifier<GateioMarket>
25    for Subscription<Gateio<Server>, MarketDataInstrument, Kind>
26{
27    fn id(&self) -> GateioMarket {
28        gateio_market(&self.instrument)
29    }
30}
31
32impl<Server, InstrumentKey, Kind> Identifier<GateioMarket>
33    for Subscription<Gateio<Server>, Keyed<InstrumentKey, MarketDataInstrument>, Kind>
34{
35    fn id(&self) -> GateioMarket {
36        gateio_market(&self.instrument.value)
37    }
38}
39
40impl<Server, InstrumentKey, Kind> Identifier<GateioMarket>
41    for Subscription<Gateio<Server>, MarketInstrumentData<InstrumentKey>, Kind>
42{
43    fn id(&self) -> GateioMarket {
44        GateioMarket(self.instrument.name_exchange.name().clone())
45    }
46}
47
48impl AsRef<str> for GateioMarket {
49    fn as_ref(&self) -> &str {
50        &self.0
51    }
52}
53
54fn gateio_market(instrument: &MarketDataInstrument) -> GateioMarket {
55    let MarketDataInstrument { base, quote, kind } = instrument;
56
57    GateioMarket(
58        match kind {
59            // `Cfd` is unreachable -- Gateio lists no CFDs, so `exchange_supports_instrument_kind`
60            // denies the subscription first. `Identifier` is infallible, so the arm exists only to
61            // keep this match total, and takes the identically shaped spot form.
62            Spot | Perpetual | Cfd => format_smolstr!("{base}_{quote}"),
63            Future(contract) => {
64                format_smolstr!(
65                    "{base}_{quote}_QUARTERLY_{}",
66                    format_expiry(contract.expiry)
67                )
68            }
69            Option(contract) => format_smolstr!(
70                "{base}_{quote}-{}-{}-{}",
71                format_expiry(contract.expiry),
72                contract.strike,
73                match contract.kind {
74                    OptionKind::Call => "C",
75                    OptionKind::Put => "P",
76                },
77            ),
78        }
79        .to_uppercase_smolstr(),
80    )
81}
82
83/// Format the expiry `DateTime<Utc>` to be Gateio API compatible.
84///
85/// eg/ "20241231" (31st of December 2024)
86///
87/// See docs: <https://www.gate.io/docs/developers/options/ws/en/#public-contract-trades-channel>
88fn format_expiry<'a>(expiry: DateTime<Utc>) -> DelayedFormat<StrftimeItems<'a>> {
89    expiry.date_naive().format("%Y%m%d")
90}