1use super::order::{Direction, OrderType, Status, TimeInForce};
7use crate::application::models::market::InstrumentType;
8use crate::impl_json_display;
9use crate::presentation::MarketState;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::ops::Add;
13
14#[derive(Debug, Clone, Deserialize)]
16pub struct AccountInfo {
17 pub accounts: Vec<Account>,
19}
20
21#[derive(Debug, Clone, Deserialize)]
23pub struct Account {
24 #[serde(rename = "accountId")]
26 pub account_id: String,
27 #[serde(rename = "accountName")]
29 pub account_name: String,
30 #[serde(rename = "accountType")]
32 pub account_type: String,
33 pub balance: AccountBalance,
35 pub currency: String,
37 pub status: String,
39 pub preferred: bool,
41}
42
43#[derive(Debug, Clone, Deserialize)]
45pub struct AccountBalance {
46 pub balance: f64,
48 pub deposit: f64,
50 #[serde(rename = "profitLoss")]
52 pub profit_loss: f64,
53 pub available: f64,
55}
56
57#[derive(Debug, Clone, Deserialize)]
59pub struct AccountActivity {
60 pub activities: Vec<Activity>,
62 pub metadata: Option<ActivityMetadata>,
64}
65
66#[derive(Debug, Clone, Deserialize)]
68pub struct ActivityMetadata {
69 pub paging: Option<ActivityPaging>,
71}
72
73#[derive(Debug, Clone, Deserialize)]
75pub struct ActivityPaging {
76 pub size: Option<i32>,
78 pub next: Option<String>,
80}
81
82#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
83pub enum ActivityType {
85 #[serde(rename = "EDIT_STOP_AND_LIMIT")]
87 EditStopAndLimit,
88 #[serde(rename = "POSITION")]
90 Position,
91 #[serde(rename = "SYSTEM")]
93 System,
94 #[serde(rename = "WORKING_ORDER")]
96 WorkingOrder,
97}
98
99#[derive(Debug, Clone, Deserialize, Serialize)]
101pub struct Activity {
102 pub date: String,
104 #[serde(rename = "dealId", default)]
106 pub deal_id: Option<String>,
107 #[serde(default)]
109 pub epic: Option<String>,
110 #[serde(default)]
112 pub period: Option<String>,
113 #[serde(rename = "dealReference", default)]
115 pub deal_reference: Option<String>,
116 #[serde(rename = "type")]
118 pub activity_type: ActivityType,
119 #[serde(default)]
121 pub status: Option<Status>,
122 #[serde(default)]
124 pub description: Option<String>,
125 #[serde(default)]
128 pub details: Option<ActivityDetails>,
129 #[serde(default)]
131 pub channel: Option<String>,
132 #[serde(default)]
134 pub currency: Option<String>,
135 #[serde(default)]
137 pub level: Option<String>,
138}
139
140#[derive(Debug, Clone, Deserialize, Serialize)]
143pub struct ActivityDetails {
144 #[serde(rename = "dealReference", default)]
146 pub deal_reference: Option<String>,
147 #[serde(default)]
149 pub actions: Vec<ActivityAction>,
150 #[serde(rename = "marketName", default)]
152 pub market_name: Option<String>,
153 #[serde(rename = "goodTillDate", default)]
155 pub good_till_date: Option<String>,
156 #[serde(default)]
158 pub currency: Option<String>,
159 #[serde(default)]
161 pub size: Option<f64>,
162 #[serde(default)]
164 pub direction: Option<Direction>,
165 #[serde(default)]
167 pub level: Option<f64>,
168 #[serde(rename = "stopLevel", default)]
170 pub stop_level: Option<f64>,
171 #[serde(rename = "stopDistance", default)]
173 pub stop_distance: Option<f64>,
174 #[serde(rename = "guaranteedStop", default)]
176 pub guaranteed_stop: Option<bool>,
177 #[serde(rename = "trailingStopDistance", default)]
179 pub trailing_stop_distance: Option<f64>,
180 #[serde(rename = "trailingStep", default)]
182 pub trailing_step: Option<f64>,
183 #[serde(rename = "limitLevel", default)]
185 pub limit_level: Option<f64>,
186 #[serde(rename = "limitDistance", default)]
188 pub limit_distance: Option<f64>,
189}
190
191#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
193pub enum ActionType {
194 #[serde(rename = "LIMIT_ORDER_DELETED")]
196 LimitOrderDeleted,
197 #[serde(rename = "LIMIT_ORDER_FILLED")]
199 LimitOrderFilled,
200 #[serde(rename = "LIMIT_ORDER_OPENED")]
202 LimitOrderOpened,
203 #[serde(rename = "LIMIT_ORDER_ROLLED")]
205 LimitOrderRolled,
206 #[serde(rename = "POSITION_CLOSED")]
208 PositionClosed,
209 #[serde(rename = "POSITION_DELETED")]
211 PositionDeleted,
212 #[serde(rename = "POSITION_OPENED")]
214 PositionOpened,
215 #[serde(rename = "POSITION_PARTIALLY_CLOSED")]
217 PositionPartiallyClosed,
218 #[serde(rename = "POSITION_ROLLED")]
220 PositionRolled,
221 #[serde(rename = "STOP_LIMIT_AMENDED")]
223 StopLimitAmended,
224 #[serde(rename = "STOP_ORDER_AMENDED")]
226 StopOrderAmended,
227 #[serde(rename = "STOP_ORDER_DELETED")]
229 StopOrderDeleted,
230 #[serde(rename = "STOP_ORDER_FILLED")]
232 StopOrderFilled,
233 #[serde(rename = "STOP_ORDER_OPENED")]
235 StopOrderOpened,
236 #[serde(rename = "STOP_ORDER_ROLLED")]
238 StopOrderRolled,
239 #[serde(rename = "UNKNOWN")]
241 Unknown,
242 #[serde(rename = "WORKING_ORDER_DELETED")]
244 WorkingOrderDeleted,
245}
246
247impl_json_display!(ActionType);
248
249#[derive(Debug, Clone, Deserialize, Serialize)]
251pub struct ActivityAction {
252 #[serde(rename = "actionType")]
254 pub action_type: ActionType,
255 #[serde(rename = "affectedDealId", default)]
257 pub affected_deal_id: Option<String>,
258}
259
260#[derive(Debug, Clone, Deserialize, Serialize)]
262pub struct Positions {
263 pub positions: Vec<Position>,
265}
266
267impl Positions {
268 pub fn compact_by_epic(positions: Vec<Position>) -> Vec<Position> {
279 let mut epic_map: HashMap<String, Position> = std::collections::HashMap::new();
280
281 for position in positions {
282 let epic = position.market.epic.clone();
283 epic_map
284 .entry(epic)
285 .and_modify(|existing| {
286 *existing = existing.clone() + position.clone();
287 })
288 .or_insert(position);
289 }
290
291 epic_map.into_values().collect()
292 }
293}
294
295#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct Position {
298 pub position: PositionDetails,
300 pub market: PositionMarket,
302 pub pnl: Option<f64>,
304}
305
306impl Add for Position {
307 type Output = Position;
308
309 fn add(self, other: Position) -> Position {
310 if self.market.epic != other.market.epic {
311 panic!("Cannot add positions from different markets");
312 }
313 Position {
314 position: self.position + other.position,
315 market: self.market,
316 pnl: match (self.pnl, other.pnl) {
317 (Some(a), Some(b)) => Some(a + b),
318 (Some(a), None) => Some(a),
319 (None, Some(b)) => Some(b),
320 (None, None) => None,
321 },
322 }
323 }
324}
325
326#[derive(Debug, Clone, Deserialize, Serialize)]
328pub struct PositionDetails {
329 #[serde(rename = "contractSize")]
331 pub contract_size: f64,
332 #[serde(rename = "createdDate")]
334 pub created_date: String,
335 #[serde(rename = "createdDateUTC")]
337 pub created_date_utc: String,
338 #[serde(rename = "dealId")]
340 pub deal_id: String,
341 #[serde(rename = "dealReference")]
343 pub deal_reference: String,
344 pub direction: Direction,
346 #[serde(rename = "limitLevel")]
348 pub limit_level: Option<f64>,
349 pub level: f64,
351 pub size: f64,
353 #[serde(rename = "stopLevel")]
355 pub stop_level: Option<f64>,
356 #[serde(rename = "trailingStep")]
358 pub trailing_step: Option<f64>,
359 #[serde(rename = "trailingStopDistance")]
361 pub trailing_stop_distance: Option<f64>,
362 pub currency: String,
364 #[serde(rename = "controlledRisk")]
366 pub controlled_risk: bool,
367 #[serde(rename = "limitedRiskPremium")]
369 pub limited_risk_premium: Option<f64>,
370}
371
372impl Add for PositionDetails {
373 type Output = PositionDetails;
374
375 fn add(self, other: PositionDetails) -> PositionDetails {
376 let (contract_size, size) = if self.direction != other.direction {
377 (
378 (self.contract_size - other.contract_size).abs(),
379 (self.size - other.size).abs(),
380 )
381 } else {
382 (
383 self.contract_size + other.contract_size,
384 self.size + other.size,
385 )
386 };
387
388 PositionDetails {
389 contract_size,
390 created_date: self.created_date,
391 created_date_utc: self.created_date_utc,
392 deal_id: self.deal_id,
393 deal_reference: self.deal_reference,
394 direction: self.direction,
395 limit_level: other.limit_level.or(self.limit_level),
396 level: (self.level + other.level) / 2.0, size,
398 stop_level: other.stop_level.or(self.stop_level),
399 trailing_step: other.trailing_step.or(self.trailing_step),
400 trailing_stop_distance: other.trailing_stop_distance.or(self.trailing_stop_distance),
401 currency: self.currency.clone(),
402 controlled_risk: self.controlled_risk || other.controlled_risk,
403 limited_risk_premium: other.limited_risk_premium.or(self.limited_risk_premium),
404 }
405 }
406}
407
408#[derive(Debug, Clone, Deserialize, Serialize)]
410pub struct PositionMarket {
411 #[serde(rename = "instrumentName")]
413 pub instrument_name: String,
414 pub expiry: String,
416 pub epic: String,
418 #[serde(rename = "instrumentType")]
420 pub instrument_type: String,
421 #[serde(rename = "lotSize")]
423 pub lot_size: f64,
424 pub high: f64,
426 pub low: f64,
428 #[serde(rename = "percentageChange")]
430 pub percentage_change: f64,
431 #[serde(rename = "netChange")]
433 pub net_change: f64,
434 pub bid: f64,
436 pub offer: f64,
438 #[serde(rename = "updateTime")]
440 pub update_time: String,
441 #[serde(rename = "updateTimeUTC")]
443 pub update_time_utc: String,
444 #[serde(rename = "delayTime")]
446 pub delay_time: i64,
447 #[serde(rename = "streamingPricesAvailable")]
449 pub streaming_prices_available: bool,
450 #[serde(rename = "marketStatus")]
452 pub market_status: String,
453 #[serde(rename = "scalingFactor")]
455 pub scaling_factor: i64,
456}
457
458#[derive(Debug, Clone, Deserialize, Serialize)]
460pub struct WorkingOrders {
461 #[serde(rename = "workingOrders")]
463 pub working_orders: Vec<WorkingOrder>,
464}
465
466#[derive(Debug, Clone, Deserialize, Serialize)]
468pub struct WorkingOrder {
469 #[serde(rename = "workingOrderData")]
471 pub working_order_data: WorkingOrderData,
472 #[serde(rename = "marketData")]
474 pub market_data: MarketData,
475}
476
477#[derive(Debug, Clone, Deserialize, Serialize)]
479pub struct WorkingOrderData {
480 #[serde(rename = "dealId")]
482 pub deal_id: String,
483 pub direction: Direction,
485 pub epic: String,
487 #[serde(rename = "orderSize")]
489 pub order_size: f64,
490 #[serde(rename = "orderLevel")]
492 pub order_level: f64,
493 #[serde(rename = "timeInForce")]
495 pub time_in_force: TimeInForce,
496 #[serde(rename = "goodTillDate")]
498 pub good_till_date: Option<String>,
499 #[serde(rename = "goodTillDateISO")]
501 pub good_till_date_iso: Option<String>,
502 #[serde(rename = "createdDate")]
504 pub created_date: String,
505 #[serde(rename = "createdDateUTC")]
507 pub created_date_utc: String,
508 #[serde(rename = "guaranteedStop")]
510 pub guaranteed_stop: bool,
511 #[serde(rename = "orderType")]
513 pub order_type: OrderType,
514 #[serde(rename = "stopDistance")]
516 pub stop_distance: Option<f64>,
517 #[serde(rename = "limitDistance")]
519 pub limit_distance: Option<f64>,
520 #[serde(rename = "currencyCode")]
522 pub currency_code: String,
523 pub dma: bool,
525 #[serde(rename = "limitedRiskPremium")]
527 pub limited_risk_premium: Option<f64>,
528 #[serde(rename = "limitLevel", default)]
530 pub limit_level: Option<f64>,
531 #[serde(rename = "stopLevel", default)]
533 pub stop_level: Option<f64>,
534 #[serde(rename = "dealReference", default)]
536 pub deal_reference: Option<String>,
537}
538
539#[derive(Debug, Clone, Deserialize, Serialize)]
541pub struct MarketData {
542 #[serde(rename = "instrumentName")]
544 pub instrument_name: String,
545 #[serde(rename = "exchangeId")]
547 pub exchange_id: String,
548 pub expiry: String,
550 #[serde(rename = "marketStatus")]
552 pub market_status: MarketState,
553 pub epic: String,
555 #[serde(rename = "instrumentType")]
557 pub instrument_type: InstrumentType,
558 #[serde(rename = "lotSize")]
560 pub lot_size: f64,
561 pub high: f64,
563 pub low: f64,
565 #[serde(rename = "percentageChange")]
567 pub percentage_change: f64,
568 #[serde(rename = "netChange")]
570 pub net_change: f64,
571 pub bid: f64,
573 pub offer: f64,
575 #[serde(rename = "updateTime")]
577 pub update_time: String,
578 #[serde(rename = "updateTimeUTC")]
580 pub update_time_utc: String,
581 #[serde(rename = "delayTime")]
583 pub delay_time: i64,
584 #[serde(rename = "streamingPricesAvailable")]
586 pub streaming_prices_available: bool,
587 #[serde(rename = "scalingFactor")]
589 pub scaling_factor: i64,
590}
591
592#[derive(Debug, Clone, Deserialize, Serialize)]
594pub struct TransactionHistory {
595 pub transactions: Vec<AccountTransaction>,
597 pub metadata: TransactionMetadata,
599}
600
601#[derive(Debug, Clone, Deserialize, Serialize)]
603pub struct TransactionMetadata {
604 #[serde(rename = "pageData")]
606 pub page_data: PageData,
607 pub size: i32,
609}
610
611#[derive(Debug, Clone, Deserialize, Serialize)]
613pub struct PageData {
614 #[serde(rename = "pageNumber")]
616 pub page_number: i32,
617 #[serde(rename = "pageSize")]
619 pub page_size: i32,
620 #[serde(rename = "totalPages")]
622 pub total_pages: i32,
623}
624
625#[derive(Debug, Clone, Deserialize, Serialize)]
627pub struct AccountTransaction {
628 pub date: String,
630 #[serde(rename = "dateUtc")]
632 pub date_utc: String,
633 #[serde(rename = "openDateUtc")]
635 pub open_date_utc: String,
636 #[serde(rename = "instrumentName")]
638 pub instrument_name: String,
639 pub period: String,
641 #[serde(rename = "profitAndLoss")]
643 pub profit_and_loss: String,
644 #[serde(rename = "transactionType")]
646 pub transaction_type: String,
647 pub reference: String,
649 #[serde(rename = "openLevel")]
651 pub open_level: String,
652 #[serde(rename = "closeLevel")]
654 pub close_level: String,
655 pub size: String,
657 pub currency: String,
659 #[serde(rename = "cashTransaction")]
661 pub cash_transaction: bool,
662}
663
664impl_json_display!(
665 Positions,
666 Position,
667 AccountTransaction,
668 MarketData,
669 PositionDetails,
670 PositionMarket
671);