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, Eq, Hash)]
323pub enum MarketQuantityMode {
324 Base,
325 Quote,
326}
327
328#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
329#[non_exhaustive]
330#[derive(Debug, Clone, PartialEq, Builder)]
331#[builder(pattern = "owned", setter(into))]
332pub struct QuantityModeSupport {
333 pub mode: MarketQuantityMode,
334 #[builder(default)]
335 pub order_types: Vec<crate::OrderType>,
336 #[builder(default)]
337 pub sides: Vec<crate::OrderSide>,
338}
339
340impl QuantityModeSupport {
341 pub fn builder() -> QuantityModeSupportBuilder {
342 QuantityModeSupportBuilder::default()
343 }
344}
345
346#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
347#[non_exhaustive]
348#[derive(Debug, Clone, PartialEq, Builder, Default)]
349#[builder(pattern = "owned", setter(into))]
350pub struct TradingPermissions {
351 #[builder(default)]
352 pub spot_order_entry_allowed: Option<bool>,
353 #[builder(default)]
354 pub supported_order_types: Vec<crate::OrderType>,
355 #[builder(default)]
356 pub quantity_mode_support: Vec<QuantityModeSupport>,
357}
358
359impl TradingPermissions {
360 pub fn builder() -> TradingPermissionsBuilder {
361 TradingPermissionsBuilder::default()
362 }
363}
364
365#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
366#[non_exhaustive]
367#[derive(Debug, Clone, PartialEq, Builder, Default)]
368#[builder(pattern = "owned", setter(into))]
369pub struct TradingConstraints {
370 #[builder(default)]
371 pub price_filter: Option<PriceFilter>,
372 #[builder(default)]
373 pub lot_size: Option<LotSizeFilter>,
374 #[builder(default)]
375 pub market_lot_size: Option<LotSizeFilter>,
376 #[builder(default)]
377 pub notional: Option<NotionalConstraints>,
378}
379
380impl TradingConstraints {
381 pub fn builder() -> TradingConstraintsBuilder {
382 TradingConstraintsBuilder::default()
383 }
384}
385
386#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
387#[non_exhaustive]
388#[derive(Debug, Clone, PartialEq, Builder)]
389#[builder(pattern = "owned", setter(into))]
390pub struct MarketInfo {
391 pub exchange_id: ExchangeId,
392 pub symbol: Symbol,
393 pub status: MarketStatus,
394 pub base_asset: String,
395 pub quote_asset: String,
396 #[builder(default)]
397 pub trading_permissions: TradingPermissions,
398 #[builder(default)]
399 pub trading_constraints: TradingConstraints,
400}
401
402impl MarketInfo {
403 pub fn builder() -> MarketInfoBuilder {
404 MarketInfoBuilder::default()
405 }
406}