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