1use crate::constants::{DEFAULT_ORDER_BUY_LEVEL, DEFAULT_ORDER_SELL_LEVEL};
7use crate::prelude::{Deserialize, Serialize, WorkingOrder};
8use crate::presentation::order::{Direction, OrderType, TimeInForce};
9use pretty_simple_display::DisplaySimple;
10
11#[derive(Debug, Clone, Default, Deserialize, Serialize)]
13pub struct RecentPricesRequest<'a> {
14 pub epic: &'a str,
16 pub resolution: Option<&'a str>,
18 pub from: Option<&'a str>,
20 pub to: Option<&'a str>,
22 pub max_points: Option<i32>,
24 pub page_size: Option<i32>,
26 pub page_number: Option<i32>,
28}
29
30impl<'a> RecentPricesRequest<'a> {
31 pub fn new(epic: &'a str) -> Self {
33 Self {
34 epic,
35 ..Default::default()
36 }
37 }
38
39 pub fn with_resolution(mut self, resolution: &'a str) -> Self {
41 self.resolution = Some(resolution);
42 self
43 }
44
45 pub fn with_from(mut self, from: &'a str) -> Self {
47 self.from = Some(from);
48 self
49 }
50
51 pub fn with_to(mut self, to: &'a str) -> Self {
53 self.to = Some(to);
54 self
55 }
56
57 pub fn with_max_points(mut self, max_points: i32) -> Self {
59 self.max_points = Some(max_points);
60 self
61 }
62
63 pub fn with_page_size(mut self, page_size: i32) -> Self {
65 self.page_size = Some(page_size);
66 self
67 }
68
69 pub fn with_page_number(mut self, page_number: i32) -> Self {
71 self.page_number = Some(page_number);
72 self
73 }
74}
75
76#[derive(Debug, Clone, DisplaySimple, Serialize, Deserialize)]
78pub struct CreateOrderRequest {
79 pub epic: String,
81 pub direction: Direction,
83 pub size: f64,
85 #[serde(rename = "orderType")]
87 pub order_type: OrderType,
88 #[serde(rename = "timeInForce")]
90 pub time_in_force: TimeInForce,
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub level: Option<f64>,
94 #[serde(rename = "guaranteedStop")]
96 pub guaranteed_stop: bool,
97 #[serde(rename = "stopLevel", skip_serializing_if = "Option::is_none")]
99 pub stop_level: Option<f64>,
100 #[serde(rename = "stopDistance", skip_serializing_if = "Option::is_none")]
102 pub stop_distance: Option<f64>,
103 #[serde(rename = "limitLevel", skip_serializing_if = "Option::is_none")]
105 pub limit_level: Option<f64>,
106 #[serde(rename = "limitDistance", skip_serializing_if = "Option::is_none")]
108 pub limit_distance: Option<f64>,
109 #[serde(skip_serializing_if = "Option::is_none")]
111 pub expiry: Option<String>,
112 #[serde(rename = "dealReference", skip_serializing_if = "Option::is_none")]
114 pub deal_reference: Option<String>,
115 #[serde(rename = "forceOpen")]
117 pub force_open: bool,
118 #[serde(rename = "currencyCode")]
120 pub currency_code: String,
121 #[serde(rename = "quoteId", skip_serializing_if = "Option::is_none")]
123 pub quote_id: Option<String>,
124 #[serde(rename = "trailingStop", skip_serializing_if = "Option::is_none")]
126 pub trailing_stop: Option<bool>,
127 #[serde(
129 rename = "trailingStopIncrement",
130 skip_serializing_if = "Option::is_none"
131 )]
132 pub trailing_stop_increment: Option<f64>,
133}
134
135impl CreateOrderRequest {
136 pub fn market(
138 epic: String,
139 direction: Direction,
140 size: f64,
141 currency_code: Option<String>,
142 deal_reference: Option<String>,
143 ) -> Self {
144 let rounded_size = (size * 100.0).floor() / 100.0;
145
146 let currency_code = currency_code.unwrap_or_else(|| "EUR".to_string());
147
148 Self {
149 epic,
150 direction,
151 size: rounded_size,
152 order_type: OrderType::Market,
153 time_in_force: TimeInForce::FillOrKill,
154 level: None,
155 guaranteed_stop: false,
156 stop_level: None,
157 stop_distance: None,
158 limit_level: None,
159 limit_distance: None,
160 expiry: Some("-".to_string()),
161 deal_reference,
162 force_open: true,
163 currency_code,
164 quote_id: None,
165 trailing_stop: Some(false),
166 trailing_stop_increment: None,
167 }
168 }
169
170 pub fn limit(
172 epic: String,
173 direction: Direction,
174 size: f64,
175 level: f64,
176 currency_code: Option<String>,
177 deal_reference: Option<String>,
178 ) -> Self {
179 let rounded_size = (size * 100.0).floor() / 100.0;
180
181 let currency_code = currency_code.unwrap_or_else(|| "EUR".to_string());
182
183 Self {
184 epic,
185 direction,
186 size: rounded_size,
187 order_type: OrderType::Limit,
188 time_in_force: TimeInForce::GoodTillCancelled,
189 level: Some(level),
190 guaranteed_stop: false,
191 stop_level: None,
192 stop_distance: None,
193 limit_level: None,
194 limit_distance: None,
195 expiry: None,
196 deal_reference,
197 force_open: true,
198 currency_code,
199 quote_id: None,
200 trailing_stop: Some(false),
201 trailing_stop_increment: None,
202 }
203 }
204
205 pub fn sell_option_to_market(
238 epic: String,
239 size: f64,
240 expiry: Option<String>,
241 deal_reference: Option<String>,
242 currency_code: Option<String>,
243 ) -> Self {
244 let rounded_size = (size * 100.0).floor() / 100.0;
245
246 let currency_code = currency_code.unwrap_or_else(|| "EUR".to_string());
247
248 let deal_reference =
249 deal_reference.or_else(|| Some(nanoid::nanoid!(30, &nanoid::alphabet::SAFE)));
250
251 Self {
252 epic,
253 direction: Direction::Sell,
254 size: rounded_size,
255 order_type: OrderType::Limit,
256 time_in_force: TimeInForce::FillOrKill,
257 level: Some(DEFAULT_ORDER_SELL_LEVEL),
258 guaranteed_stop: false,
259 stop_level: None,
260 stop_distance: None,
261 limit_level: None,
262 limit_distance: None,
263 expiry: expiry.clone(),
264 deal_reference: deal_reference.clone(),
265 force_open: true,
266 currency_code,
267 quote_id: None,
268 trailing_stop: Some(false),
269 trailing_stop_increment: None,
270 }
271 }
272
273 pub fn sell_option_to_market_w_force(
306 epic: String,
307 size: f64,
308 expiry: Option<String>,
309 deal_reference: Option<String>,
310 currency_code: Option<String>,
311 force_open: bool, ) -> Self {
313 let rounded_size = (size * 100.0).floor() / 100.0;
314
315 let currency_code = currency_code.unwrap_or_else(|| "EUR".to_string());
316
317 let deal_reference =
318 deal_reference.or_else(|| Some(nanoid::nanoid!(30, &nanoid::alphabet::SAFE)));
319
320 Self {
321 epic,
322 direction: Direction::Sell,
323 size: rounded_size,
324 order_type: OrderType::Limit,
325 time_in_force: TimeInForce::FillOrKill,
326 level: Some(DEFAULT_ORDER_SELL_LEVEL),
327 guaranteed_stop: false,
328 stop_level: None,
329 stop_distance: None,
330 limit_level: None,
331 limit_distance: None,
332 expiry: expiry.clone(),
333 deal_reference: deal_reference.clone(),
334 force_open,
335 currency_code,
336 quote_id: None,
337 trailing_stop: Some(false),
338 trailing_stop_increment: None,
339 }
340 }
341
342 pub fn buy_option_to_market(
362 epic: String,
363 size: f64,
364 expiry: Option<String>,
365 deal_reference: Option<String>,
366 currency_code: Option<String>,
367 ) -> Self {
368 let rounded_size = (size * 100.0).floor() / 100.0;
369
370 let currency_code = currency_code.unwrap_or_else(|| "EUR".to_string());
371
372 let deal_reference =
373 deal_reference.or_else(|| Some(nanoid::nanoid!(30, &nanoid::alphabet::SAFE)));
374
375 Self {
376 epic,
377 direction: Direction::Buy,
378 size: rounded_size,
379 order_type: OrderType::Limit,
380 time_in_force: TimeInForce::FillOrKill,
381 level: Some(DEFAULT_ORDER_BUY_LEVEL),
382 guaranteed_stop: false,
383 stop_level: None,
384 stop_distance: None,
385 limit_level: None,
386 limit_distance: None,
387 expiry: expiry.clone(),
388 deal_reference: deal_reference.clone(),
389 force_open: true,
390 currency_code: currency_code.clone(),
391 quote_id: None,
392 trailing_stop: Some(false),
393 trailing_stop_increment: None,
394 }
395 }
396
397 pub fn buy_option_to_market_w_force(
425 epic: String,
426 size: f64,
427 expiry: Option<String>,
428 deal_reference: Option<String>,
429 currency_code: Option<String>,
430 force_open: bool,
431 ) -> Self {
432 let rounded_size = (size * 100.0).floor() / 100.0;
433
434 let currency_code = currency_code.unwrap_or_else(|| "EUR".to_string());
435
436 let deal_reference =
437 deal_reference.or_else(|| Some(nanoid::nanoid!(30, &nanoid::alphabet::SAFE)));
438
439 Self {
440 epic,
441 direction: Direction::Buy,
442 size: rounded_size,
443 order_type: OrderType::Limit,
444 time_in_force: TimeInForce::FillOrKill,
445 level: Some(DEFAULT_ORDER_BUY_LEVEL),
446 guaranteed_stop: false,
447 stop_level: None,
448 stop_distance: None,
449 limit_level: None,
450 limit_distance: None,
451 expiry: expiry.clone(),
452 deal_reference: deal_reference.clone(),
453 force_open,
454 currency_code: currency_code.clone(),
455 quote_id: None,
456 trailing_stop: Some(false),
457 trailing_stop_increment: None,
458 }
459 }
460
461 pub fn with_stop_loss(mut self, stop_level: f64) -> Self {
463 self.stop_level = Some(stop_level);
464 self
465 }
466
467 pub fn with_take_profit(mut self, limit_level: f64) -> Self {
469 self.limit_level = Some(limit_level);
470 self
471 }
472
473 pub fn with_trailing_stop_loss(mut self, trailing_stop_increment: f64) -> Self {
475 self.trailing_stop = Some(true);
476 self.trailing_stop_increment = Some(trailing_stop_increment);
477 self
478 }
479
480 pub fn with_reference(mut self, reference: String) -> Self {
482 self.deal_reference = Some(reference);
483 self
484 }
485
486 pub fn with_stop_distance(mut self, stop_distance: f64) -> Self {
488 self.stop_distance = Some(stop_distance);
489 self
490 }
491
492 pub fn with_limit_distance(mut self, limit_distance: f64) -> Self {
494 self.limit_distance = Some(limit_distance);
495 self
496 }
497
498 pub fn with_guaranteed_stop(mut self, guaranteed: bool) -> Self {
500 self.guaranteed_stop = guaranteed;
501 self
502 }
503}
504
505#[derive(Debug, Clone, DisplaySimple, Serialize, Deserialize)]
507pub struct UpdatePositionRequest {
508 #[serde(rename = "stopLevel", skip_serializing_if = "Option::is_none")]
510 pub stop_level: Option<f64>,
511 #[serde(rename = "limitLevel", skip_serializing_if = "Option::is_none")]
513 pub limit_level: Option<f64>,
514 #[serde(rename = "trailingStop", skip_serializing_if = "Option::is_none")]
516 pub trailing_stop: Option<bool>,
517 #[serde(
519 rename = "trailingStopDistance",
520 skip_serializing_if = "Option::is_none"
521 )]
522 pub trailing_stop_distance: Option<f64>,
523}
524
525#[derive(Debug, Clone, DisplaySimple, Serialize, Deserialize)]
527pub struct ClosePositionRequest {
528 #[serde(rename = "dealId", skip_serializing_if = "Option::is_none")]
530 pub deal_id: Option<String>,
531 pub direction: Direction,
533 #[serde(skip_serializing_if = "Option::is_none")]
535 pub epic: Option<String>,
536 #[serde(rename = "expiry", skip_serializing_if = "Option::is_none")]
538 pub expiry: Option<String>,
539 #[serde(rename = "level", skip_serializing_if = "Option::is_none")]
541 pub level: Option<f64>,
542 #[serde(rename = "orderType")]
544 pub order_type: OrderType,
545 #[serde(rename = "quoteId", skip_serializing_if = "Option::is_none")]
547 pub quote_id: Option<String>,
548 pub size: f64,
550 #[serde(rename = "timeInForce")]
552 pub time_in_force: TimeInForce,
553}
554
555impl ClosePositionRequest {
556 pub fn market(deal_id: String, direction: Direction, size: f64) -> Self {
558 Self {
559 deal_id: Some(deal_id),
560 direction,
561 size,
562 order_type: OrderType::Market,
563 time_in_force: TimeInForce::FillOrKill,
564 level: None,
565 expiry: None,
566 epic: None,
567 quote_id: None,
568 }
569 }
570
571 pub fn limit(deal_id: String, direction: Direction, size: f64, level: f64) -> Self {
575 Self {
576 deal_id: Some(deal_id),
577 direction,
578 size,
579 order_type: OrderType::Limit,
580 time_in_force: TimeInForce::FillOrKill,
581 level: Some(level),
582 expiry: None,
583 epic: None,
584 quote_id: None,
585 }
586 }
587
588 pub fn close_option_to_market_by_id(deal_id: String, direction: Direction, size: f64) -> Self {
598 let level = match direction {
601 Direction::Buy => Some(DEFAULT_ORDER_BUY_LEVEL),
602 Direction::Sell => Some(DEFAULT_ORDER_SELL_LEVEL),
603 };
604
605 Self {
606 deal_id: Some(deal_id),
607 direction,
608 size,
609 order_type: OrderType::Limit,
610 time_in_force: TimeInForce::FillOrKill,
611 level,
612 expiry: None,
613 epic: None,
614 quote_id: None,
615 }
616 }
617
618 pub fn close_option_to_market_by_epic(
630 epic: String,
631 expiry: String,
632 direction: Direction,
633 size: f64,
634 ) -> Self {
635 let level = match direction {
638 Direction::Buy => Some(DEFAULT_ORDER_BUY_LEVEL),
639 Direction::Sell => Some(DEFAULT_ORDER_SELL_LEVEL),
640 };
641
642 Self {
643 deal_id: None,
644 direction,
645 size,
646 order_type: OrderType::Limit,
647 time_in_force: TimeInForce::FillOrKill,
648 level,
649 expiry: Some(expiry),
650 epic: Some(epic),
651 quote_id: None,
652 }
653 }
654}
655
656#[derive(Debug, Clone, DisplaySimple, Deserialize, Serialize, Default)]
658pub struct CreateWorkingOrderRequest {
659 pub epic: String,
661 pub direction: Direction,
663 pub size: f64,
665 pub level: f64,
667 #[serde(rename = "type")]
669 pub order_type: OrderType,
670 #[serde(rename = "timeInForce")]
672 pub time_in_force: TimeInForce,
673 #[serde(rename = "guaranteedStop", skip_serializing_if = "Option::is_none")]
675 pub guaranteed_stop: Option<bool>,
676 #[serde(rename = "stopLevel", skip_serializing_if = "Option::is_none")]
678 pub stop_level: Option<f64>,
679 #[serde(rename = "stopDistance", skip_serializing_if = "Option::is_none")]
681 pub stop_distance: Option<f64>,
682 #[serde(rename = "limitLevel", skip_serializing_if = "Option::is_none")]
684 pub limit_level: Option<f64>,
685 #[serde(rename = "limitDistance", skip_serializing_if = "Option::is_none")]
687 pub limit_distance: Option<f64>,
688 #[serde(rename = "goodTillDate", skip_serializing_if = "Option::is_none")]
690 pub good_till_date: Option<String>,
691 #[serde(rename = "dealReference", skip_serializing_if = "Option::is_none")]
693 pub deal_reference: Option<String>,
694 #[serde(rename = "currencyCode")]
696 pub currency_code: String,
697 pub expiry: String,
699}
700
701impl From<WorkingOrder> for CreateWorkingOrderRequest {
702 fn from(value: WorkingOrder) -> Self {
703 let data = value.working_order_data;
704 Self {
705 epic: data.epic,
706 direction: data.direction,
707 size: data.order_size,
708 level: data.order_level,
709 order_type: data.order_type,
710 time_in_force: data.time_in_force,
711 guaranteed_stop: Some(data.guaranteed_stop),
712 stop_level: data.stop_level,
713 stop_distance: data.stop_distance,
714 limit_level: data.limit_level,
715 limit_distance: data.limit_distance,
716 good_till_date: data.good_till_date,
717 deal_reference: data.deal_reference,
718 currency_code: data.currency_code,
719 expiry: value.market_data.expiry,
720 }
721 }
722}
723
724impl CreateWorkingOrderRequest {
725 pub fn limit(
727 epic: String,
728 direction: Direction,
729 size: f64,
730 level: f64,
731 currency_code: String,
732 expiry: String,
733 ) -> Self {
734 Self {
735 epic,
736 direction,
737 size,
738 level,
739 order_type: OrderType::Limit,
740 time_in_force: TimeInForce::GoodTillCancelled,
741 guaranteed_stop: Some(false),
742 stop_level: None,
743 stop_distance: None,
744 limit_level: None,
745 limit_distance: None,
746 good_till_date: None,
747 deal_reference: None,
748 currency_code,
749 expiry,
750 }
751 }
752
753 pub fn stop(
755 epic: String,
756 direction: Direction,
757 size: f64,
758 level: f64,
759 currency_code: String,
760 expiry: String,
761 ) -> Self {
762 Self {
763 epic,
764 direction,
765 size,
766 level,
767 order_type: OrderType::Stop,
768 time_in_force: TimeInForce::GoodTillCancelled,
769 guaranteed_stop: Some(false),
770 stop_level: None,
771 stop_distance: None,
772 limit_level: None,
773 limit_distance: None,
774 good_till_date: None,
775 deal_reference: None,
776 currency_code,
777 expiry,
778 }
779 }
780
781 pub fn with_stop_loss(mut self, stop_level: f64) -> Self {
783 self.stop_level = Some(stop_level);
784 self
785 }
786
787 pub fn with_take_profit(mut self, limit_level: f64) -> Self {
789 self.limit_level = Some(limit_level);
790 self
791 }
792
793 pub fn with_reference(mut self, reference: String) -> Self {
795 self.deal_reference = Some(reference);
796 self
797 }
798
799 pub fn expires_at(mut self, date: String) -> Self {
801 self.time_in_force = TimeInForce::GoodTillDate;
802 self.good_till_date = Some(date);
803 self
804 }
805}