ig_client/application/services/interfaces/
market.rs

1use crate::application::models::market::{
2    HistoricalPricesResponse, MarketDetails, MarketNavigationResponse, MarketSearchResult,
3};
4use crate::application::services::types::DBEntry;
5use crate::error::AppError;
6use crate::session::interface::IgSession;
7use async_trait::async_trait;
8
9/// Parameters for getting recent prices (API v3)
10#[derive(Debug, Clone, Default)]
11pub struct RecentPricesParams<'a> {
12    /// Instrument epic
13    pub epic: &'a str,
14    /// Optional price resolution (default: MINUTE)
15    pub resolution: Option<&'a str>,
16    /// Optional start date time (yyyy-MM-dd'T'HH:mm:ss)
17    pub from: Option<&'a str>,
18    /// Optional end date time (yyyy-MM-dd'T'HH:mm:ss)
19    pub to: Option<&'a str>,
20    /// Optional max number of price points (default: 10)
21    pub max_points: Option<i32>,
22    /// Optional page size (default: 20, disable paging = 0)
23    pub page_size: Option<i32>,
24    /// Optional page number (default: 1)
25    pub page_number: Option<i32>,
26}
27
28impl<'a> RecentPricesParams<'a> {
29    /// Create new parameters with just the epic (required field)
30    pub fn new(epic: &'a str) -> Self {
31        Self {
32            epic,
33            ..Default::default()
34        }
35    }
36
37    /// Set the resolution
38    pub fn with_resolution(mut self, resolution: &'a str) -> Self {
39        self.resolution = Some(resolution);
40        self
41    }
42
43    /// Set the from date
44    pub fn with_from(mut self, from: &'a str) -> Self {
45        self.from = Some(from);
46        self
47    }
48
49    /// Set the to date
50    pub fn with_to(mut self, to: &'a str) -> Self {
51        self.to = Some(to);
52        self
53    }
54
55    /// Set the max points
56    pub fn with_max_points(mut self, max_points: i32) -> Self {
57        self.max_points = Some(max_points);
58        self
59    }
60
61    /// Set the page size
62    pub fn with_page_size(mut self, page_size: i32) -> Self {
63        self.page_size = Some(page_size);
64        self
65    }
66
67    /// Set the page number
68    pub fn with_page_number(mut self, page_number: i32) -> Self {
69        self.page_number = Some(page_number);
70        self
71    }
72}
73
74/// Interface for the market service
75#[async_trait]
76pub trait MarketService: Send + Sync {
77    /// Searches markets by search term
78    async fn search_markets(
79        &self,
80        session: &IgSession,
81        search_term: &str,
82    ) -> Result<MarketSearchResult, AppError>;
83
84    /// Gets details of a specific market by its EPIC
85    async fn get_market_details(
86        &self,
87        session: &IgSession,
88        epic: &str,
89    ) -> Result<MarketDetails, AppError>;
90
91    /// Gets details of multiple markets by their EPICs in a single request
92    ///
93    /// This method accepts a vector of EPICs and returns a vector of market details.
94    /// The EPICs are sent as a comma-separated list in a single API request.
95    ///
96    /// # Arguments
97    /// * `session` - The active IG session
98    /// * `epics` - A slice of EPICs to get details for
99    ///
100    /// # Returns
101    /// A vector of market details in the same order as the input EPICs
102    async fn get_multiple_market_details(
103        &self,
104        session: &IgSession,
105        epics: &[String],
106    ) -> Result<Vec<MarketDetails>, AppError>;
107
108    /// Gets historical prices for a market
109    async fn get_historical_prices(
110        &self,
111        session: &IgSession,
112        epic: &str,
113        resolution: &str,
114        from: &str,
115        to: &str,
116    ) -> Result<HistoricalPricesResponse, AppError>;
117
118    /// Gets historical prices for a market using path parameters (API v2)
119    ///
120    /// # Arguments
121    /// * `epic` - Instrument epic
122    /// * `resolution` - Price resolution (SECOND, MINUTE, MINUTE_2, MINUTE_3, MINUTE_5, MINUTE_10, MINUTE_15, MINUTE_30, HOUR, HOUR_2, HOUR_3, HOUR_4, DAY, WEEK, MONTH)
123    /// * `start_date` - Start date (yyyy-MM-dd HH:mm:ss)
124    /// * `end_date` - End date (yyyy-MM-dd HH:mm:ss). Must be later than the start date
125    async fn get_historical_prices_by_date_range(
126        &self,
127        session: &IgSession,
128        epic: &str,
129        resolution: &str,
130        start_date: &str,
131        end_date: &str,
132    ) -> Result<HistoricalPricesResponse, AppError>;
133
134    /// Gets recent historical prices for an instrument (API v3)
135    /// Returns minute prices within the last 10 minutes by default
136    ///
137    /// # Arguments
138    /// * `session` - The authenticated IG session
139    /// * `params` - Parameters for the recent prices request
140    ///
141    /// # Example
142    /// ```rust,no_run
143    /// use ig_client::application::services::{MarketService, RecentPricesParams};
144    /// # use ig_client::application::services::market_service::MarketServiceImpl;
145    /// # use ig_client::transport::http_client::IgHttpClientImpl;
146    /// # use ig_client::config::Config;
147    /// # use std::sync::Arc;
148    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
149    /// # let config = Arc::new(Config::default());
150    /// # let http_client = Arc::new(IgHttpClientImpl::new(config.clone()));
151    /// # let market_service = MarketServiceImpl::new(config, http_client);
152    /// # let session = ig_client::session::interface::IgSession::new(
153    /// #     "cst_token".to_string(),
154    /// #     "security_token".to_string(),
155    /// #     "account_id".to_string()
156    /// # );
157    ///
158    /// let params = RecentPricesParams::new("CS.D.EURUSD.TODAY.IP")
159    ///     .with_resolution("MINUTE_5")
160    ///     .with_max_points(10);
161    /// let prices = market_service.get_recent_prices(&session, &params).await?;
162    /// # Ok(())
163    /// # }
164    /// ```
165    async fn get_recent_prices(
166        &self,
167        session: &IgSession,
168        params: &RecentPricesParams<'_>,
169    ) -> Result<HistoricalPricesResponse, AppError>;
170
171    /// Gets historical prices by number of data points (API v1)
172    ///
173    /// # Arguments
174    /// * `epic` - Instrument epic
175    /// * `resolution` - Price resolution
176    /// * `num_points` - Number of data points required
177    async fn get_historical_prices_by_count_v1(
178        &self,
179        session: &IgSession,
180        epic: &str,
181        resolution: &str,
182        num_points: i32,
183    ) -> Result<HistoricalPricesResponse, AppError>;
184
185    /// Gets historical prices by number of data points (API v2)
186    ///
187    /// # Arguments
188    /// * `epic` - Instrument epic
189    /// * `resolution` - Price resolution
190    /// * `num_points` - Number of data points required
191    async fn get_historical_prices_by_count_v2(
192        &self,
193        session: &IgSession,
194        epic: &str,
195        resolution: &str,
196        num_points: i32,
197    ) -> Result<HistoricalPricesResponse, AppError>;
198
199    /// Gets the top-level market navigation nodes
200    ///
201    /// This method returns the root nodes of the market hierarchy, which can be used
202    /// to navigate through the available markets.
203    async fn get_market_navigation(
204        &self,
205        session: &IgSession,
206    ) -> Result<MarketNavigationResponse, AppError>;
207
208    /// Gets the market navigation node with the specified ID
209    ///
210    /// This method returns the child nodes and markets under the specified node ID.
211    ///
212    /// # Arguments
213    /// * `node_id` - The ID of the navigation node to retrieve
214    async fn get_market_navigation_node(
215        &self,
216        session: &IgSession,
217        node_id: &str,
218    ) -> Result<MarketNavigationResponse, AppError>;
219
220    /// Navigates through all levels of the market hierarchy and collects all MarketData
221    ///
222    /// This method performs a comprehensive traversal of the IG Markets hierarchy,
223    /// starting from the root navigation and going through multiple levels to collect
224    /// all available market instruments.
225    ///
226    /// # Arguments
227    /// * `session` - The authenticated IG session
228    /// * `max_levels` - Maximum depth to traverse (default: 5 levels)
229    ///
230    /// # Returns
231    /// * `Result<Vec<MarketData>, AppError>` - Vector containing all found market instruments
232    async fn get_all_markets(
233        &self,
234        session: &IgSession,
235    ) -> Result<Vec<crate::application::models::market::MarketData>, AppError>;
236
237    /// Gets all markets converted to database entries format
238    ///
239    /// This method retrieves all available markets and converts them to a standardized
240    /// database entry format for storage or further processing.
241    ///
242    /// # Arguments
243    /// * `session` - The authenticated IG session
244    ///
245    /// # Returns
246    /// * `Result<Vec<DBEntry>, AppError>` - Vector of database entries representing all markets
247    async fn get_vec_db_entries(&self, session: &IgSession) -> Result<Vec<DBEntry>, AppError>;
248}