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