Skip to main content

mkt_types/
market.rs

1use derive_builder::Builder;
2use rust_decimal::Decimal;
3use std::fmt;
4use std::str::FromStr;
5
6use crate::ExchangeId;
7use strum_macros::{Display, EnumString};
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11
12#[non_exhaustive]
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub enum MarketFamily {
15    Spot,
16    Derivative,
17}
18
19#[non_exhaustive]
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
21pub enum SettlementMode {
22    Linear,
23    Inverse,
24}
25
26impl fmt::Display for SettlementMode {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        f.write_str(match self {
29            Self::Linear => "linear",
30            Self::Inverse => "inverse",
31        })
32    }
33}
34
35impl FromStr for SettlementMode {
36    type Err = MarketKindParseError;
37
38    fn from_str(value: &str) -> Result<Self, Self::Err> {
39        match value {
40            "linear" => Ok(Self::Linear),
41            "inverse" => Ok(Self::Inverse),
42            _ => Err(MarketKindParseError::Invalid(value.to_owned())),
43        }
44    }
45}
46
47#[non_exhaustive]
48#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
49pub enum ContractMaturity {
50    Perpetual,
51    Expiring,
52}
53
54impl fmt::Display for ContractMaturity {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        f.write_str(match self {
57            Self::Perpetual => "perpetual",
58            Self::Expiring => "expiring",
59        })
60    }
61}
62
63impl FromStr for ContractMaturity {
64    type Err = MarketKindParseError;
65
66    fn from_str(value: &str) -> Result<Self, Self::Err> {
67        match value {
68            "perpetual" => Ok(Self::Perpetual),
69            "expiring" | "future" => Ok(Self::Expiring),
70            _ => Err(MarketKindParseError::Invalid(value.to_owned())),
71        }
72    }
73}
74
75#[non_exhaustive]
76#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
77pub struct DerivativeKind {
78    pub maturity: ContractMaturity,
79    pub settlement: SettlementMode,
80}
81
82impl DerivativeKind {
83    pub const fn new(maturity: ContractMaturity, settlement: SettlementMode) -> Self {
84        Self {
85            maturity,
86            settlement,
87        }
88    }
89
90    pub const fn perpetual(settlement: SettlementMode) -> Self {
91        Self::new(ContractMaturity::Perpetual, settlement)
92    }
93
94    pub const fn expiring(settlement: SettlementMode) -> Self {
95        Self::new(ContractMaturity::Expiring, settlement)
96    }
97}
98
99impl fmt::Display for DerivativeKind {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        write!(f, "{}_{}", self.settlement, self.maturity)
102    }
103}
104
105impl FromStr for DerivativeKind {
106    type Err = MarketKindParseError;
107
108    fn from_str(value: &str) -> Result<Self, Self::Err> {
109        let (settlement, maturity) = value
110            .split_once('_')
111            .ok_or_else(|| MarketKindParseError::Invalid(value.to_owned()))?;
112
113        Ok(Self::new(maturity.parse()?, settlement.parse()?))
114    }
115}
116
117#[non_exhaustive]
118#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
119pub enum MarketKind {
120    Spot,
121    Derivative(DerivativeKind),
122}
123
124impl MarketKind {
125    pub const fn spot() -> Self {
126        Self::Spot
127    }
128
129    pub const fn derivative(kind: DerivativeKind) -> Self {
130        Self::Derivative(kind)
131    }
132
133    pub const fn linear_perpetual() -> Self {
134        Self::Derivative(DerivativeKind::perpetual(SettlementMode::Linear))
135    }
136
137    pub const fn inverse_perpetual() -> Self {
138        Self::Derivative(DerivativeKind::perpetual(SettlementMode::Inverse))
139    }
140
141    pub const fn linear_expiring() -> Self {
142        Self::Derivative(DerivativeKind::expiring(SettlementMode::Linear))
143    }
144
145    pub const fn inverse_expiring() -> Self {
146        Self::Derivative(DerivativeKind::expiring(SettlementMode::Inverse))
147    }
148
149    pub fn family(self) -> MarketFamily {
150        match self {
151            Self::Spot => MarketFamily::Spot,
152            Self::Derivative(_) => MarketFamily::Derivative,
153        }
154    }
155
156    pub fn is_derivative(self) -> bool {
157        matches!(self, Self::Derivative(_))
158    }
159
160    pub fn derivative_kind(self) -> Option<DerivativeKind> {
161        match self {
162            Self::Spot => None,
163            Self::Derivative(kind) => Some(kind),
164        }
165    }
166}
167
168impl fmt::Display for MarketKind {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        match self {
171            Self::Spot => f.write_str("spot"),
172            Self::Derivative(kind) => write!(f, "{kind}"),
173        }
174    }
175}
176
177impl FromStr for MarketKind {
178    type Err = MarketKindParseError;
179
180    fn from_str(value: &str) -> Result<Self, Self::Err> {
181        match value {
182            "spot" => Ok(Self::Spot),
183            other => Ok(Self::Derivative(other.parse()?)),
184        }
185    }
186}
187
188#[cfg(feature = "serde")]
189impl Serialize for MarketKind {
190    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
191    where
192        S: Serializer,
193    {
194        serializer.serialize_str(&self.to_string())
195    }
196}
197
198#[cfg(feature = "serde")]
199impl<'de> Deserialize<'de> for MarketKind {
200    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
201    where
202        D: Deserializer<'de>,
203    {
204        let value = <String as Deserialize>::deserialize(deserializer)?;
205        value.parse().map_err(serde::de::Error::custom)
206    }
207}
208
209#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
210#[non_exhaustive]
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub enum MarketKindParseError {
213    Invalid(String),
214}
215
216impl fmt::Display for MarketKindParseError {
217    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218        match self {
219            Self::Invalid(value) => write!(f, "invalid market kind: {value}"),
220        }
221    }
222}
223
224impl std::error::Error for MarketKindParseError {}
225
226#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
227#[non_exhaustive]
228#[derive(Debug, Clone, PartialEq, Eq, Hash)]
229pub struct Symbol {
230    pub kind: MarketKind,
231    pub venue_symbol: String,
232}
233
234impl Symbol {
235    pub fn spot(venue_symbol: impl Into<String>) -> Self {
236        Self {
237            kind: MarketKind::Spot,
238            venue_symbol: venue_symbol.into(),
239        }
240    }
241
242    pub fn derivative(kind: DerivativeKind, venue_symbol: impl Into<String>) -> Self {
243        Self {
244            kind: MarketKind::Derivative(kind),
245            venue_symbol: venue_symbol.into(),
246        }
247    }
248}
249
250#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
251#[non_exhaustive]
252#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
253#[strum(serialize_all = "snake_case", ascii_case_insensitive)]
254pub enum MarketStatus {
255    Trading,
256    Halted,
257    PreLaunch,
258    Delisted,
259}
260
261#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
262#[non_exhaustive]
263#[derive(Debug, Clone, PartialEq, Builder)]
264#[builder(pattern = "owned", setter(into))]
265pub struct PriceFilter {
266    #[builder(default)]
267    pub min_price: Option<Decimal>,
268    #[builder(default)]
269    pub max_price: Option<Decimal>,
270    #[builder(default)]
271    pub tick_size: Option<Decimal>,
272}
273
274impl PriceFilter {
275    pub fn builder() -> PriceFilterBuilder {
276        PriceFilterBuilder::default()
277    }
278}
279
280#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
281#[non_exhaustive]
282#[derive(Debug, Clone, PartialEq, Builder)]
283#[builder(pattern = "owned", setter(into))]
284pub struct LotSizeFilter {
285    #[builder(default)]
286    pub min_quantity: Option<Decimal>,
287    #[builder(default)]
288    pub max_quantity: Option<Decimal>,
289    #[builder(default)]
290    pub step_size: Option<Decimal>,
291}
292
293impl LotSizeFilter {
294    pub fn builder() -> LotSizeFilterBuilder {
295        LotSizeFilterBuilder::default()
296    }
297}
298
299#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
300#[non_exhaustive]
301#[derive(Debug, Clone, PartialEq, Builder)]
302#[builder(pattern = "owned", setter(into))]
303pub struct NotionalConstraints {
304    #[builder(default)]
305    pub min_notional: Option<Decimal>,
306    #[builder(default)]
307    pub max_notional: Option<Decimal>,
308    #[builder(default)]
309    pub apply_min_to_market: Option<bool>,
310    #[builder(default)]
311    pub apply_max_to_market: Option<bool>,
312}
313
314impl NotionalConstraints {
315    pub fn builder() -> NotionalConstraintsBuilder {
316        NotionalConstraintsBuilder::default()
317    }
318}
319
320#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
321#[non_exhaustive]
322#[derive(Debug, Clone, PartialEq, Builder, Default)]
323#[builder(pattern = "owned", setter(into))]
324pub struct TradingConstraints {
325    #[builder(default)]
326    pub allowed_order_types: Vec<crate::OrderType>,
327    #[builder(default)]
328    pub price_filter: Option<PriceFilter>,
329    #[builder(default)]
330    pub lot_size: Option<LotSizeFilter>,
331    #[builder(default)]
332    pub market_lot_size: Option<LotSizeFilter>,
333    #[builder(default)]
334    pub notional: Option<NotionalConstraints>,
335}
336
337impl TradingConstraints {
338    pub fn builder() -> TradingConstraintsBuilder {
339        TradingConstraintsBuilder::default()
340    }
341}
342
343#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
344#[non_exhaustive]
345#[derive(Debug, Clone, PartialEq, Builder)]
346#[builder(pattern = "owned", setter(into))]
347pub struct MarketInfo {
348    pub exchange_id: ExchangeId,
349    pub symbol: Symbol,
350    pub status: MarketStatus,
351    pub base_asset: String,
352    pub quote_asset: String,
353    #[builder(default)]
354    pub trading_constraints: TradingConstraints,
355}
356
357impl MarketInfo {
358    pub fn builder() -> MarketInfoBuilder {
359        MarketInfoBuilder::default()
360    }
361}