1use crate::impl_json_display;
7use serde::{Deserialize, Deserializer, Serialize};
8
9const DEFAULT_ORDER_SELL_SIZE: f64 = 0.0;
10const DEFAULT_ORDER_BUY_SIZE: f64 = 10000.0;
11
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
14#[serde(rename_all = "UPPERCASE")]
15pub enum Direction {
16 #[default]
18 Buy,
19 Sell,
21}
22
23impl_json_display!(Direction);
24
25#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
27#[serde(rename_all = "UPPERCASE")]
28pub enum OrderType {
29 #[default]
31 Limit,
32 Market,
34 Quote,
36 Stop,
38 StopLimit,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
47#[serde(rename_all = "UPPERCASE")]
48pub enum Status {
49 Amended,
51 Deleted,
53 #[serde(rename = "FULLY_CLOSED")]
55 FullyClosed,
56 Opened,
58 #[serde(rename = "PARTIALLY_CLOSED")]
60 PartiallyClosed,
61 Closed,
63 #[default]
65 Open,
66 Updated,
68 Accepted,
70 Rejected,
72 Working,
74 Filled,
76 Cancelled,
78 Expired,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
84pub enum TimeInForce {
85 #[serde(rename = "GOOD_TILL_CANCELLED")]
87 #[default]
88 GoodTillCancelled,
89 #[serde(rename = "GOOD_TILL_DATE")]
91 GoodTillDate,
92 #[serde(rename = "IMMEDIATE_OR_CANCEL")]
94 ImmediateOrCancel,
95 #[serde(rename = "FILL_OR_KILL")]
97 FillOrKill,
98}
99
100#[derive(Debug, Clone, Serialize)]
102pub struct CreateOrderRequest {
103 pub epic: String,
105 pub direction: Direction,
107 pub size: f64,
109 #[serde(rename = "orderType")]
111 pub order_type: OrderType,
112 #[serde(rename = "timeInForce")]
114 pub time_in_force: TimeInForce,
115 #[serde(rename = "level", skip_serializing_if = "Option::is_none")]
117 pub level: Option<f64>,
118 #[serde(rename = "guaranteedStop", skip_serializing_if = "Option::is_none")]
120 pub guaranteed_stop: Option<bool>,
121 #[serde(rename = "stopLevel", skip_serializing_if = "Option::is_none")]
123 pub stop_level: Option<f64>,
124 #[serde(rename = "stopDistance", skip_serializing_if = "Option::is_none")]
126 pub stop_distance: Option<f64>,
127 #[serde(rename = "limitLevel", skip_serializing_if = "Option::is_none")]
129 pub limit_level: Option<f64>,
130 #[serde(rename = "limitDistance", skip_serializing_if = "Option::is_none")]
132 pub limit_distance: Option<f64>,
133 #[serde(rename = "expiry", skip_serializing_if = "Option::is_none")]
135 pub expiry: Option<String>,
136 #[serde(rename = "dealReference", skip_serializing_if = "Option::is_none")]
138 pub deal_reference: Option<String>,
139 #[serde(rename = "forceOpen", skip_serializing_if = "Option::is_none")]
141 pub force_open: Option<bool>,
142 #[serde(rename = "currencyCode", skip_serializing_if = "Option::is_none")]
144 pub currency_code: Option<String>,
145}
146
147impl CreateOrderRequest {
148 pub fn market(epic: String, direction: Direction, size: f64) -> Self {
150 Self {
151 epic,
152 direction,
153 size,
154 order_type: OrderType::Market,
155 time_in_force: TimeInForce::FillOrKill,
156 level: None,
157 guaranteed_stop: None,
158 stop_level: None,
159 stop_distance: None,
160 limit_level: None,
161 limit_distance: None,
162 expiry: None,
163 deal_reference: None,
164 force_open: Some(true),
165 currency_code: None,
166 }
167 }
168
169 pub fn limit(epic: String, direction: Direction, size: f64, level: f64) -> Self {
171 Self {
172 epic,
173 direction,
174 size,
175 order_type: OrderType::Limit,
176 time_in_force: TimeInForce::GoodTillCancelled,
177 level: Some(level),
178 guaranteed_stop: None,
179 stop_level: None,
180 stop_distance: None,
181 limit_level: None,
182 limit_distance: None,
183 expiry: None,
184 deal_reference: None,
185 force_open: Some(true),
186 currency_code: None,
187 }
188 }
189
190 pub fn sell_option_to_market(
223 epic: String,
224 size: f64,
225 expiry: Option<String>,
226 deal_reference: Option<String>,
227 currency_code: Option<String>,
228 ) -> Self {
229 Self {
230 epic,
231 direction: Direction::Sell,
232 size,
233 order_type: OrderType::Limit,
234 time_in_force: TimeInForce::FillOrKill,
235 level: Some(DEFAULT_ORDER_SELL_SIZE),
236 guaranteed_stop: Some(false),
237 stop_level: None,
238 stop_distance: None,
239 limit_level: None,
240 limit_distance: None,
241 expiry,
242 deal_reference,
243 force_open: Some(true),
244 currency_code,
245 }
246 }
247
248 pub fn buy_option_to_market(
268 epic: String,
269 size: f64,
270 expiry: Option<String>,
271 deal_reference: Option<String>,
272 currency_code: Option<String>,
273 ) -> Self {
274 Self {
275 epic,
276 direction: Direction::Buy,
277 size,
278 order_type: OrderType::Limit,
279 time_in_force: TimeInForce::FillOrKill,
280 level: Some(DEFAULT_ORDER_BUY_SIZE),
281 guaranteed_stop: Some(false),
282 stop_level: None,
283 stop_distance: None,
284 limit_level: None,
285 limit_distance: None,
286 expiry,
287 deal_reference,
288 force_open: Some(true),
289 currency_code,
290 }
291 }
292
293 pub fn with_stop_loss(mut self, stop_level: f64) -> Self {
295 self.stop_level = Some(stop_level);
296 self
297 }
298
299 pub fn with_take_profit(mut self, limit_level: f64) -> Self {
301 self.limit_level = Some(limit_level);
302 self
303 }
304
305 pub fn with_reference(mut self, reference: String) -> Self {
307 self.deal_reference = Some(reference);
308 self
309 }
310}
311
312#[derive(Debug, Clone, Deserialize)]
314pub struct CreateOrderResponse {
315 #[serde(rename = "dealReference")]
317 pub deal_reference: String,
318}
319
320fn deserialize_nullable_status<'de, D>(deserializer: D) -> Result<Status, D::Error>
323where
324 D: Deserializer<'de>,
325{
326 let opt = Option::deserialize(deserializer)?;
327 Ok(opt.unwrap_or(Status::Rejected))
328}
329
330#[derive(Debug, Clone, Deserialize)]
332pub struct OrderConfirmation {
333 pub date: String,
335 #[serde(deserialize_with = "deserialize_nullable_status")]
338 pub status: Status,
339 pub reason: Option<String>,
341 #[serde(rename = "dealId")]
343 pub deal_id: Option<String>,
344 #[serde(rename = "dealReference")]
346 pub deal_reference: String,
347 #[serde(rename = "dealStatus")]
349 pub deal_status: Option<String>,
350 pub epic: Option<String>,
352 #[serde(rename = "expiry")]
354 pub expiry: Option<String>,
355 #[serde(rename = "guaranteedStop")]
357 pub guaranteed_stop: Option<bool>,
358 #[serde(rename = "level")]
360 pub level: Option<f64>,
361 #[serde(rename = "limitDistance")]
363 pub limit_distance: Option<f64>,
364 #[serde(rename = "limitLevel")]
366 pub limit_level: Option<f64>,
367 pub size: Option<f64>,
369 #[serde(rename = "stopDistance")]
371 pub stop_distance: Option<f64>,
372 #[serde(rename = "stopLevel")]
374 pub stop_level: Option<f64>,
375 #[serde(rename = "trailingStop")]
377 pub trailing_stop: Option<bool>,
378 pub direction: Option<Direction>,
380}
381
382#[derive(Debug, Clone, Serialize)]
384pub struct UpdatePositionRequest {
385 #[serde(rename = "stopLevel", skip_serializing_if = "Option::is_none")]
387 pub stop_level: Option<f64>,
388 #[serde(rename = "limitLevel", skip_serializing_if = "Option::is_none")]
390 pub limit_level: Option<f64>,
391 #[serde(rename = "trailingStop", skip_serializing_if = "Option::is_none")]
393 pub trailing_stop: Option<bool>,
394 #[serde(
396 rename = "trailingStopDistance",
397 skip_serializing_if = "Option::is_none"
398 )]
399 pub trailing_stop_distance: Option<f64>,
400}
401
402#[derive(Debug, Clone, Serialize)]
404pub struct ClosePositionRequest {
405 #[serde(rename = "dealId")]
407 pub deal_id: Option<String>,
408 pub direction: Direction,
410 pub size: f64,
412 #[serde(rename = "orderType")]
414 pub order_type: OrderType,
415 #[serde(rename = "timeInForce")]
417 pub time_in_force: TimeInForce,
418 #[serde(rename = "level", skip_serializing_if = "Option::is_none")]
420 pub level: Option<f64>,
421 #[serde(rename = "expiry")]
423 pub expiry: Option<String>,
424 pub epic: Option<String>,
426
427 #[serde(rename = "quoteId")]
429 pub quote_id: Option<String>,
430}
431
432impl ClosePositionRequest {
433 pub fn market(deal_id: String, direction: Direction, size: f64) -> Self {
435 Self {
436 deal_id: Some(deal_id),
437 direction,
438 size,
439 order_type: OrderType::Market,
440 time_in_force: TimeInForce::FillOrKill,
441 level: None,
442 expiry: None,
443 epic: None,
444 quote_id: None,
445 }
446 }
447
448 pub fn limit(deal_id: String, direction: Direction, size: f64, level: f64) -> Self {
452 Self {
453 deal_id: Some(deal_id),
454 direction,
455 size,
456 order_type: OrderType::Limit,
457 time_in_force: TimeInForce::FillOrKill,
458 level: Some(level),
459 expiry: None,
460 epic: None,
461 quote_id: None,
462 }
463 }
464
465 pub fn close_option_to_market_by_id(deal_id: String, direction: Direction, size: f64) -> Self {
475 let level = match direction {
476 Direction::Buy => Some(DEFAULT_ORDER_BUY_SIZE),
477 Direction::Sell => Some(DEFAULT_ORDER_SELL_SIZE),
478 };
479 Self {
480 deal_id: Some(deal_id),
481 direction,
482 size,
483 order_type: OrderType::Limit,
484 time_in_force: TimeInForce::FillOrKill,
485 level,
486 expiry: None,
487 epic: None,
488 quote_id: None,
489 }
490 }
491
492 pub fn close_option_to_market_by_epic(
504 epic: String,
505 expiry: String,
506 direction: Direction,
507 size: f64,
508 ) -> Self {
509 let level = match direction {
510 Direction::Buy => Some(DEFAULT_ORDER_BUY_SIZE),
511 Direction::Sell => Some(DEFAULT_ORDER_SELL_SIZE),
512 };
513 Self {
514 deal_id: None,
515 direction,
516 size,
517 order_type: OrderType::Limit,
518 time_in_force: TimeInForce::FillOrKill,
519 level,
520 expiry: Some(expiry),
521 epic: Some(epic),
522 quote_id: None,
523 }
524 }
525}
526
527#[derive(Debug, Clone, Deserialize)]
529pub struct ClosePositionResponse {
530 #[serde(rename = "dealReference")]
532 pub deal_reference: String,
533}
534
535#[derive(Debug, Clone, Deserialize)]
537pub struct UpdatePositionResponse {
538 #[serde(rename = "dealReference")]
540 pub deal_reference: String,
541}
542
543#[derive(Debug, Clone, Serialize)]
545pub struct CreateWorkingOrderRequest {
546 pub epic: String,
548 pub direction: Direction,
550 pub size: f64,
552 pub level: f64,
554 #[serde(rename = "type")]
556 pub order_type: OrderType,
557 #[serde(rename = "timeInForce")]
559 pub time_in_force: TimeInForce,
560 #[serde(rename = "guaranteedStop", skip_serializing_if = "Option::is_none")]
562 pub guaranteed_stop: Option<bool>,
563 #[serde(rename = "stopLevel", skip_serializing_if = "Option::is_none")]
565 pub stop_level: Option<f64>,
566 #[serde(rename = "stopDistance", skip_serializing_if = "Option::is_none")]
568 pub stop_distance: Option<f64>,
569 #[serde(rename = "limitLevel", skip_serializing_if = "Option::is_none")]
571 pub limit_level: Option<f64>,
572 #[serde(rename = "limitDistance", skip_serializing_if = "Option::is_none")]
574 pub limit_distance: Option<f64>,
575 #[serde(rename = "goodTillDate", skip_serializing_if = "Option::is_none")]
577 pub good_till_date: Option<String>,
578 #[serde(rename = "dealReference", skip_serializing_if = "Option::is_none")]
580 pub deal_reference: Option<String>,
581 #[serde(rename = "currencyCode", skip_serializing_if = "Option::is_none")]
583 pub currency_code: Option<String>,
584}
585
586impl CreateWorkingOrderRequest {
587 pub fn limit(epic: String, direction: Direction, size: f64, level: f64) -> Self {
589 Self {
590 epic,
591 direction,
592 size,
593 level,
594 order_type: OrderType::Limit,
595 time_in_force: TimeInForce::GoodTillCancelled,
596 guaranteed_stop: None,
597 stop_level: None,
598 stop_distance: None,
599 limit_level: None,
600 limit_distance: None,
601 good_till_date: None,
602 deal_reference: None,
603 currency_code: None,
604 }
605 }
606
607 pub fn stop(epic: String, direction: Direction, size: f64, level: f64) -> Self {
609 Self {
610 epic,
611 direction,
612 size,
613 level,
614 order_type: OrderType::Stop,
615 time_in_force: TimeInForce::GoodTillCancelled,
616 guaranteed_stop: None,
617 stop_level: None,
618 stop_distance: None,
619 limit_level: None,
620 limit_distance: None,
621 good_till_date: None,
622 deal_reference: None,
623 currency_code: None,
624 }
625 }
626
627 pub fn with_stop_loss(mut self, stop_level: f64) -> Self {
629 self.stop_level = Some(stop_level);
630 self
631 }
632
633 pub fn with_take_profit(mut self, limit_level: f64) -> Self {
635 self.limit_level = Some(limit_level);
636 self
637 }
638
639 pub fn with_reference(mut self, reference: String) -> Self {
641 self.deal_reference = Some(reference);
642 self
643 }
644
645 pub fn expires_at(mut self, date: String) -> Self {
647 self.time_in_force = TimeInForce::GoodTillDate;
648 self.good_till_date = Some(date);
649 self
650 }
651}
652
653#[derive(Debug, Clone, Deserialize)]
655pub struct CreateWorkingOrderResponse {
656 #[serde(rename = "dealReference")]
658 pub deal_reference: String,
659}