bpx_api_types/
rfq.rs

1use rust_decimal::Decimal;
2use serde::{Deserialize, Serialize};
3use strum::{Display, EnumString};
4
5use crate::order::{OrderStatus, Side};
6
7#[derive(
8    Debug, Display, Clone, Copy, Serialize, Deserialize, Default, EnumString, PartialEq, Eq, Hash,
9)]
10#[strum(serialize_all = "PascalCase")]
11#[serde(rename_all = "PascalCase")]
12pub enum RfqExecutionMode {
13    #[default]
14    AwaitAccept,
15    Immediate,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct RequestForQuotePayload {
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub client_id: Option<u32>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub quantity: Option<Decimal>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub quote_quantity: Option<Decimal>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub price: Option<Decimal>,
29    pub symbol: String,
30    pub side: Side,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub execution_mode: Option<RfqExecutionMode>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36#[serde(rename_all = "camelCase")]
37pub struct RequestForQuoteCancelPayload {
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub rfq_id: Option<String>,
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub client_id: Option<u32>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub struct RequestForQuoteRefreshPayload {
47    pub rfq_id: String,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct QuoteAcceptPayload {
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub rfq_id: Option<String>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub client_id: Option<u32>,
57    pub quote_id: String,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(rename_all = "camelCase")]
62pub struct QuotePayload {
63    pub rfq_id: String,
64    pub bid_price: Decimal,
65    pub ask_price: Decimal,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub client_id: Option<u32>,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub auto_lend: Option<bool>,
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub auto_lend_redeem: Option<bool>,
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub auto_borrow: Option<bool>,
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub auto_borrow_repay: Option<bool>,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct RequestForQuoteStream {
80    pub stream: String,
81    pub data: RequestForQuoteUpdate,
82}
83
84/// RequestForQuote updates received from the websocket.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86#[serde(tag = "e", rename_all = "camelCase")] // Discriminates based on "e" field
87pub enum RequestForQuoteUpdate {
88    RfqActive {
89        #[serde(rename = "E")]
90        event_time: i64,
91        #[serde(rename = "R")]
92        rfq_id: u64,
93        #[serde(rename = "C", skip_serializing_if = "Option::is_none")]
94        client_id: Option<u32>,
95        #[serde(rename = "s")]
96        symbol: String,
97        #[serde(rename = "q", skip_serializing_if = "Option::is_none")]
98        quantity: Option<Decimal>,
99        #[serde(rename = "Q", skip_serializing_if = "Option::is_none")]
100        quote_quantity: Option<Decimal>,
101        #[serde(rename = "w")]
102        submission_time: i64,
103        #[serde(rename = "W")]
104        expiry_time: i64,
105        #[serde(rename = "X")]
106        order_status: OrderStatus,
107        #[serde(rename = "T")]
108        timestamp: i64,
109    },
110    RfqRefreshed {
111        #[serde(rename = "E")]
112        event_time: i64,
113        #[serde(rename = "R")]
114        rfq_id: u64,
115        #[serde(rename = "C", skip_serializing_if = "Option::is_none")]
116        client_id: Option<u32>,
117        #[serde(rename = "s")]
118        symbol: String,
119        #[serde(rename = "S")]
120        side: Side,
121        #[serde(rename = "q", skip_serializing_if = "Option::is_none")]
122        quantity: Option<Decimal>,
123        #[serde(rename = "Q", skip_serializing_if = "Option::is_none")]
124        quote_quantity: Option<Decimal>,
125        #[serde(rename = "w")]
126        submission_time: i64,
127        #[serde(rename = "W")]
128        expiry_time: i64,
129        #[serde(rename = "X")]
130        order_status: OrderStatus,
131        #[serde(rename = "T")]
132        timestamp: i64,
133    },
134    RfqAccepted {
135        #[serde(rename = "E")]
136        event_time: i64,
137        #[serde(rename = "R")]
138        rfq_id: u64,
139        #[serde(rename = "C", skip_serializing_if = "Option::is_none")]
140        client_id: Option<u32>,
141        #[serde(rename = "s")]
142        symbol: String,
143        #[serde(rename = "S")]
144        side: Side,
145        #[serde(rename = "q", skip_serializing_if = "Option::is_none")]
146        quantity: Option<Decimal>,
147        #[serde(rename = "Q", skip_serializing_if = "Option::is_none")]
148        quote_quantity: Option<Decimal>,
149        #[serde(rename = "w")]
150        submission_time: i64,
151        #[serde(rename = "W")]
152        expiry_time: i64,
153        #[serde(rename = "X")]
154        order_status: OrderStatus,
155        #[serde(rename = "T")]
156        timestamp: i64,
157    },
158    RfqCancelled {
159        #[serde(rename = "E")]
160        event_time: i64,
161        #[serde(rename = "R")]
162        rfq_id: u64,
163        #[serde(rename = "C", skip_serializing_if = "Option::is_none")]
164        client_id: Option<u32>,
165        #[serde(rename = "s")]
166        symbol: String,
167        #[serde(rename = "S")]
168        side: Side,
169        #[serde(rename = "q", skip_serializing_if = "Option::is_none")]
170        quantity: Option<Decimal>,
171        #[serde(rename = "Q", skip_serializing_if = "Option::is_none")]
172        quote_quantity: Option<Decimal>,
173        #[serde(rename = "w")]
174        submission_time: i64,
175        #[serde(rename = "W")]
176        expiry_time: i64,
177        #[serde(rename = "X")]
178        order_status: OrderStatus,
179        #[serde(rename = "T")]
180        timestamp: i64,
181    },
182    QuoteAccepted {
183        #[serde(rename = "E")]
184        event_time: i64,
185        #[serde(rename = "R")]
186        rfq_id: u64,
187        #[serde(rename = "u")]
188        quote_id: u64,
189        #[serde(rename = "C", skip_serializing_if = "Option::is_none")]
190        client_id: Option<u32>,
191        #[serde(rename = "s")]
192        symbol: String,
193        #[serde(rename = "p", skip_serializing_if = "Option::is_none")]
194        price: Option<Decimal>,
195        #[serde(rename = "X")]
196        order_status: OrderStatus,
197        #[serde(rename = "T")]
198        timestamp: i64,
199    },
200    QuoteCancelled {
201        #[serde(rename = "E")]
202        event_time: i64,
203        #[serde(rename = "R")]
204        rfq_id: u64,
205        #[serde(rename = "u")]
206        quote_id: u64,
207        #[serde(rename = "C", skip_serializing_if = "Option::is_none")]
208        client_id: Option<u32>,
209        #[serde(rename = "s")]
210        symbol: String,
211        #[serde(rename = "p", skip_serializing_if = "Option::is_none")]
212        price: Option<Decimal>,
213        #[serde(rename = "X")]
214        order_status: OrderStatus,
215        #[serde(rename = "T")]
216        timestamp: i64,
217    },
218    RfqCandidate {
219        #[serde(rename = "E")]
220        event_time: i64,
221        #[serde(rename = "R")]
222        rfq_id: u64,
223        #[serde(rename = "u")]
224        quote_id: u64,
225        #[serde(rename = "C", skip_serializing_if = "Option::is_none")]
226        client_id: Option<u32>,
227        #[serde(rename = "s")]
228        symbol: String,
229        #[serde(rename = "S", skip_serializing_if = "Option::is_none")]
230        side: Option<Side>,
231        #[serde(rename = "q", skip_serializing_if = "Option::is_none")]
232        quantity: Option<Decimal>,
233        #[serde(rename = "Q", skip_serializing_if = "Option::is_none")]
234        quote_quantity: Option<Decimal>,
235        #[serde(rename = "p")]
236        price: Decimal,
237        #[serde(rename = "X")]
238        order_status: OrderStatus,
239        #[serde(rename = "T")]
240        timestamp: i64,
241    },
242    RfqFilled {
243        #[serde(rename = "E")]
244        event_time: i64,
245        #[serde(rename = "R")]
246        rfq_id: u64,
247        #[serde(rename = "u")]
248        quote_id: u64,
249        #[serde(rename = "C", skip_serializing_if = "Option::is_none")]
250        client_id: Option<u32>,
251        #[serde(rename = "s")]
252        symbol: String,
253        #[serde(rename = "S")]
254        side: Side,
255        #[serde(rename = "q", skip_serializing_if = "Option::is_none")]
256        quantity: Option<Decimal>,
257        #[serde(rename = "Q", skip_serializing_if = "Option::is_none")]
258        quote_quantity: Option<Decimal>,
259        #[serde(rename = "p", skip_serializing_if = "Option::is_none")]
260        price: Option<Decimal>,
261        #[serde(rename = "X")]
262        order_status: OrderStatus,
263        #[serde(rename = "T")]
264        timestamp: i64,
265    },
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
269#[serde(rename_all = "camelCase")]
270pub struct Quote {
271    /// Unique RFQ order ID assigned by the matching engine.
272    pub rfq_id: String,
273
274    /// Unique RFQ quote ID, assigned by the matching engine.
275    pub quote_id: String,
276
277    /// Custom RFQ quote ID assigned by the maker (optionally)
278    pub client_id: Option<u32>,
279
280    /// Quote Bid Price.
281    pub bid_price: Decimal,
282
283    /// Quote Ask Price.
284    pub ask_price: Decimal,
285
286    /// Status.
287    pub status: OrderStatus,
288
289    /// Time the quote was created.
290    pub created_at: i64,
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
294#[serde(rename_all = "camelCase")]
295pub struct RequestForQuote {
296    pub rfq_id: String,
297    pub client_id: Option<u32>,
298    pub symbol: String,
299    pub side: Side,
300    pub price: Option<Decimal>,
301    pub quantity: Option<Decimal>,
302    pub quote_quantity: Option<Decimal>,
303    pub submission_time: i64,
304    pub expiry_time: i64,
305    pub status: OrderStatus,
306    pub execution_mode: RfqExecutionMode,
307    pub created_at: i64,
308}
309
310impl QuotePayload {
311    pub fn new(rfq_id: String, bid_price: Decimal, ask_price: Decimal) -> Self {
312        Self {
313            rfq_id,
314            bid_price,
315            ask_price,
316            client_id: None,
317            auto_lend: None,
318            auto_lend_redeem: None,
319            auto_borrow: None,
320            auto_borrow_repay: None,
321        }
322    }
323
324    pub fn with_client_id(mut self, client_id: u32) -> Self {
325        self.client_id = Some(client_id);
326        self
327    }
328
329    pub fn with_auto_lend(mut self, auto_lend: bool) -> Self {
330        self.auto_lend = Some(auto_lend);
331        self
332    }
333
334    pub fn with_auto_lend_redeem(mut self, auto_lend_redeem: bool) -> Self {
335        self.auto_lend_redeem = Some(auto_lend_redeem);
336        self
337    }
338
339    pub fn with_auto_borrow(mut self, auto_borrow: bool) -> Self {
340        self.auto_borrow = Some(auto_borrow);
341        self
342    }
343
344    pub fn with_auto_borrow_repay(mut self, auto_borrow_repay: bool) -> Self {
345        self.auto_borrow_repay = Some(auto_borrow_repay);
346        self
347    }
348}