ig_client/application/services/
order_service.rs

1use crate::application::models::account::WorkingOrders;
2use crate::application::models::order::{
3    ClosePositionRequest, ClosePositionResponse, CreateOrderRequest, CreateOrderResponse,
4    OrderConfirmation, UpdatePositionRequest, UpdatePositionResponse,
5};
6use crate::application::models::working_order::{
7    CreateWorkingOrderRequest, CreateWorkingOrderResponse,
8};
9use crate::application::services::interfaces::order::OrderService;
10use crate::config::Config;
11use crate::error::AppError;
12use crate::session::interface::IgSession;
13use crate::transport::http_client::IgHttpClient;
14use async_trait::async_trait;
15use reqwest::Method;
16use std::sync::Arc;
17use tracing::{debug, info};
18
19/// Implementation of the order service
20pub struct OrderServiceImpl<T: IgHttpClient> {
21    config: Arc<Config>,
22    client: Arc<T>,
23}
24
25impl<T: IgHttpClient> OrderServiceImpl<T> {
26    /// Creates a new instance of the order service
27    pub fn new(config: Arc<Config>, client: Arc<T>) -> Self {
28        Self { config, client }
29    }
30
31    /// Gets the current configuration
32    ///
33    /// # Returns
34    /// * The current configuration as an `Arc<Config>`
35    pub fn get_config(&self) -> Arc<Config> {
36        self.config.clone()
37    }
38
39    /// Sets a new configuration
40    ///
41    /// # Arguments
42    /// * `config` - The new configuration to use
43    pub fn set_config(&mut self, config: Arc<Config>) {
44        self.config = config;
45    }
46}
47
48#[async_trait]
49impl<T: IgHttpClient + 'static> OrderService for OrderServiceImpl<T> {
50    async fn create_order(
51        &self,
52        session: &IgSession,
53        order: &CreateOrderRequest,
54    ) -> Result<CreateOrderResponse, AppError> {
55        info!("Creating order for: {}", order.epic);
56
57        let result = self
58            .client
59            .request::<CreateOrderRequest, CreateOrderResponse>(
60                Method::POST,
61                "positions/otc",
62                session,
63                Some(order),
64                "2",
65            )
66            .await?;
67
68        debug!("Order created with reference: {}", result.deal_reference);
69        Ok(result)
70    }
71
72    async fn get_order_confirmation(
73        &self,
74        session: &IgSession,
75        deal_reference: &str,
76    ) -> Result<OrderConfirmation, AppError> {
77        let path = format!("confirms/{deal_reference}");
78        info!("Getting confirmation for order: {}", deal_reference);
79
80        let result = self
81            .client
82            .request::<(), OrderConfirmation>(Method::GET, &path, session, None, "1")
83            .await?;
84
85        debug!("Confirmation obtained for order: {}", deal_reference);
86        Ok(result)
87    }
88
89    async fn update_position(
90        &self,
91        session: &IgSession,
92        deal_id: &str,
93        update: &UpdatePositionRequest,
94    ) -> Result<UpdatePositionResponse, AppError> {
95        let path = format!("positions/otc/{deal_id}");
96        info!("Updating position: {}", deal_id);
97
98        let result = self
99            .client
100            .request::<UpdatePositionRequest, UpdatePositionResponse>(
101                Method::PUT,
102                &path,
103                session,
104                Some(update),
105                "2",
106            )
107            .await?;
108
109        debug!(
110            "Position updated: {} with deal reference: {}",
111            deal_id, result.deal_reference
112        );
113        Ok(result)
114    }
115
116    async fn close_position(
117        &self,
118        session: &IgSession,
119        close_request: &ClosePositionRequest,
120    ) -> Result<ClosePositionResponse, AppError> {
121        info!("{}", serde_json::to_string(close_request)?);
122        let result = self
123            .client
124            .request::<ClosePositionRequest, ClosePositionResponse>(
125                Method::DELETE,
126                "positions/otc",
127                session,
128                Some(close_request),
129                "1",
130            )
131            .await?;
132
133        debug!("Position closed with reference: {}", result.deal_reference);
134        Ok(result)
135    }
136
137    async fn get_working_orders(&self, session: &IgSession) -> Result<WorkingOrders, AppError> {
138        info!("Getting all working orders");
139
140        let result = self
141            .client
142            .request::<(), WorkingOrders>(Method::GET, "workingorders", session, None, "2")
143            .await?;
144
145        debug!("Retrieved {} working orders", result.working_orders.len());
146        Ok(result)
147    }
148
149    async fn create_working_order(
150        &self,
151        session: &IgSession,
152        order: &CreateWorkingOrderRequest,
153    ) -> Result<CreateWorkingOrderResponse, AppError> {
154        info!("Creating working order for: {}", order.epic);
155
156        let result = self
157            .client
158            .request::<CreateWorkingOrderRequest, CreateWorkingOrderResponse>(
159                Method::POST,
160                "workingorders/otc",
161                session,
162                Some(order),
163                "2",
164            )
165            .await?;
166
167        debug!(
168            "Working order created with reference: {}",
169            result.deal_reference
170        );
171        Ok(result)
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    use crate::config::Config;
179    use crate::transport::http_client::IgHttpClientImpl;
180    use crate::utils::rate_limiter::RateLimitType;
181    use std::sync::Arc;
182
183    #[test]
184    fn test_get_and_set_config() {
185        let config = Arc::new(Config::with_rate_limit_type(
186            RateLimitType::NonTradingAccount,
187            0.7,
188        ));
189        let client = Arc::new(IgHttpClientImpl::new(config.clone()));
190        let mut service = OrderServiceImpl::new(config.clone(), client.clone());
191
192        let cfg1 = service.get_config();
193        assert!(Arc::ptr_eq(&cfg1, &config));
194
195        let new_cfg = Arc::new(Config::default());
196        service.set_config(new_cfg.clone());
197        assert!(Arc::ptr_eq(&service.get_config(), &new_cfg));
198    }
199}