1use crate::{
4 FillSource, MarketUpdateStatus, OptionType, OrderStatus, OrderUpdateStatus, RfqStatus, Side,
5 TimeInForce, TradeSide, WalletAddress,
6};
7use rust_decimal::Decimal;
8use serde::{Deserialize, Deserializer, Serialize};
9
10fn default_true() -> bool {
11 true
12}
13
14fn string_decimal<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
17where
18 D: Deserializer<'de>,
19{
20 use serde::de;
21
22 struct StringDecimalVisitor;
23
24 impl<'de> de::Visitor<'de> for StringDecimalVisitor {
25 type Value = Decimal;
26
27 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
28 formatter.write_str("a string representing Decimal")
29 }
30
31 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
32 where
33 E: de::Error,
34 {
35 v.parse::<Decimal>().map_err(de::Error::custom)
36 }
37 }
38
39 deserializer.deserialize_str(StringDecimalVisitor)
40}
41
42fn option_string_decimal<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
45where
46 D: Deserializer<'de>,
47{
48 use serde::de;
49
50 struct OptionStringDecimalVisitor;
51
52 impl<'de> de::Visitor<'de> for OptionStringDecimalVisitor {
53 type Value = Option<Decimal>;
54
55 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
56 formatter.write_str("null or a string representing Decimal")
57 }
58
59 fn visit_none<E>(self) -> Result<Self::Value, E>
60 where
61 E: de::Error,
62 {
63 Ok(None)
64 }
65
66 fn visit_unit<E>(self) -> Result<Self::Value, E>
67 where
68 E: de::Error,
69 {
70 Ok(None)
71 }
72
73 fn visit_some<D2>(self, deserializer: D2) -> Result<Self::Value, D2::Error>
74 where
75 D2: Deserializer<'de>,
76 {
77 string_decimal(deserializer).map(Some)
78 }
79 }
80
81 deserializer.deserialize_option(OptionStringDecimalVisitor)
82}
83
84fn string_or_f64_default<'de, D>(deserializer: D) -> Result<f64, D::Error>
86where
87 D: Deserializer<'de>,
88{
89 use serde::de;
90
91 struct StringOrF64DefaultVisitor;
92
93 impl<'de> de::Visitor<'de> for StringOrF64DefaultVisitor {
94 type Value = f64;
95
96 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
97 formatter.write_str("a string, number, or null representing f64")
98 }
99
100 fn visit_none<E>(self) -> Result<Self::Value, E>
101 where
102 E: de::Error,
103 {
104 Ok(0.0)
105 }
106
107 fn visit_unit<E>(self) -> Result<Self::Value, E>
108 where
109 E: de::Error,
110 {
111 Ok(0.0)
112 }
113
114 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
115 where
116 E: de::Error,
117 {
118 Ok(v)
119 }
120
121 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
122 where
123 E: de::Error,
124 {
125 Ok(v as f64)
126 }
127
128 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
129 where
130 E: de::Error,
131 {
132 Ok(v as f64)
133 }
134
135 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
136 where
137 E: de::Error,
138 {
139 v.parse::<f64>().map_err(de::Error::custom)
140 }
141 }
142
143 deserializer.deserialize_any(StringOrF64DefaultVisitor)
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct ApiResponse<T> {
149 pub success: bool,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub data: Option<T>,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub error: Option<String>,
154}
155
156impl<T> ApiResponse<T> {
157 pub fn success(data: T) -> Self {
158 Self {
159 success: true,
160 data: Some(data),
161 error: None,
162 }
163 }
164
165 pub fn error(message: impl Into<String>) -> Self {
166 Self {
167 success: false,
168 data: None,
169 error: Some(message.into()),
170 }
171 }
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
177pub struct OrderInfo {
178 pub symbol: String,
180 #[serde(deserialize_with = "string_decimal")]
182 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
183 pub price: Decimal,
184 #[serde(deserialize_with = "string_decimal")]
186 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
187 pub size: Decimal,
188 pub side: Side,
190 pub tif: TimeInForce,
192 pub client_id: Option<String>,
194 pub order_id: Option<u64>,
196 pub is_perp: bool,
198 pub underlying: Option<String>,
200 pub reduce_only: Option<bool>,
202 pub nonce: Option<u64>,
204 pub signature: Option<String>,
206 #[serde(default)]
208 pub mmp_enabled: bool,
209 #[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
211 pub builder_code_address: Option<WalletAddress>,
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct OrderMessage {
217 pub order_id: Option<u64>,
218 #[serde(alias = "request")]
219 pub info: OrderInfo,
220 pub status: OrderStatus,
221 pub timestamp: u64,
222 pub reason: Option<String>,
223 #[serde(
224 default,
225 skip_serializing_if = "Option::is_none",
226 deserialize_with = "option_string_decimal"
227 )]
228 pub filled_size: Option<Decimal>,
229 #[serde(alias = "account")]
230 pub wallet_address: WalletAddress,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
235#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
236pub struct OrderUpdateMessage {
237 pub timestamp: u64,
239 pub info: OrderInfo,
241 pub status: OrderUpdateStatus,
243 pub reason: Option<String>,
245 #[serde(deserialize_with = "string_decimal")]
247 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
248 pub filled_size: Decimal,
249 pub order_id: Option<u64>,
251 #[serde(alias = "wallet", alias = "account")]
253 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
254 pub wallet_address: WalletAddress,
255 #[serde(default)]
257 pub mmp_triggered: bool,
258 #[serde(default, skip_serializing_if = "Option::is_none")]
261 pub request_id: Option<String>,
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize)]
266#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
267pub struct Market {
268 pub symbol: String,
270 pub underlying: String,
272 pub expiry: u64,
274 #[serde(deserialize_with = "string_decimal")]
276 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
277 pub strike: Decimal,
278 pub option_type: OptionType,
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize)]
284#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
285pub struct MarketUpdateMessage {
286 pub market: Market,
288 pub status: MarketUpdateStatus,
290 pub timestamp: u64,
292 #[serde(skip_serializing_if = "Option::is_none")]
294 pub reason: Option<String>,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
299#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
300pub struct MarketResponse {
301 pub success: bool,
302 pub message: String,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct Fill {
308 pub trade_id: u64,
309 pub taker_order_id: u64,
310 pub maker_order_id: u64,
311 pub symbol: String,
312 #[serde(deserialize_with = "string_decimal")]
313 pub price: Decimal,
314 #[serde(deserialize_with = "string_decimal")]
316 pub size: Decimal,
317 pub taker_side: Side,
318 pub taker_wallet_address: WalletAddress,
319 pub maker_wallet_address: WalletAddress,
320 #[serde(deserialize_with = "string_decimal")]
321 pub fee: Decimal,
322 pub is_taker: bool,
323 pub timestamp: u64,
324 pub builder_code_address: Option<WalletAddress>,
326 #[serde(default)]
328 pub builder_code_fee: Option<Decimal>,
329 #[serde(default)]
331 pub source: FillSource,
332 #[serde(default)]
335 pub taker_realized_pnl: Option<Decimal>,
336 #[serde(default)]
338 pub maker_realized_pnl: Option<Decimal>,
339 #[serde(default)]
341 pub underlying_notional: Option<Decimal>,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize)]
346pub struct OrderbookUpdate {
347 pub symbol: String,
348 pub bids: Vec<(Decimal, Decimal)>,
349 pub asks: Vec<(Decimal, Decimal)>,
350 pub timestamp: u64,
351}
352
353#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct L2Update {
356 #[serde(deserialize_with = "string_decimal")]
357 pub price: Decimal,
358 #[serde(deserialize_with = "string_decimal")]
359 pub size: Decimal, }
361
362#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct L2Message {
365 pub symbol: String,
366 pub bid_updates: Vec<L2Update>,
367 pub ask_updates: Vec<L2Update>,
368 pub timestamp: u64,
369 #[serde(default)]
370 #[serde(skip_serializing_if = "Option::is_none")]
371 pub sequence: Option<i64>,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
376pub struct TradeMessage {
377 pub symbol: String,
378 #[serde(deserialize_with = "string_decimal")]
379 pub price: Decimal,
380 #[serde(deserialize_with = "string_decimal")]
381 pub size: Decimal,
382 pub side: TradeSide,
383 pub timestamp: u64,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize)]
388pub struct BulkOrderResult {
389 pub index: usize,
390 pub success: bool,
391 #[serde(skip_serializing_if = "Option::is_none")]
392 pub data: Option<OrderMessage>,
393 #[serde(skip_serializing_if = "Option::is_none")]
394 pub error: Option<String>,
395}
396
397#[derive(Debug, Clone, Serialize, Deserialize)]
399pub struct BulkPlaceOrderResponse {
400 pub results: Vec<BulkOrderResult>,
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize)]
405pub struct BulkCancelOrderResponse {
406 pub results: Vec<BulkOrderResult>,
407}
408
409#[derive(Debug, Clone, Serialize, Deserialize)]
411#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
412pub struct Pagination {
413 pub limit: usize,
414 pub offset: usize,
415 pub count: usize,
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize)]
420#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
421pub struct CompetitionLeaderboardRow {
422 pub rank: usize,
423 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
424 pub wallet: WalletAddress,
425 pub username: String,
426 #[serde(deserialize_with = "string_decimal")]
427 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
428 pub pnl: Decimal,
429 #[serde(deserialize_with = "string_decimal")]
430 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
431 pub volume: Decimal,
432 #[serde(deserialize_with = "string_decimal")]
433 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
434 pub efficiency: Decimal,
435 pub medal: Option<u8>,
436}
437
438#[derive(Debug, Clone, Serialize, Deserialize)]
440#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
441pub struct CompetitionConnectedUserRank {
442 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
443 pub wallet: WalletAddress,
444 pub username: String,
445 pub rank: Option<usize>,
446 #[serde(default, deserialize_with = "option_string_decimal")]
447 #[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
448 pub pnl: Option<Decimal>,
449 #[serde(default, deserialize_with = "option_string_decimal")]
450 #[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
451 pub volume: Option<Decimal>,
452 #[serde(default, deserialize_with = "option_string_decimal")]
453 #[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
454 pub efficiency: Option<Decimal>,
455 pub medal: Option<u8>,
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize)]
460#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
461pub struct CompetitionLeaderboardResponse {
462 pub success: bool,
463 pub competition_id: i64,
464 pub sort_by: String,
465 pub sort_order: String,
466 #[serde(default)]
467 pub data: Vec<CompetitionLeaderboardRow>,
468 #[serde(default)]
469 pub connected_user: Option<CompetitionConnectedUserRank>,
470 pub pagination: Pagination,
471}
472
473#[derive(Debug, Clone, Serialize, Deserialize)]
475#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
476pub struct CompetitionPnlStanding {
477 pub competition_id: i64,
478 pub competition_name: String,
479 pub competition_state: String,
480 pub rank: Option<usize>,
481 #[serde(deserialize_with = "string_decimal")]
482 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
483 pub pnl: Decimal,
484 #[serde(deserialize_with = "string_decimal")]
485 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
486 pub volume: Decimal,
487 #[serde(deserialize_with = "string_decimal")]
488 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
489 pub efficiency: Decimal,
490 pub medal: Option<u8>,
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
495#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
496pub struct CompetitionPnlSummary {
497 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
498 pub wallet: WalletAddress,
499 #[serde(deserialize_with = "string_decimal")]
500 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
501 pub lifetime_realized_pnl: Decimal,
502 #[serde(default)]
503 pub active_competition: Option<CompetitionPnlStanding>,
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
508#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
509pub struct CompetitionPnlSummaryResponse {
510 pub success: bool,
511 pub data: CompetitionPnlSummary,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize)]
516#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
517pub struct CompetitionAccountPnl {
518 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
519 pub wallet: WalletAddress,
520 pub username: String,
521 #[serde(deserialize_with = "string_decimal")]
522 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
523 pub lifetime_realized_pnl: Decimal,
524 pub competition: CompetitionPnlStanding,
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
530pub struct CompetitionAccountResponse {
531 pub success: bool,
532 pub data: CompetitionAccountPnl,
533}
534
535#[derive(Debug, Clone, Serialize, Deserialize)]
537pub struct OrdersResponse {
538 pub success: bool,
539 pub data: Vec<OrderInfo>,
540 pub pagination: Pagination,
541}
542
543#[derive(Debug, Clone, Default, Serialize, Deserialize)]
545pub struct OptionSummary {
546 #[serde(default, deserialize_with = "string_or_f64_default")]
547 pub underlying_price: f64,
548 pub option_token_address: Option<WalletAddress>,
549 #[serde(default)]
550 pub greeks: Option<OptionGreeks>,
551}
552
553#[derive(Debug, Clone, Default, Serialize, Deserialize)]
554pub struct OptionGreeks {
555 #[serde(default, deserialize_with = "string_or_f64_default")]
556 pub delta: f64,
557 #[serde(default, deserialize_with = "string_or_f64_default")]
558 pub gamma: f64,
559 #[serde(default, deserialize_with = "string_or_f64_default")]
560 pub theta: f64,
561 #[serde(default, deserialize_with = "string_or_f64_default")]
562 pub vega: f64,
563 #[serde(default, deserialize_with = "string_or_f64_default")]
564 pub rho: f64,
565}
566
567#[derive(Debug, Clone, Serialize, Deserialize)]
569#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
570pub struct JsonRpcError {
571 pub code: i32,
573 pub message: String,
575 pub data: Option<serde_json::Value>,
577}
578
579#[derive(Debug, Clone, Serialize, Deserialize)]
581#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
582pub struct JsonRpcResponse<T> {
583 pub jsonrpc: String,
585 #[serde(skip_serializing_if = "Option::is_none")]
587 pub result: Option<T>,
588 #[serde(skip_serializing_if = "Option::is_none")]
590 pub error: Option<JsonRpcError>,
591 pub testnet: bool,
593 #[serde(rename = "usDiff")]
595 pub us_diff: i64,
596 #[serde(rename = "usIn")]
598 pub us_in: i64,
599 #[serde(rename = "usOut")]
601 pub us_out: i64,
602}
603
604#[derive(Debug, Clone, Serialize, Deserialize)]
606#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
607pub struct ApproveAgentResponse {
608 pub success: bool,
610 pub error: Option<String>,
612}
613
614#[derive(Debug, Clone, Serialize, Deserialize)]
616#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
617pub struct RevokeAgentResponse {
618 pub success: bool,
620 pub error: Option<String>,
622}
623
624#[derive(Debug, Clone, Serialize, Deserialize)]
626#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
627pub struct AuthorizedAgentsResponse {
628 #[cfg_attr(feature = "utoipa", schema(value_type = Vec<String>))]
630 pub agents: Vec<WalletAddress>,
631}
632
633#[derive(Debug, Clone, Serialize, Deserialize)]
635#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
636pub struct TickSizeStep {
637 pub tick_size: f64,
639 pub above_price: f64,
641}
642
643#[derive(Debug, Clone, Serialize, Deserialize)]
645#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
646pub struct InstrumentResponse {
647 pub price_index: String,
649 pub rfq: bool,
651 #[serde(default = "default_true")]
653 pub orderbook: bool,
654 pub kind: String,
656 pub instrument_name: String,
658 #[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
660 pub option_token_address: Option<WalletAddress>,
661 pub maker_commission: f64,
663 pub taker_commission: f64,
665 pub instrument_type: String,
667 pub expiration_timestamp: i64,
669 pub creation_timestamp: i64,
671 pub is_active: bool,
673 pub option_type: String,
675 pub contract_size: f64,
677 pub tick_size: f64,
679 pub strike: f64,
681 pub instrument_id: i32,
683 pub settlement_period: String,
685 pub min_trade_amount: f64,
687 pub block_trade_commission: f64,
689 pub block_trade_min_trade_amount: f64,
691 pub block_trade_tick_size: f64,
693 pub settlement_currency: String,
695 pub base_currency: String,
697 pub counter_currency: String,
699 pub quote_currency: String,
701 pub tick_size_steps: Vec<TickSizeStep>,
703}
704
705#[derive(Debug, Clone, Serialize, Deserialize)]
708#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
709pub struct InstrumentSpecResponse {
710 pub instrument_id: String,
712 pub instrument_numeric_id: i32,
714 pub exchange_symbol: String,
716 pub sym: String,
718 pub exchange: String,
720 pub instrument_kind: String,
722 pub option_kind: Option<String>,
724 pub delivery: Option<String>,
726 pub settle_asset: Option<String>,
728 pub base_asset: String,
730 pub quote_asset: String,
732 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
734 pub strike: Decimal,
735 pub expiry_ns: i64,
737 pub settlement_hour_utc: Option<u8>,
739 pub settlement_time_utc: Option<String>,
742 pub contract_size: f64,
744 pub min_trade_size: f64,
746 pub tick_size: f64,
748 pub price_decimals: Option<u32>,
750 pub size_decimals: Option<u32>,
752 pub min_price_increment_bands: Vec<TickSizeStep>,
754 pub state: String,
756 pub is_tradable: bool,
758 pub listed_time_ns: Option<i64>,
760 pub event_ts_ns: Option<i64>,
764 pub maker_fee_bps: Option<f64>,
766 pub taker_fee_bps: Option<f64>,
768 pub initial_margin_fraction: Option<f64>,
770 pub maintenance_margin_fraction: Option<f64>,
772 pub position_limit: Option<f64>,
774 #[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
776 pub option_token_address: Option<WalletAddress>,
777 pub settlement_oracle: Option<String>,
779 pub condition_id: Option<String>,
781 pub underlying_resolution_source: Option<String>,
783}
784
785#[derive(Debug, Clone, Serialize, Deserialize)]
787#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
788pub struct OrderBookStats {
789 pub high: Option<f64>,
791 pub low: Option<f64>,
793 pub price_change: Option<f64>,
795 pub volume: f64,
797 pub volume_usd: f64,
799}
800
801#[derive(Debug, Clone, Serialize, Deserialize)]
803#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
804pub struct OrderBookGreeks {
805 pub delta: f64,
807 pub gamma: f64,
809 pub vega: f64,
811 pub theta: f64,
813 pub rho: f64,
815}
816
817#[derive(Debug, Clone, Serialize, Deserialize)]
819#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
820pub struct OrderBookResponse {
821 pub timestamp: i64,
823 pub state: String,
825 pub stats: OrderBookStats,
827 #[serde(skip_serializing_if = "Option::is_none")]
829 pub greeks: Option<OrderBookGreeks>,
830 pub change_id: i64,
832 pub index_price: f64,
834 pub instrument_name: String,
836 #[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
838 pub option_token_address: Option<WalletAddress>,
839 pub bids: Vec<[f64; 2]>,
841 pub asks: Vec<[f64; 2]>,
843 pub last_price: Option<f64>,
845 pub settlement_price: f64,
847 pub min_price: f64,
849 pub max_price: f64,
851 pub open_interest: f64,
853 pub mark_price: f64,
855 #[serde(default, skip_serializing_if = "Option::is_none")]
857 pub theoretical_price: Option<f64>,
858 pub best_bid_price: f64,
860 pub best_ask_price: f64,
862 #[serde(default, skip_serializing_if = "Option::is_none")]
864 pub mark_iv: Option<f64>,
865 #[serde(default, skip_serializing_if = "Option::is_none")]
867 pub ask_iv: Option<f64>,
868 #[serde(default, skip_serializing_if = "Option::is_none")]
870 pub bid_iv: Option<f64>,
871 pub underlying_price: f64,
873 pub underlying_index: String,
875 pub interest_rate: f64,
877 pub estimated_delivery_price: f64,
879 pub best_ask_amount: f64,
881 pub best_bid_amount: f64,
883}
884
885pub const HISTORICAL_PNL_INTERVAL_5M_MS: i64 = 5 * 60 * 1000;
891pub const HISTORICAL_PNL_INTERVAL_1H_MS: i64 = 60 * 60 * 1000;
893pub const HISTORICAL_PNL_INTERVAL_1D_MS: i64 = 24 * 60 * 60 * 1000;
895
896#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
898#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
899pub enum HistoricalPnlInterval {
900 #[serde(rename = "5m")]
902 FiveMinutes,
903 #[serde(rename = "1h")]
905 OneHour,
906 #[serde(rename = "1d")]
908 OneDay,
909}
910
911impl HistoricalPnlInterval {
912 pub fn as_ms(self) -> i64 {
914 match self {
915 Self::FiveMinutes => HISTORICAL_PNL_INTERVAL_5M_MS,
916 Self::OneHour => HISTORICAL_PNL_INTERVAL_1H_MS,
917 Self::OneDay => HISTORICAL_PNL_INTERVAL_1D_MS,
918 }
919 }
920
921 pub fn as_str(self) -> &'static str {
923 match self {
924 Self::FiveMinutes => "5m",
925 Self::OneHour => "1h",
926 Self::OneDay => "1d",
927 }
928 }
929}
930
931#[derive(Debug, Clone, Serialize, Deserialize)]
933#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
934pub struct HistoricalPnlPoint {
935 pub timestamp: i64,
937 #[serde(deserialize_with = "string_decimal")]
939 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
940 pub equity: Decimal,
941 #[serde(default, skip_serializing_if = "Option::is_none")]
943 pub attribution: Option<std::collections::HashMap<String, [f64; 5]>>,
944 #[serde(
949 default,
950 skip_serializing_if = "Option::is_none",
951 deserialize_with = "option_string_decimal"
952 )]
953 #[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
954 pub net_deposits: Option<Decimal>,
955}
956
957#[derive(Debug, Clone, Serialize, Deserialize)]
959#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
960pub struct HistoricalPnlResponse {
961 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
963 pub wallet_address: WalletAddress,
964 pub interval: HistoricalPnlInterval,
966 #[serde(default)]
968 pub points: Vec<HistoricalPnlPoint>,
969}
970
971pub const HISTORICAL_THEO_INTERVAL_5M_MS: i64 = HISTORICAL_PNL_INTERVAL_5M_MS;
973pub const HISTORICAL_THEO_INTERVAL_1H_MS: i64 = HISTORICAL_PNL_INTERVAL_1H_MS;
975pub const HISTORICAL_THEO_INTERVAL_1D_MS: i64 = HISTORICAL_PNL_INTERVAL_1D_MS;
977
978#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
980#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
981pub enum HistoricalTheoInterval {
982 #[serde(rename = "5m")]
984 FiveMinutes,
985 #[serde(rename = "1h")]
987 OneHour,
988 #[serde(rename = "1d")]
990 OneDay,
991}
992
993impl HistoricalTheoInterval {
994 pub fn as_ms(self) -> i64 {
996 match self {
997 Self::FiveMinutes => HISTORICAL_THEO_INTERVAL_5M_MS,
998 Self::OneHour => HISTORICAL_THEO_INTERVAL_1H_MS,
999 Self::OneDay => HISTORICAL_THEO_INTERVAL_1D_MS,
1000 }
1001 }
1002
1003 pub fn as_str(self) -> &'static str {
1005 match self {
1006 Self::FiveMinutes => "5m",
1007 Self::OneHour => "1h",
1008 Self::OneDay => "1d",
1009 }
1010 }
1011}
1012
1013#[derive(Debug, Clone, Serialize, Deserialize)]
1015#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1016pub struct HistoricalTheoPoint {
1017 pub timestamp: i64,
1019 pub theoretical_price: f64,
1021}
1022
1023#[derive(Debug, Clone, Serialize, Deserialize)]
1025#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1026pub struct HistoricalTheoResponse {
1027 pub instrument_name: String,
1029 pub interval: HistoricalTheoInterval,
1031 #[serde(default)]
1033 pub points: Vec<HistoricalTheoPoint>,
1034}
1035
1036#[derive(Debug, Clone, Serialize, Deserialize)]
1042#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1043pub struct MarginSummary {
1044 pub mode: String,
1046 #[serde(deserialize_with = "string_decimal")]
1048 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1049 pub equity: Decimal,
1050 #[serde(deserialize_with = "string_decimal")]
1052 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1053 pub position_im: Decimal,
1054 #[serde(deserialize_with = "string_decimal")]
1056 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1057 pub open_orders_im: Decimal,
1058 #[serde(deserialize_with = "string_decimal")]
1060 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1061 pub initial_margin: Decimal,
1062 #[serde(deserialize_with = "string_decimal")]
1064 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1065 pub maintenance_margin: Decimal,
1066 #[serde(
1068 default,
1069 skip_serializing_if = "Option::is_none",
1070 deserialize_with = "option_string_decimal"
1071 )]
1072 #[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
1073 pub open_orders_premium_reserved: Option<Decimal>,
1074}
1075
1076impl MarginSummary {
1077 pub fn maintenance_margin_required(&self) -> Decimal {
1080 (self.equity - self.maintenance_margin).max(Decimal::ZERO)
1083 }
1084
1085 pub fn margin_utilization(&self) -> Decimal {
1088 if self.equity <= Decimal::ZERO {
1089 return Decimal::ONE;
1090 }
1091 (self.maintenance_margin_required() / self.equity).min(Decimal::ONE)
1092 }
1093}
1094
1095#[derive(Debug, Clone, Serialize, Deserialize)]
1097#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1098pub struct PortfolioPosition {
1099 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1101 pub wallet_address: WalletAddress,
1102 pub symbol: String,
1104 #[serde(deserialize_with = "string_decimal")]
1106 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1107 pub amount: Decimal,
1108 #[serde(deserialize_with = "string_decimal")]
1110 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1111 pub entry_price: Decimal,
1112 #[serde(deserialize_with = "string_decimal")]
1114 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1115 pub margin_posted: Decimal,
1116 #[serde(deserialize_with = "string_decimal")]
1118 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1119 pub realized_pnl: Decimal,
1120 #[serde(deserialize_with = "string_decimal")]
1122 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1123 pub unrealized_pnl: Decimal,
1124 #[serde(default, deserialize_with = "option_string_decimal_default")]
1126 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1127 pub notional_value: Decimal,
1128 #[serde(default, deserialize_with = "option_string_decimal_default")]
1130 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1131 pub maintenance_margin: Decimal,
1132 #[serde(default, deserialize_with = "option_string_decimal_default")]
1134 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1135 pub liquidation_price: Decimal,
1136 #[serde(default, deserialize_with = "option_string_decimal_default")]
1138 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1139 pub margin_ratio: Decimal,
1140}
1141
1142#[derive(Debug, Clone, Serialize, Deserialize)]
1144#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1145pub struct PortfolioResponse {
1146 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1148 pub wallet_address: WalletAddress,
1149 #[serde(default)]
1151 pub positions: Vec<PortfolioPosition>,
1152 #[serde(deserialize_with = "string_decimal")]
1154 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1155 pub total_margin_used: Decimal,
1156 #[serde(deserialize_with = "string_decimal")]
1158 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1159 pub available_balance: Decimal,
1160 #[serde(default = "default_margin_mode")]
1162 pub margin_mode: String,
1163 pub margin_summary: Option<MarginSummary>,
1165}
1166
1167fn default_margin_mode() -> String {
1168 "standard".to_string()
1169}
1170
1171impl PortfolioResponse {
1172 pub fn equity(&self) -> Option<Decimal> {
1174 self.margin_summary.as_ref().map(|m| m.equity)
1175 }
1176
1177 pub fn maintenance_margin_required(&self) -> Option<Decimal> {
1179 self.margin_summary
1180 .as_ref()
1181 .map(|m| m.maintenance_margin_required())
1182 }
1183
1184 pub fn margin_utilization(&self) -> Option<Decimal> {
1186 self.margin_summary.as_ref().map(|m| m.margin_utilization())
1187 }
1188
1189 pub fn position_im(&self) -> Option<Decimal> {
1191 self.margin_summary.as_ref().map(|m| m.position_im)
1192 }
1193
1194 pub fn open_orders_im(&self) -> Option<Decimal> {
1196 self.margin_summary.as_ref().map(|m| m.open_orders_im)
1197 }
1198}
1199
1200fn option_string_decimal_default<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
1202where
1203 D: Deserializer<'de>,
1204{
1205 use serde::de;
1206
1207 struct OptStringDecimalVisitor;
1208
1209 impl<'de> de::Visitor<'de> for OptStringDecimalVisitor {
1210 type Value = Decimal;
1211
1212 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1213 formatter.write_str("a string representing Decimal or null")
1214 }
1215
1216 fn visit_none<E>(self) -> Result<Self::Value, E>
1217 where
1218 E: de::Error,
1219 {
1220 Ok(Decimal::ZERO)
1221 }
1222
1223 fn visit_unit<E>(self) -> Result<Self::Value, E>
1224 where
1225 E: de::Error,
1226 {
1227 Ok(Decimal::ZERO)
1228 }
1229
1230 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
1231 where
1232 E: de::Error,
1233 {
1234 v.parse::<Decimal>().map_err(de::Error::custom)
1235 }
1236
1237 fn visit_some<D2>(self, deserializer: D2) -> Result<Self::Value, D2::Error>
1238 where
1239 D2: Deserializer<'de>,
1240 {
1241 deserializer.deserialize_str(OptStringDecimalVisitor)
1242 }
1243 }
1244
1245 deserializer.deserialize_any(OptStringDecimalVisitor)
1246}
1247
1248#[derive(Debug, Clone, Serialize, Deserialize)]
1252#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1253pub struct RfqQuoteLegResponse {
1254 pub instrument: String,
1255 pub side: Side,
1256 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1257 pub price: Decimal,
1258 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1259 pub size: Decimal,
1260}
1261
1262#[derive(Debug, Clone, Serialize, Deserialize)]
1264#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1265pub struct RfqQuoteResponse {
1266 pub quote_id: String,
1267 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1268 pub net_premium: Decimal,
1269 pub legs: Vec<RfqQuoteLegResponse>,
1270 pub expires_at: u64,
1271}
1272
1273#[derive(Debug, Clone, Serialize, Deserialize)]
1275#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1276pub struct RfqLegResponse {
1277 pub instrument: String,
1278 pub side: Side,
1279 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1280 pub size: Decimal,
1281}
1282
1283#[derive(Debug, Clone, Serialize, Deserialize)]
1285#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1286pub struct RfqStatusResponse {
1287 pub rfq_id: String,
1288 pub status: RfqStatus,
1289 pub underlying: String,
1290 pub legs: Vec<RfqLegResponse>,
1291 pub quotes: Vec<RfqQuoteResponse>,
1292 pub created_at: u64,
1293 pub expires_at: u64,
1294}
1295
1296#[derive(Debug, Clone, Serialize, Deserialize)]
1298#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1299pub struct RfqAcceptResponse {
1300 pub rfq_id: String,
1301 pub quote_id: String,
1302 pub status: RfqStatus,
1303 pub fill_id: String,
1304}
1305
1306#[derive(Debug, Clone, Serialize, Deserialize)]
1308#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1309pub struct RfqHistoryResponse {
1310 pub rfqs: Vec<RfqStatusResponse>,
1311}
1312
1313#[derive(Debug, Clone, Serialize, Deserialize)]
1315#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1316pub struct QuoteProviderResponse {
1317 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1318 pub wallet_address: WalletAddress,
1319 pub tier: String,
1320 pub status: String,
1321 pub allowed_underlyings: Option<Vec<String>>,
1322 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1323 pub max_notional_per_quote: Decimal,
1324 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
1325 pub max_open_notional: Decimal,
1326}
1327
1328pub use crate::api_models::Instrument;
1332pub use crate::api_models::MarketInfo;
1333pub use crate::api_models::MarketsResponse;