1use derive_more::Display;
2use futures::stream::BoxStream;
3use positions::Instrument;
4use rust_decimal::Decimal;
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8use crate::{symbol::ExcSymbol, Str};
9use exc_service::{ExchangeError, Request};
10
11#[derive(Debug, Error)]
13pub enum InstrumentMetaError<E> {
14 #[error("parse num error: {0}")]
16 FromStrError(#[from] E),
17
18 #[error("missing fields")]
20 MissingFields,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Display)]
25#[display(bound = "Num: std::fmt::Display")]
26#[display(
27 fmt = "name={name} inst={inst} rev={} unit={} pt={} st={} ms={} mv={} live={live} will_expire={}",
28 "inst.is_prefer_reversed()",
29 "attrs.unit",
30 "attrs.price_tick",
31 "attrs.size_tick",
32 "attrs.min_size",
33 "attrs.min_value",
34 "expire.is_some()"
35)]
36#[serde(bound = "Num: num_traits::Zero + Serialize + for<'a> Deserialize<'a>")]
37pub struct InstrumentMeta<Num> {
38 name: Str,
40 inst: Instrument,
42 #[serde(flatten)]
44 attrs: Attributes<Num>,
45 #[serde(default = "live")]
47 live: bool,
48 #[serde(default)]
50 #[serde(with = "time::serde::rfc3339::option")]
51 expire: Option<time::OffsetDateTime>,
52}
53
54fn live() -> bool {
55 true
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
60#[serde(bound = "Num: num_traits::Zero + Serialize + for<'a> Deserialize<'a>")]
61pub struct Attributes<Num> {
62 pub reversed: bool,
64 pub unit: Num,
66 pub price_tick: Num,
68 pub size_tick: Num,
70 pub min_size: Num,
72 #[serde(default = "num_traits::Zero::zero")]
74 pub min_value: Num,
75}
76
77impl<Num> InstrumentMeta<Num> {
78 pub fn new(name: impl AsRef<str>, symbol: ExcSymbol, attrs: Attributes<Num>) -> Self {
80 let (base, quote, _) = symbol.to_parts();
81 let inst = Instrument::try_with_symbol(symbol.into(), &base, "e)
82 .expect("symbol must be valid")
83 .prefer_reversed(attrs.reversed);
84 Self {
85 name: Str::new(name),
86 inst,
87 attrs,
88 live: true,
89 expire: None,
90 }
91 }
92
93 pub fn name(&self) -> &str {
95 &self.name
96 }
97
98 pub fn smol_name(&self) -> &Str {
100 &self.name
101 }
102
103 pub fn instrument(&self) -> &Instrument {
105 &self.inst
106 }
107
108 pub fn attrs(&self) -> &Attributes<Num> {
110 &self.attrs
111 }
112
113 pub fn is_live(&self) -> bool {
115 self.live
116 }
117
118 pub fn expire(&self) -> Option<&time::OffsetDateTime> {
120 self.expire.as_ref()
121 }
122
123 pub fn with_live(mut self, live: bool) -> Self {
125 self.live = live;
126 self
127 }
128
129 pub fn with_expire(mut self, expire: impl Into<Option<time::OffsetDateTime>>) -> Self {
131 self.expire = expire.into();
132 self
133 }
134}
135
136pub type InstrumentStream = BoxStream<'static, Result<InstrumentMeta<Decimal>, ExchangeError>>;
138
139#[derive(Debug, Clone)]
141pub struct SubscribeInstruments {
142 pub tag: Str,
144}
145
146impl SubscribeInstruments {
147 pub fn new(tag: impl AsRef<str>) -> Self {
149 Self { tag: Str::new(tag) }
150 }
151}
152
153#[derive(Debug, Clone)]
155pub struct FetchInstruments {
156 pub tag: Str,
158}
159
160impl FetchInstruments {
161 pub fn new(tag: impl AsRef<str>) -> Self {
163 Self { tag: Str::new(tag) }
164 }
165}
166
167impl Request for SubscribeInstruments {
168 type Response = InstrumentStream;
169}
170
171impl Request for FetchInstruments {
172 type Response = InstrumentStream;
173}