1use crate::impl_json_display;
7use serde::{Deserialize, Deserializer, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
11#[serde(rename_all = "UPPERCASE")]
12pub enum Direction {
13 #[default]
15 Buy,
16 Sell,
18}
19
20impl_json_display!(Direction);
21
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
24#[serde(rename_all = "UPPERCASE")]
25pub enum OrderType {
26 #[default]
28 Limit,
29 Market,
31 Quote,
33 Stop,
35 StopLimit,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
44#[serde(rename_all = "UPPERCASE")]
45pub enum Status {
46 Amended,
48 Deleted,
50 #[serde(rename = "FULLY_CLOSED")]
52 FullyClosed,
53 Opened,
55 #[serde(rename = "PARTIALLY_CLOSED")]
57 PartiallyClosed,
58 Closed,
60 #[default]
62 Open,
63 Updated,
65 Accepted,
67 Rejected,
69 Working,
71 Filled,
73 Cancelled,
75 Expired,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
81pub enum TimeInForce {
82 #[serde(rename = "GOOD_TILL_CANCELLED")]
84 #[default]
85 GoodTillCancelled,
86 #[serde(rename = "GOOD_TILL_DATE")]
88 GoodTillDate,
89 #[serde(rename = "IMMEDIATE_OR_CANCEL")]
91 ImmediateOrCancel,
92 #[serde(rename = "FILL_OR_KILL")]
94 FillOrKill,
95}
96
97#[derive(Debug, Clone, Serialize)]
99pub struct CreateOrderRequest {
100 pub epic: String,
102 pub direction: Direction,
104 pub size: f64,
106 #[serde(rename = "orderType")]
108 pub order_type: OrderType,
109 #[serde(rename = "timeInForce")]
111 pub time_in_force: TimeInForce,
112 #[serde(rename = "level", skip_serializing_if = "Option::is_none")]
114 pub level: Option<f64>,
115 #[serde(rename = "guaranteedStop", skip_serializing_if = "Option::is_none")]
117 pub guaranteed_stop: Option<bool>,
118 #[serde(rename = "stopLevel", skip_serializing_if = "Option::is_none")]
120 pub stop_level: Option<f64>,
121 #[serde(rename = "stopDistance", skip_serializing_if = "Option::is_none")]
123 pub stop_distance: Option<f64>,
124 #[serde(rename = "limitLevel", skip_serializing_if = "Option::is_none")]
126 pub limit_level: Option<f64>,
127 #[serde(rename = "limitDistance", skip_serializing_if = "Option::is_none")]
129 pub limit_distance: Option<f64>,
130 #[serde(rename = "expiry", skip_serializing_if = "Option::is_none")]
132 pub expiry: Option<String>,
133 #[serde(rename = "dealReference", skip_serializing_if = "Option::is_none")]
135 pub deal_reference: Option<String>,
136 #[serde(rename = "forceOpen", skip_serializing_if = "Option::is_none")]
138 pub force_open: Option<bool>,
139 #[serde(rename = "currencyCode", skip_serializing_if = "Option::is_none")]
141 pub currency_code: Option<String>,
142}
143
144impl CreateOrderRequest {
145 pub fn market(epic: String, direction: Direction, size: f64) -> Self {
147 Self {
148 epic,
149 direction,
150 size,
151 order_type: OrderType::Market,
152 time_in_force: TimeInForce::FillOrKill,
153 level: None,
154 guaranteed_stop: None,
155 stop_level: None,
156 stop_distance: None,
157 limit_level: None,
158 limit_distance: None,
159 expiry: None,
160 deal_reference: None,
161 force_open: Some(true),
162 currency_code: None,
163 }
164 }
165
166 pub fn limit(epic: String, direction: Direction, size: f64, level: f64) -> Self {
168 Self {
169 epic,
170 direction,
171 size,
172 order_type: OrderType::Limit,
173 time_in_force: TimeInForce::GoodTillCancelled,
174 level: Some(level),
175 guaranteed_stop: None,
176 stop_level: None,
177 stop_distance: None,
178 limit_level: None,
179 limit_distance: None,
180 expiry: None,
181 deal_reference: None,
182 force_open: Some(true),
183 currency_code: None,
184 }
185 }
186
187 pub fn with_stop_loss(mut self, stop_level: f64) -> Self {
189 self.stop_level = Some(stop_level);
190 self
191 }
192
193 pub fn with_take_profit(mut self, limit_level: f64) -> Self {
195 self.limit_level = Some(limit_level);
196 self
197 }
198
199 pub fn with_reference(mut self, reference: String) -> Self {
201 self.deal_reference = Some(reference);
202 self
203 }
204}
205
206#[derive(Debug, Clone, Deserialize)]
208pub struct CreateOrderResponse {
209 #[serde(rename = "dealReference")]
211 pub deal_reference: String,
212}
213
214fn deserialize_nullable_status<'de, D>(deserializer: D) -> Result<Status, D::Error>
217where
218 D: Deserializer<'de>,
219{
220 let opt = Option::deserialize(deserializer)?;
221 Ok(opt.unwrap_or(Status::Rejected))
222}
223
224#[derive(Debug, Clone, Deserialize)]
226pub struct OrderConfirmation {
227 pub date: String,
229 #[serde(deserialize_with = "deserialize_nullable_status")]
232 pub status: Status,
233 pub reason: Option<String>,
235 #[serde(rename = "dealId")]
237 pub deal_id: Option<String>,
238 #[serde(rename = "dealReference")]
240 pub deal_reference: String,
241 #[serde(rename = "dealStatus")]
243 pub deal_status: Option<String>,
244 pub epic: Option<String>,
246 #[serde(rename = "expiry")]
248 pub expiry: Option<String>,
249 #[serde(rename = "guaranteedStop")]
251 pub guaranteed_stop: Option<bool>,
252 #[serde(rename = "level")]
254 pub level: Option<f64>,
255 #[serde(rename = "limitDistance")]
257 pub limit_distance: Option<f64>,
258 #[serde(rename = "limitLevel")]
260 pub limit_level: Option<f64>,
261 pub size: Option<f64>,
263 #[serde(rename = "stopDistance")]
265 pub stop_distance: Option<f64>,
266 #[serde(rename = "stopLevel")]
268 pub stop_level: Option<f64>,
269 #[serde(rename = "trailingStop")]
271 pub trailing_stop: Option<bool>,
272 pub direction: Option<Direction>,
274}
275
276#[derive(Debug, Clone, Serialize)]
278pub struct UpdatePositionRequest {
279 #[serde(rename = "stopLevel", skip_serializing_if = "Option::is_none")]
281 pub stop_level: Option<f64>,
282 #[serde(rename = "limitLevel", skip_serializing_if = "Option::is_none")]
284 pub limit_level: Option<f64>,
285 #[serde(rename = "trailingStop", skip_serializing_if = "Option::is_none")]
287 pub trailing_stop: Option<bool>,
288 #[serde(
290 rename = "trailingStopDistance",
291 skip_serializing_if = "Option::is_none"
292 )]
293 pub trailing_stop_distance: Option<f64>,
294}
295
296#[derive(Debug, Clone, Serialize)]
298pub struct ClosePositionRequest {
299 #[serde(rename = "dealId")]
301 pub deal_id: String,
302 pub direction: Direction,
304 pub size: f64,
306 #[serde(rename = "orderType")]
308 pub order_type: OrderType,
309 #[serde(rename = "timeInForce")]
311 pub time_in_force: TimeInForce,
312 #[serde(rename = "level", skip_serializing_if = "Option::is_none")]
314 pub level: Option<f64>,
315 #[serde(rename = "forceOpen")]
317 pub force_open: bool,
318 #[serde(rename = "expiry")]
320 pub expiry: String,
321 pub epic: String,
323 #[serde(rename = "currencyCode")]
325 pub currency_code: String,
326 #[serde(rename = "guaranteedStop")]
328 pub guaranteed_stop: bool,
329}
330
331impl ClosePositionRequest {
332 pub fn market(deal_id: String, direction: Direction, size: f64, epic: String) -> Self {
334 Self {
335 deal_id,
336 direction,
337 size,
338 order_type: OrderType::Market,
339 time_in_force: TimeInForce::FillOrKill,
340 level: None,
341 force_open: false,
342 expiry: "JUL-25".to_string(), epic,
344 currency_code: "EUR".to_string(), guaranteed_stop: false, }
347 }
348
349 pub fn limit(
353 deal_id: String,
354 direction: Direction,
355 size: f64,
356 epic: String,
357 level: f64,
358 ) -> Self {
359 Self {
360 deal_id,
361 direction,
362 size,
363 order_type: OrderType::Limit,
364 time_in_force: TimeInForce::FillOrKill,
365 level: Some(level),
366 force_open: false,
367 expiry: "JUL-25".to_string(), epic,
369 currency_code: "EUR".to_string(), guaranteed_stop: false, }
372 }
373}
374
375#[derive(Debug, Clone, Deserialize)]
377pub struct ClosePositionResponse {
378 #[serde(rename = "dealReference")]
380 pub deal_reference: String,
381}
382
383#[derive(Debug, Clone, Deserialize)]
385pub struct UpdatePositionResponse {
386 #[serde(rename = "dealReference")]
388 pub deal_reference: String,
389}
390
391#[derive(Debug, Clone, Serialize)]
393pub struct CreateWorkingOrderRequest {
394 pub epic: String,
396 pub direction: Direction,
398 pub size: f64,
400 pub level: f64,
402 #[serde(rename = "type")]
404 pub order_type: OrderType,
405 #[serde(rename = "timeInForce")]
407 pub time_in_force: TimeInForce,
408 #[serde(rename = "guaranteedStop", skip_serializing_if = "Option::is_none")]
410 pub guaranteed_stop: Option<bool>,
411 #[serde(rename = "stopLevel", skip_serializing_if = "Option::is_none")]
413 pub stop_level: Option<f64>,
414 #[serde(rename = "stopDistance", skip_serializing_if = "Option::is_none")]
416 pub stop_distance: Option<f64>,
417 #[serde(rename = "limitLevel", skip_serializing_if = "Option::is_none")]
419 pub limit_level: Option<f64>,
420 #[serde(rename = "limitDistance", skip_serializing_if = "Option::is_none")]
422 pub limit_distance: Option<f64>,
423 #[serde(rename = "goodTillDate", skip_serializing_if = "Option::is_none")]
425 pub good_till_date: Option<String>,
426 #[serde(rename = "dealReference", skip_serializing_if = "Option::is_none")]
428 pub deal_reference: Option<String>,
429 #[serde(rename = "currencyCode", skip_serializing_if = "Option::is_none")]
431 pub currency_code: Option<String>,
432}
433
434impl CreateWorkingOrderRequest {
435 pub fn limit(epic: String, direction: Direction, size: f64, level: f64) -> Self {
437 Self {
438 epic,
439 direction,
440 size,
441 level,
442 order_type: OrderType::Limit,
443 time_in_force: TimeInForce::GoodTillCancelled,
444 guaranteed_stop: None,
445 stop_level: None,
446 stop_distance: None,
447 limit_level: None,
448 limit_distance: None,
449 good_till_date: None,
450 deal_reference: None,
451 currency_code: None,
452 }
453 }
454
455 pub fn stop(epic: String, direction: Direction, size: f64, level: f64) -> Self {
457 Self {
458 epic,
459 direction,
460 size,
461 level,
462 order_type: OrderType::Stop,
463 time_in_force: TimeInForce::GoodTillCancelled,
464 guaranteed_stop: None,
465 stop_level: None,
466 stop_distance: None,
467 limit_level: None,
468 limit_distance: None,
469 good_till_date: None,
470 deal_reference: None,
471 currency_code: None,
472 }
473 }
474
475 pub fn with_stop_loss(mut self, stop_level: f64) -> Self {
477 self.stop_level = Some(stop_level);
478 self
479 }
480
481 pub fn with_take_profit(mut self, limit_level: f64) -> Self {
483 self.limit_level = Some(limit_level);
484 self
485 }
486
487 pub fn with_reference(mut self, reference: String) -> Self {
489 self.deal_reference = Some(reference);
490 self
491 }
492
493 pub fn expires_at(mut self, date: String) -> Self {
495 self.time_in_force = TimeInForce::GoodTillDate;
496 self.good_till_date = Some(date);
497 self
498 }
499}
500
501#[derive(Debug, Clone, Deserialize)]
503pub struct CreateWorkingOrderResponse {
504 #[serde(rename = "dealReference")]
506 pub deal_reference: String,
507}