barter_data/exchange/okx/
market.rs

1use super::Okx;
2use crate::{Identifier, instrument::MarketInstrumentData, subscription::Subscription};
3use barter_instrument::{
4    Keyed,
5    instrument::{
6        kind::option::OptionKind,
7        market_data::{MarketDataInstrument, kind::MarketDataInstrumentKind::*},
8    },
9};
10use chrono::{
11    DateTime, Utc,
12    format::{DelayedFormat, StrftimeItems},
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/// [`Okx`] 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 OkxMarket(pub SmolStr);
23
24impl<Kind> Identifier<OkxMarket> for Subscription<Okx, MarketDataInstrument, Kind> {
25    fn id(&self) -> OkxMarket {
26        okx_market(&self.instrument)
27    }
28}
29
30impl<InstrumentKey, Kind> Identifier<OkxMarket>
31    for Subscription<Okx, Keyed<InstrumentKey, MarketDataInstrument>, Kind>
32{
33    fn id(&self) -> OkxMarket {
34        okx_market(&self.instrument.value)
35    }
36}
37
38impl<InstrumentKey, Kind> Identifier<OkxMarket>
39    for Subscription<Okx, MarketInstrumentData<InstrumentKey>, Kind>
40{
41    fn id(&self) -> OkxMarket {
42        OkxMarket(self.instrument.name_exchange.name().clone())
43    }
44}
45
46impl AsRef<str> for OkxMarket {
47    fn as_ref(&self) -> &str {
48        &self.0
49    }
50}
51
52fn okx_market(instrument: &MarketDataInstrument) -> OkxMarket {
53    let MarketDataInstrument { base, quote, kind } = instrument;
54
55    OkxMarket(match kind {
56        Spot => format_smolstr!("{base}-{quote}").to_uppercase_smolstr(),
57        Future(contract) => format_smolstr!("{base}-{quote}-{}", format_expiry(contract.expiry))
58            .to_uppercase_smolstr(),
59        Perpetual => format_smolstr!("{base}-{quote}-SWAP").to_uppercase_smolstr(),
60        Option(contract) => format_smolstr!(
61            "{base}-{quote}-{}-{}-{}",
62            format_expiry(contract.expiry),
63            contract.strike,
64            match contract.kind {
65                OptionKind::Call => "C",
66                OptionKind::Put => "P",
67            },
68        )
69        .to_uppercase_smolstr(),
70    })
71}
72
73/// Format the expiry DateTime<Utc> to be Okx API compatible.
74///
75/// eg/ "230526" (26th of May 2023)
76///
77/// See docs: <https://www.okx.com/docs-v5/en/#rest-api-public-data-get-instruments>
78fn format_expiry<'a>(expiry: DateTime<Utc>) -> DelayedFormat<StrftimeItems<'a>> {
79    expiry.date_naive().format("%g%m%d")
80}