Skip to main content

oanda_client/
pricing.rs

1//! OANDA pricing endpoint contracts.
2// Shared imports vary by capability; unused ones are deliberately allowed.
3#[allow(unused_imports)]
4use crate::Timestamp;
5#[allow(unused_imports)]
6use crate::ids::*;
7#[allow(unused_imports)]
8use crate::models::*;
9#[allow(unused_imports)]
10use crate::{Client, Result};
11#[allow(unused_imports)]
12use reqwest::Method;
13#[allow(unused_imports)]
14use rust_decimal::Decimal;
15#[allow(unused_imports)]
16use serde::{Deserialize, Serialize};
17
18/// Query parameters for `latest_candles`.
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20#[serde(rename_all = "camelCase")]
21pub struct LatestCandlesQuery {
22    /// List of candle specifications to get pricing for. \[required\]
23    #[serde(rename = "candleSpecifications")]
24    pub candle_specifications: Vec<CandleSpecification>,
25    /// The number of units used to calculate the volume-weighted average bid and ask prices in the returned
26    /// candles. \[default=1\]
27    #[serde(rename = "units", default, skip_serializing_if = "Option::is_none")]
28    #[serde(deserialize_with = "crate::decimal_wire::optional_number_or_string")]
29    pub units: Option<Decimal>,
30    /// A flag that controls whether the candlestick is “smoothed” or not. A smoothed candlestick uses the
31    /// previous candle’s close price as its open price, while an unsmoothed candlestick uses the first price
32    /// from its time range as its open price. \[default=False\]
33    #[serde(rename = "smooth", default, skip_serializing_if = "Option::is_none")]
34    pub smooth: Option<bool>,
35    /// The hour of the day (in the specified timezone) to use for granularities that have daily alignments.
36    /// \[default=17, minimum=0, maximum=23\]
37    #[serde(
38        rename = "dailyAlignment",
39        default,
40        skip_serializing_if = "Option::is_none"
41    )]
42    pub daily_alignment: Option<i64>,
43    /// The timezone to use for the dailyAlignment parameter. Candlesticks with daily alignment will be aligned
44    /// to the dailyAlignment hour within the alignmentTimezone. Note that the returned times will still be
45    /// represented in UTC. \[default=America/New_York\]
46    #[serde(
47        rename = "alignmentTimezone",
48        default,
49        skip_serializing_if = "Option::is_none"
50    )]
51    pub alignment_timezone: Option<String>,
52    /// The day of the week used for granularities that have weekly alignment. \[default=Friday\]
53    #[serde(
54        rename = "weeklyAlignment",
55        default,
56        skip_serializing_if = "Option::is_none"
57    )]
58    pub weekly_alignment: Option<WeeklyAlignment>,
59}
60
61/// Successful response for `latest_candles`.
62#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
63#[serde(rename_all = "camelCase")]
64pub struct LatestCandlesResponse {
65    /// The latest candle sticks.
66    #[serde(
67        rename = "latestCandles",
68        default,
69        skip_serializing_if = "Option::is_none"
70    )]
71    pub latest_candles: Option<Vec<CandlestickResponse>>,
72}
73
74impl Client {
75    /// Latest candles.
76    pub async fn latest_candles(
77        &self,
78        account_id: &AccountID,
79        query: &LatestCandlesQuery,
80    ) -> std::result::Result<
81        crate::ApiResponse<LatestCandlesResponse>,
82        crate::OperationError<crate::GenericRejection>,
83    > {
84        let account_id_encoded = crate::client::segment(account_id.as_str());
85        let path = format!("/v3/accounts/{account_id_encoded}/candles/latest");
86        self.execute(Method::GET, &path, Some(query), None::<&()>, None, None)
87            .await
88    }
89}
90
91/// Query parameters for `pricing`.
92#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
93#[serde(rename_all = "camelCase")]
94pub struct PricingQuery {
95    /// List of Instruments to get pricing for. \[required\]
96    #[serde(rename = "instruments")]
97    pub instruments: Vec<InstrumentName>,
98    /// Date/Time filter to apply to the response. Only prices and home conversions (if requested) with a time
99    /// later than this filter (i.e. the price has changed after the since time) will be provided, and are
100    /// filtered independently.
101    #[serde(rename = "since", default, skip_serializing_if = "Option::is_none")]
102    pub since: Option<Timestamp>,
103    /// Flag that enables the inclusion of the unitsAvailable field in the returned Price objects.
104    /// \[default=True\] Deprecated: Will be removed in a future API update.
105    #[serde(
106        rename = "includeUnitsAvailable",
107        default,
108        skip_serializing_if = "Option::is_none"
109    )]
110    pub include_units_available: Option<bool>,
111    /// Flag that enables the inclusion of the homeConversions field in the returned response. An entry will be
112    /// returned for each currency in the set of all base and quote currencies present in the requested
113    /// instruments list. \[default=False\]
114    #[serde(
115        rename = "includeHomeConversions",
116        default,
117        skip_serializing_if = "Option::is_none"
118    )]
119    pub include_home_conversions: Option<bool>,
120}
121
122/// Successful response for `pricing`.
123#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
124#[serde(rename_all = "camelCase")]
125pub struct PricingResponse {
126    /// The list of Price objects requested.
127    #[serde(rename = "prices")]
128    pub prices: Vec<ClientPrice>,
129    /// The list of home currency conversion factors requested. This field will only be present if
130    /// includeHomeConversions was set to true in the request.
131    #[serde(
132        rename = "homeConversions",
133        default,
134        skip_serializing_if = "Option::is_none"
135    )]
136    pub home_conversions: Option<Vec<HomeConversions>>,
137    /// The DateTime value to use for the “since” parameter in the next poll request.
138    #[serde(rename = "time", default, skip_serializing_if = "Option::is_none")]
139    pub time: Option<Timestamp>,
140}
141
142impl Client {
143    /// Pricing.
144    pub async fn pricing(
145        &self,
146        account_id: &AccountID,
147        query: &PricingQuery,
148    ) -> std::result::Result<
149        crate::ApiResponse<PricingResponse>,
150        crate::OperationError<crate::GenericRejection>,
151    > {
152        let account_id_encoded = crate::client::segment(account_id.as_str());
153        let path = format!("/v3/accounts/{account_id_encoded}/pricing");
154        self.execute(Method::GET, &path, Some(query), None::<&()>, None, None)
155            .await
156    }
157}
158
159/// stream_pricing stream query parameters.
160#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
161#[serde(rename_all = "camelCase")]
162pub struct StreamPricingQuery {
163    /// List of Instruments to stream Prices for. \[required\]
164    #[serde(rename = "instruments")]
165    pub instruments: Vec<InstrumentName>,
166    /// Flag that enables/disables the sending of a pricing snapshot when initially connecting to the stream.
167    /// \[default=True\]
168    #[serde(rename = "snapshot", default, skip_serializing_if = "Option::is_none")]
169    pub snapshot: Option<bool>,
170    /// Flag that enables the inclusion of the homeConversions field in the returned response. An entry will be
171    /// returned for each currency in the set of all base and quote currencies present in the requested
172    /// instruments list. \[default=False\]
173    #[serde(
174        rename = "includeHomeConversions",
175        default,
176        skip_serializing_if = "Option::is_none"
177    )]
178    pub include_home_conversions: Option<bool>,
179}
180
181/// Query parameters for `instrument_candles`.
182#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
183#[serde(rename_all = "camelCase")]
184pub struct InstrumentCandlesQuery {
185    /// The Price component(s) to get candlestick data for. \[default=M\]
186    #[serde(rename = "price", default, skip_serializing_if = "Option::is_none")]
187    pub price: Option<PricingComponent>,
188    /// The granularity of the candlesticks to fetch \[default=S5\]
189    #[serde(
190        rename = "granularity",
191        default,
192        skip_serializing_if = "Option::is_none"
193    )]
194    pub granularity: Option<CandlestickGranularity>,
195    /// The number of candlesticks to return in the response. Count should not be specified if both the start
196    /// and end parameters are provided, as the time range combined with the granularity will determine the
197    /// number of candlesticks to return. \[default=500, maximum=5000\]
198    #[serde(rename = "count", default, skip_serializing_if = "Option::is_none")]
199    pub count: Option<i64>,
200    /// The start of the time range to fetch candlesticks for.
201    #[serde(rename = "from", default, skip_serializing_if = "Option::is_none")]
202    pub from: Option<Timestamp>,
203    /// The end of the time range to fetch candlesticks for.
204    #[serde(rename = "to", default, skip_serializing_if = "Option::is_none")]
205    pub to: Option<Timestamp>,
206    /// A flag that controls whether the candlestick is “smoothed” or not. A smoothed candlestick uses the
207    /// previous candle’s close price as its open price, while an unsmoothed candlestick uses the first price
208    /// from its time range as its open price. \[default=False\]
209    #[serde(rename = "smooth", default, skip_serializing_if = "Option::is_none")]
210    pub smooth: Option<bool>,
211    /// A flag that controls whether the candlestick that is covered by the from time should be included in the
212    /// results. This flag enables clients to use the timestamp of the last completed candlestick received to
213    /// poll for future candlesticks but avoid receiving the previous candlestick repeatedly. \[default=True\]
214    #[serde(
215        rename = "includeFirst",
216        default,
217        skip_serializing_if = "Option::is_none"
218    )]
219    pub include_first: Option<bool>,
220    /// The hour of the day (in the specified timezone) to use for granularities that have daily alignments.
221    /// \[default=17, minimum=0, maximum=23\]
222    #[serde(
223        rename = "dailyAlignment",
224        default,
225        skip_serializing_if = "Option::is_none"
226    )]
227    pub daily_alignment: Option<i64>,
228    /// The timezone to use for the dailyAlignment parameter. Candlesticks with daily alignment will be aligned
229    /// to the dailyAlignment hour within the alignmentTimezone. Note that the returned times will still be
230    /// represented in UTC. \[default=America/New_York\]
231    #[serde(
232        rename = "alignmentTimezone",
233        default,
234        skip_serializing_if = "Option::is_none"
235    )]
236    pub alignment_timezone: Option<String>,
237    /// The day of the week used for granularities that have weekly alignment. \[default=Friday\]
238    #[serde(
239        rename = "weeklyAlignment",
240        default,
241        skip_serializing_if = "Option::is_none"
242    )]
243    pub weekly_alignment: Option<WeeklyAlignment>,
244    /// The number of units used to calculate the volume-weighted average bid and ask prices in the returned
245    /// candles. \[default=1\]
246    #[serde(rename = "units", default, skip_serializing_if = "Option::is_none")]
247    #[serde(deserialize_with = "crate::decimal_wire::optional_number_or_string")]
248    pub units: Option<Decimal>,
249}
250
251/// Successful response for `instrument_candles`.
252#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
253#[serde(rename_all = "camelCase")]
254pub struct InstrumentCandlesResponse {
255    /// The instrument whose Prices are represented by the candlesticks.
256    #[serde(
257        rename = "instrument",
258        default,
259        skip_serializing_if = "Option::is_none"
260    )]
261    pub instrument: Option<InstrumentName>,
262    /// The granularity of the candlesticks provided.
263    #[serde(
264        rename = "granularity",
265        default,
266        skip_serializing_if = "Option::is_none"
267    )]
268    pub granularity: Option<CandlestickGranularity>,
269    /// The list of candlesticks that satisfy the request.
270    #[serde(rename = "candles", default, skip_serializing_if = "Option::is_none")]
271    pub candles: Option<Vec<Candlestick>>,
272}
273
274impl Client {
275    /// Instrument candles.
276    pub async fn instrument_candles(
277        &self,
278        account_id: &AccountID,
279        instrument: &InstrumentName,
280        query: Option<&InstrumentCandlesQuery>,
281    ) -> std::result::Result<
282        crate::ApiResponse<InstrumentCandlesResponse>,
283        crate::OperationError<crate::GenericRejection>,
284    > {
285        let account_id_encoded = crate::client::segment(account_id.as_str());
286        let instrument_encoded = crate::client::segment(instrument.as_str());
287        let path =
288            format!("/v3/accounts/{account_id_encoded}/instruments/{instrument_encoded}/candles");
289        self.execute(Method::GET, &path, query, None::<&()>, None, None)
290            .await
291    }
292}