ig_client/application/models/
market.rs

1pub(crate) use crate::presentation::InstrumentType;
2use serde::{Deserialize, Serialize};
3use std::fmt::Display;
4
5/// Model for a market instrument with enhanced deserialization
6#[derive(Debug, Clone, Deserialize, PartialEq)]
7pub struct Instrument {
8    /// Unique identifier for the instrument
9    pub epic: String,
10    /// Human-readable name of the instrument
11    pub name: String,
12    /// Expiry date of the instrument
13    pub expiry: String,
14    /// Size of one contract
15    #[serde(rename = "contractSize")]
16    pub contract_size: String,
17    /// Size of one lot
18    #[serde(rename = "lotSize")]
19    pub lot_size: Option<f64>,
20    /// Upper price limit for the instrument
21    #[serde(rename = "highLimitPrice")]
22    pub high_limit_price: Option<f64>,
23    /// Lower price limit for the instrument
24    #[serde(rename = "lowLimitPrice")]
25    pub low_limit_price: Option<f64>,
26    /// Margin factor for the instrument
27    #[serde(rename = "marginFactor")]
28    pub margin_factor: Option<f64>,
29    /// Unit for the margin factor
30    #[serde(rename = "marginFactorUnit")]
31    pub margin_factor_unit: Option<String>,
32    /// Available currencies for trading this instrument
33    pub currencies: Option<Vec<Currency>>,
34    #[serde(rename = "valueOfOnePip")]
35    /// Value of one pip for this instrument
36    pub value_of_one_pip: String,
37
38    /// Type of the instrument
39    #[serde(rename = "instrumentType")]
40    pub instrument_type: Option<InstrumentType>,
41
42    /// Expiry details including last dealing date
43    #[serde(rename = "expiryDetails")]
44    pub expiry_details: Option<ExpiryDetails>,
45
46    #[serde(rename = "slippageFactor")]
47    /// Slippage factor for the instrument
48    pub slippage_factor: Option<StepDistance>,
49
50    #[serde(rename = "limitedRiskPremium")]
51    /// Premium for limited risk trades
52    pub limited_risk_premium: Option<StepDistance>,
53    #[serde(rename = "newsCode")]
54    /// Code used for news related to this instrument
55    pub news_code: Option<String>,
56    #[serde(rename = "chartCode")]
57    /// Code used for charting this instrument
58    pub chart_code: Option<String>,
59}
60
61/// Model for an instrument's currency
62#[derive(Debug, Clone, Deserialize, PartialEq)]
63pub struct Currency {
64    /// Currency code (e.g., "USD", "EUR")
65    pub code: String,
66    /// Currency symbol (e.g., "$", "€")
67    pub symbol: Option<String>,
68    /// Base exchange rate for the currency
69    #[serde(rename = "baseExchangeRate")]
70    pub base_exchange_rate: Option<f64>,
71    /// Current exchange rate
72    #[serde(rename = "exchangeRate")]
73    pub exchange_rate: Option<f64>,
74    /// Whether this is the default currency for the instrument
75    #[serde(rename = "isDefault")]
76    pub is_default: Option<bool>,
77}
78
79/// Model for market data with enhanced deserialization
80#[derive(Debug, Clone, Deserialize)]
81pub struct MarketDetails {
82    /// Detailed information about the instrument
83    pub instrument: Instrument,
84    /// Current market snapshot with prices
85    pub snapshot: MarketSnapshot,
86    /// Trading rules for the market
87    #[serde(rename = "dealingRules")]
88    pub dealing_rules: DealingRules,
89}
90
91/// Trading rules for a market with enhanced deserialization
92#[derive(Debug, Clone, Deserialize)]
93pub struct DealingRules {
94    /// Minimum step distance
95    #[serde(rename = "minStepDistance")]
96    pub min_step_distance: StepDistance,
97
98    /// Minimum deal size allowed
99    #[serde(rename = "minDealSize")]
100    pub min_deal_size: StepDistance,
101
102    /// Minimum distance for controlled risk stop
103    #[serde(rename = "minControlledRiskStopDistance")]
104    pub min_controlled_risk_stop_distance: StepDistance,
105
106    /// Minimum distance for normal stop or limit orders
107    #[serde(rename = "minNormalStopOrLimitDistance")]
108    pub min_normal_stop_or_limit_distance: StepDistance,
109
110    /// Maximum distance for stop or limit orders
111    #[serde(rename = "maxStopOrLimitDistance")]
112    pub max_stop_or_limit_distance: StepDistance,
113
114    /// Controlled risk spacing
115    #[serde(rename = "controlledRiskSpacing")]
116    pub controlled_risk_spacing: StepDistance,
117
118    /// Market order preference setting
119    #[serde(rename = "marketOrderPreference")]
120    pub market_order_preference: String,
121
122    /// Trailing stops preference setting
123    #[serde(rename = "trailingStopsPreference")]
124    pub trailing_stops_preference: String,
125
126    #[serde(rename = "maxDealSize")]
127    /// Maximum deal size allowed
128    pub max_deal_size: Option<f64>,
129}
130
131/// Market snapshot with enhanced deserialization
132#[derive(Debug, Clone, Deserialize)]
133pub struct MarketSnapshot {
134    /// Current status of the market (e.g., "OPEN", "CLOSED")
135    #[serde(rename = "marketStatus")]
136    pub market_status: String,
137
138    /// Net change in price since previous close
139    #[serde(rename = "netChange")]
140    pub net_change: Option<f64>,
141
142    /// Percentage change in price since previous close
143    #[serde(rename = "percentageChange")]
144    pub percentage_change: Option<f64>,
145
146    /// Time of the last price update
147    #[serde(rename = "updateTime")]
148    pub update_time: Option<String>,
149
150    /// Delay time in milliseconds for market data
151    #[serde(rename = "delayTime")]
152    pub delay_time: Option<i64>,
153
154    /// Current bid price
155    pub bid: Option<f64>,
156
157    /// Current offer/ask price
158    pub offer: Option<f64>,
159
160    /// Highest price of the current trading session
161    pub high: Option<f64>,
162
163    /// Lowest price of the current trading session
164    pub low: Option<f64>,
165
166    /// Odds for binary markets
167    #[serde(rename = "binaryOdds")]
168    pub binary_odds: Option<f64>,
169
170    /// Factor for decimal places in price display
171    #[serde(rename = "decimalPlacesFactor")]
172    pub decimal_places_factor: Option<i64>,
173
174    /// Factor for scaling prices
175    #[serde(rename = "scalingFactor")]
176    pub scaling_factor: Option<i64>,
177
178    /// Extra spread for controlled risk trades
179    #[serde(rename = "controlledRiskExtraSpread")]
180    pub controlled_risk_extra_spread: Option<f64>,
181}
182
183/// Model for market search results
184#[derive(Debug, Clone, Deserialize)]
185pub struct MarketSearchResult {
186    /// List of markets matching the search criteria
187    pub markets: Vec<MarketData>,
188}
189
190/// Basic market data
191#[derive(Debug, Clone, Deserialize, Serialize)]
192pub struct MarketData {
193    /// Unique identifier for the market
194    pub epic: String,
195    /// Human-readable name of the instrument
196    #[serde(rename = "instrumentName")]
197    pub instrument_name: String,
198    /// Type of the instrument
199    #[serde(rename = "instrumentType")]
200    pub instrument_type: InstrumentType,
201    /// Expiry date of the instrument
202    pub expiry: String,
203    /// Upper price limit for the market
204    #[serde(rename = "highLimitPrice")]
205    pub high_limit_price: Option<f64>,
206    /// Lower price limit for the market
207    #[serde(rename = "lowLimitPrice")]
208    pub low_limit_price: Option<f64>,
209    /// Current status of the market
210    #[serde(rename = "marketStatus")]
211    pub market_status: String,
212    /// Net change in price since previous close
213    #[serde(rename = "netChange")]
214    pub net_change: Option<f64>,
215    /// Percentage change in price since previous close
216    #[serde(rename = "percentageChange")]
217    pub percentage_change: Option<f64>,
218    /// Time of the last price update
219    #[serde(rename = "updateTime")]
220    pub update_time: Option<String>,
221    /// Time of the last price update in UTC
222    #[serde(rename = "updateTimeUTC")]
223    pub update_time_utc: Option<String>,
224    /// Current bid price
225    pub bid: Option<f64>,
226    /// Current offer/ask price
227    pub offer: Option<f64>,
228}
229
230impl Display for MarketData {
231    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232        let json = serde_json::to_string(self).unwrap_or_else(|_| "Invalid JSON".to_string());
233        write!(f, "{}", json)
234    }
235}
236
237/// Model for historical prices
238#[derive(Debug, Clone, Deserialize)]
239pub struct HistoricalPricesResponse {
240    /// List of historical price points
241    pub prices: Vec<HistoricalPrice>,
242    /// Type of the instrument
243    #[serde(rename = "instrumentType")]
244    pub instrument_type: InstrumentType,
245    /// API usage allowance information
246    #[serde(rename = "allowance", skip_serializing_if = "Option::is_none", default)]
247    pub allowance: Option<PriceAllowance>,
248}
249
250/// Historical price data point
251#[derive(Debug, Clone, Deserialize)]
252pub struct HistoricalPrice {
253    /// Timestamp of the price data point
254    #[serde(rename = "snapshotTime")]
255    pub snapshot_time: String,
256    /// Opening price for the period
257    #[serde(rename = "openPrice")]
258    pub open_price: PricePoint,
259    /// Highest price for the period
260    #[serde(rename = "highPrice")]
261    pub high_price: PricePoint,
262    /// Lowest price for the period
263    #[serde(rename = "lowPrice")]
264    pub low_price: PricePoint,
265    /// Closing price for the period
266    #[serde(rename = "closePrice")]
267    pub close_price: PricePoint,
268    /// Volume traded during the period
269    #[serde(rename = "lastTradedVolume")]
270    pub last_traded_volume: Option<i64>,
271}
272
273/// Price point with bid, ask and last traded prices
274#[derive(Debug, Clone, Deserialize)]
275pub struct PricePoint {
276    /// Bid price at this point
277    pub bid: Option<f64>,
278    /// Ask/offer price at this point
279    pub ask: Option<f64>,
280    /// Last traded price at this point
281    #[serde(rename = "lastTraded")]
282    pub last_traded: Option<f64>,
283}
284
285/// Information about API usage allowance for price data
286#[derive(Debug, Clone, Deserialize)]
287pub struct PriceAllowance {
288    /// Remaining API calls allowed in the current period
289    #[serde(rename = "remainingAllowance")]
290    pub remaining_allowance: i64,
291    /// Total API calls allowed per period
292    #[serde(rename = "totalAllowance")]
293    pub total_allowance: i64,
294    /// Time until the allowance resets
295    #[serde(rename = "allowanceExpiry")]
296    pub allowance_expiry: i64,
297}
298
299/// Response model for market navigation
300#[derive(Debug, Clone, Deserialize, Serialize)]
301pub struct MarketNavigationResponse {
302    /// List of navigation nodes at the current level
303    #[serde(default, deserialize_with = "deserialize_null_as_empty_vec")]
304    pub nodes: Vec<MarketNavigationNode>,
305    /// List of markets at the current level
306    #[serde(default, deserialize_with = "deserialize_null_as_empty_vec")]
307    pub markets: Vec<MarketData>,
308}
309
310/// Details about instrument expiry
311#[derive(Debug, Clone, Deserialize, PartialEq)]
312pub struct ExpiryDetails {
313    /// The last dealing date and time for the instrument
314    #[serde(rename = "lastDealingDate")]
315    pub last_dealing_date: String,
316
317    /// Information about settlement
318    #[serde(rename = "settlementInfo")]
319    pub settlement_info: Option<String>,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
323/// Unit for step distances in trading rules
324pub enum StepUnit {
325    #[serde(rename = "POINTS")]
326    /// Points (price movement units)
327    Points,
328    #[serde(rename = "PERCENTAGE")]
329    /// Percentage value
330    Percentage,
331    #[serde(rename = "pct")]
332    /// Alternative representation for percentage
333    Pct,
334}
335
336/// A struct to handle the minStepDistance value which can be a complex object
337#[derive(Debug, Clone, Deserialize, PartialEq)]
338pub struct StepDistance {
339    /// Unit type for the distance
340    pub unit: Option<StepUnit>,
341    /// Numeric value of the distance
342    pub value: Option<f64>,
343}
344
345/// Helper function to deserialize null values as empty vectors
346#[allow(dead_code)]
347fn deserialize_null_as_empty_vec<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
348where
349    D: serde::Deserializer<'de>,
350    T: serde::Deserialize<'de>,
351{
352    let opt = Option::deserialize(deserializer)?;
353    Ok(opt.unwrap_or_default())
354}
355
356/// Node in the market navigation hierarchy
357#[derive(Debug, Clone, Deserialize, Serialize)]
358pub struct MarketNavigationNode {
359    /// Unique identifier for the node
360    pub id: String,
361    /// Display name of the node
362    pub name: String,
363}
364
365/// Estructura que representa un nodo en la jerarquía de mercados
366#[derive(Debug, Serialize, Deserialize)]
367pub struct MarketNode {
368    /// ID del nodo
369    pub id: String,
370    /// Nombre del nodo
371    pub name: String,
372    /// Nodos hijos
373    #[serde(skip_serializing_if = "Vec::is_empty")]
374    pub children: Vec<MarketNode>,
375    /// Mercados en este nodo
376    #[serde(skip_serializing_if = "Vec::is_empty")]
377    pub markets: Vec<MarketData>,
378}