exc_binance/types/adaptations/
instrument.rs1use exc_core::{
2 types::instrument::{Attributes, FetchInstruments, InstrumentMeta},
3 Adaptor, ExchangeError,
4};
5use futures::{stream, StreamExt};
6use rust_decimal::Decimal;
7
8use crate::{
9 http::{
10 request::{self, Payload, RestRequest},
11 response::{
12 self,
13 instrument::{Filter, SymbolFilter},
14 },
15 },
16 Request,
17};
18
19impl Adaptor<FetchInstruments> for Request {
20 fn from_request(_req: FetchInstruments) -> Result<Self, ExchangeError>
21 where
22 Self: Sized,
23 {
24 Ok(Request::Http(RestRequest::from(Payload::new(
25 request::ExchangeInfo,
26 ))))
27 }
28
29 fn into_response(
30 resp: Self::Response,
31 ) -> Result<<FetchInstruments as exc_core::Request>::Response, ExchangeError> {
32 let info = resp.into_response::<response::ExchangeInfo>()?;
33 match info {
34 response::ExchangeInfo::UsdMarginFutures(info) => {
35 Ok(stream::iter(info.symbols.into_iter().filter_map(|symbol| {
36 let mut price_tick = None;
37 let mut size_tick = None;
38 let mut min_size = None;
39 let mut min_value = None;
40 for filter in &symbol.filters {
41 if let Filter::Symbol(filter) = filter {
42 match filter {
43 SymbolFilter::PriceFilter { tick_size, .. } => {
44 price_tick = Some(tick_size.normalize());
45 }
46 SymbolFilter::LotSize {
47 min_qty, step_size, ..
48 } => {
49 min_size = Some(min_qty.normalize());
50 size_tick = Some(step_size.normalize());
51 }
52 SymbolFilter::MinNotional { notional } => {
53 min_value = Some(notional);
54 }
55 _ => {}
56 }
57 }
58 }
59 let attrs = Attributes {
60 reversed: false,
61 unit: Decimal::ONE,
62 price_tick: price_tick?,
63 size_tick: size_tick?,
64 min_size: min_size?,
65 min_value: min_value.copied()?,
66 };
67 Some(Ok(InstrumentMeta::new(
68 symbol.symbol.to_lowercase(),
69 symbol
70 .to_exc_symbol()
71 .map_err(|err| {
72 tracing::debug!(%err, "cannot build exc symbol from {}", symbol.symbol);
73 err
74 })
75 .ok()?,
76 attrs,
77 ).with_live(symbol.is_live())))
78 }))
79 .boxed())
80 }
81 response::ExchangeInfo::Spot(info) => {
82 Ok(stream::iter(info.symbols.into_iter().filter_map(|symbol| {
83 let mut price_tick = None;
84 let mut size_tick = None;
85 let mut min_size = None;
86 let mut min_value = None;
87 for filter in &symbol.filters {
88 if let Filter::Symbol(filter) = filter {
89 match filter {
90 SymbolFilter::PriceFilter { tick_size, .. } => {
91 price_tick = Some(tick_size.normalize());
92 }
93 SymbolFilter::LotSize {
94 min_qty, step_size, ..
95 } => {
96 min_size = Some(min_qty.normalize());
97 size_tick = Some(step_size.normalize());
98 }
99 SymbolFilter::Notional { min_notional } => {
100 min_value = Some(min_notional.normalize());
101 }
102 _ => {}
103 }
104 }
105 }
106 tracing::debug!("{price_tick:?} {size_tick:?} {min_size:?} {min_value:?}");
107 let attrs = Attributes {
108 reversed: false,
109 unit: Decimal::ONE,
110 price_tick: price_tick?,
111 size_tick: size_tick?,
112 min_size: min_size?,
113 min_value: min_value?,
114 };
115 Some(Ok(InstrumentMeta::new(
116 symbol.symbol.to_lowercase(),
117 symbol
118 .to_exc_symbol()
119 .map_err(|err| {
120 tracing::debug!(%err, "cannot build exc symbol from {}", symbol.symbol);
121 err
122 })
123 .ok()?,
124 attrs,
125 ).with_live(symbol.is_live())))
126 }))
127 .boxed())
128 }
129 response::ExchangeInfo::EuropeanOptions(info) => {
130 Ok(stream::iter(info.option_symbols.into_iter().filter_map(|symbol| {
131 let mut size_tick = None;
132 for filter in symbol.filters.iter() {
133 if let Filter::Symbol(SymbolFilter::LotSize { step_size, .. }) = filter {
134 size_tick = Some(step_size.normalize());
135 }
136 }
137 let Some(size_tick) = size_tick else {
138 tracing::warn!("missing size tick for {}", symbol.symbol);
139 return None;
140 };
141 let attrs = Attributes { reversed: false, unit: Decimal::from(symbol.unit), price_tick: Decimal::new(1, symbol.price_scale), size_tick, min_size: symbol.min_qty, min_value: Decimal::ZERO };
142 let expire = symbol.expire_ts().map_err(|err| {
143 tracing::warn!(%err, "cannot parse expire for {}", symbol.symbol);
144 }).ok()?;
145 let meta = InstrumentMeta::new(&symbol.symbol, symbol.to_exc_symbol().map_err(|err| {
146 tracing::warn!(%err, "cannot build exc symbol from {}", symbol.symbol);
147 }).ok()?, attrs).with_live(symbol.is_live()).with_expire(expire);
148 Some(Ok(meta))
149 })).boxed())
150 }
151 }
152 }
153}