ig_client/application/services/
order_service.rs

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