deribit_fix/message/orders/
cancel_request.rs1use crate::error::{DeribitFixError, Result as DeribitFixResult};
10use crate::{message::builder::MessageBuilder, model::types::MsgType};
11use chrono::Utc;
12use serde::{Deserialize, Serialize};
13
14#[derive(Clone, PartialEq, Serialize, Deserialize)]
16pub struct OrderCancelRequest {
17 pub cl_ord_id: Option<String>,
19 pub orig_cl_ord_id: Option<String>,
21 pub deribit_label: Option<String>,
23 pub symbol: Option<String>,
25 pub currency: Option<String>,
27}
28
29impl OrderCancelRequest {
30 pub fn by_orig_cl_ord_id(orig_cl_ord_id: String) -> Self {
32 Self {
33 cl_ord_id: None,
34 orig_cl_ord_id: Some(orig_cl_ord_id),
35 deribit_label: None,
36 symbol: None,
37 currency: None,
38 }
39 }
40
41 pub fn by_cl_ord_id(cl_ord_id: String, symbol: String) -> Self {
43 Self {
44 cl_ord_id: Some(cl_ord_id),
45 orig_cl_ord_id: None,
46 deribit_label: None,
47 symbol: Some(symbol),
48 currency: None,
49 }
50 }
51
52 pub fn by_deribit_label(deribit_label: String, symbol: String) -> Self {
54 Self {
55 cl_ord_id: None,
56 orig_cl_ord_id: None,
57 deribit_label: Some(deribit_label),
58 symbol: Some(symbol),
59 currency: None,
60 }
61 }
62
63 pub fn with_currency(mut self, currency: String) -> Self {
65 self.currency = Some(currency);
66 self
67 }
68
69 pub fn to_fix_message(
71 &self,
72 sender_comp_id: &str,
73 target_comp_id: &str,
74 msg_seq_num: u32,
75 ) -> DeribitFixResult<String> {
76 let mut builder = MessageBuilder::new()
77 .msg_type(MsgType::OrderCancelRequest)
78 .sender_comp_id(sender_comp_id.to_string())
79 .target_comp_id(target_comp_id.to_string())
80 .msg_seq_num(msg_seq_num)
81 .sending_time(Utc::now());
82
83 if self.cl_ord_id.is_none() && self.orig_cl_ord_id.is_none() && self.deribit_label.is_none()
85 {
86 return Err(DeribitFixError::Generic(
87 "Either OrigClOrdId or ClOrdId must be specified".to_string(),
88 ));
89 }
90
91 if let Some(cl_ord_id) = &self.cl_ord_id {
92 builder = builder.field(11, cl_ord_id.clone());
93 }
94
95 if let Some(orig_cl_ord_id) = &self.orig_cl_ord_id {
96 builder = builder.field(41, orig_cl_ord_id.clone());
97 }
98
99 if let Some(deribit_label) = &self.deribit_label {
100 builder = builder.field(100010, deribit_label.clone());
101 }
102
103 if let Some(symbol) = &self.symbol {
104 builder = builder.field(55, symbol.clone());
105 }
106
107 if let Some(currency) = &self.currency {
108 builder = builder.field(15, currency.clone());
109 }
110
111 Ok(builder.build()?.to_string())
112 }
113}
114
115impl_json_display!(OrderCancelRequest);
116impl_json_debug_pretty!(OrderCancelRequest);
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_order_cancel_request_by_orig_cl_ord_id() {
124 let cancel_request = OrderCancelRequest::by_orig_cl_ord_id("ORIG123".to_string());
125
126 assert_eq!(cancel_request.orig_cl_ord_id, Some("ORIG123".to_string()));
127 assert_eq!(cancel_request.cl_ord_id, None);
128 assert_eq!(cancel_request.deribit_label, None);
129 assert_eq!(cancel_request.symbol, None);
130 }
131
132 #[test]
133 fn test_order_cancel_request_by_cl_ord_id() {
134 let cancel_request =
135 OrderCancelRequest::by_cl_ord_id("ORDER123".to_string(), "BTC-PERPETUAL".to_string());
136
137 assert_eq!(cancel_request.cl_ord_id, Some("ORDER123".to_string()));
138 assert_eq!(cancel_request.symbol, Some("BTC-PERPETUAL".to_string()));
139 assert_eq!(cancel_request.orig_cl_ord_id, None);
140 assert_eq!(cancel_request.deribit_label, None);
141 }
142
143 #[test]
144 fn test_order_cancel_request_by_deribit_label() {
145 let cancel_request = OrderCancelRequest::by_deribit_label(
146 "my-order".to_string(),
147 "BTC-PERPETUAL".to_string(),
148 );
149
150 assert_eq!(cancel_request.deribit_label, Some("my-order".to_string()));
151 assert_eq!(cancel_request.symbol, Some("BTC-PERPETUAL".to_string()));
152 assert_eq!(cancel_request.cl_ord_id, None);
153 assert_eq!(cancel_request.orig_cl_ord_id, None);
154 }
155
156 #[test]
157 fn test_order_cancel_request_with_currency() {
158 let cancel_request =
159 OrderCancelRequest::by_cl_ord_id("ORDER123".to_string(), "BTC-PERPETUAL".to_string())
160 .with_currency("BTC".to_string());
161
162 assert_eq!(cancel_request.currency, Some("BTC".to_string()));
163 }
164
165 #[test]
166 fn test_order_cancel_request_to_fix_message() {
167 let cancel_request = OrderCancelRequest::by_orig_cl_ord_id("ORIG123".to_string());
168
169 let fix_message = cancel_request.to_fix_message("CLIENT", "DERIBITSERVER", 1);
170 assert!(fix_message.is_ok());
171
172 let message = fix_message.unwrap();
173 assert!(message.contains("35=F")); assert!(message.contains("41=ORIG123")); }
176
177 #[test]
178 fn test_order_cancel_request_validation_error() {
179 let cancel_request = OrderCancelRequest {
180 cl_ord_id: None,
181 orig_cl_ord_id: None,
182 deribit_label: None,
183 symbol: None,
184 currency: None,
185 };
186
187 let fix_message = cancel_request.to_fix_message("CLIENT", "DERIBITSERVER", 1);
188 assert!(fix_message.is_err());
189 }
190}