Skip to main content

oanda_rs/models/
position.rs

1//! Position models.
2
3use serde::{Deserialize, Serialize};
4
5use super::macros::string_enum;
6use super::{
7    AccountFinancingMode, AccountUnits, DecimalNumber, HomeConversionFactors, InstrumentName,
8    OpenTradeFinancing, PriceValue, TradeId,
9};
10
11/// The specification of a Position within an Account.
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13#[non_exhaustive]
14pub struct Position {
15    /// The Position's Instrument.
16    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
17    pub instrument: Option<InstrumentName>,
18
19    /// Profit/loss realized by the Position over the lifetime of the Account.
20    #[serde(rename = "pl", skip_serializing_if = "Option::is_none")]
21    pub pl: Option<AccountUnits>,
22
23    /// The unrealized profit/loss of all open Trades that contribute to this
24    /// Position.
25    #[serde(rename = "unrealizedPL", skip_serializing_if = "Option::is_none")]
26    pub unrealized_pl: Option<AccountUnits>,
27
28    /// Margin currently used by the Position.
29    #[serde(rename = "marginUsed", skip_serializing_if = "Option::is_none")]
30    pub margin_used: Option<AccountUnits>,
31
32    /// Profit/loss realized by the Position since the Account's resettablePL
33    /// was last reset by the client.
34    #[serde(rename = "resettablePL", skip_serializing_if = "Option::is_none")]
35    pub resettable_pl: Option<AccountUnits>,
36
37    /// The total amount of financing paid/collected for this instrument over
38    /// the lifetime of the Account.
39    #[serde(rename = "financing", skip_serializing_if = "Option::is_none")]
40    pub financing: Option<AccountUnits>,
41
42    /// The total amount of commission paid for this instrument over the
43    /// lifetime of the Account.
44    #[serde(rename = "commission", skip_serializing_if = "Option::is_none")]
45    pub commission: Option<AccountUnits>,
46
47    /// The total amount of fees charged over the lifetime of the Account for
48    /// the execution of guaranteed Stop Loss Orders for this instrument.
49    #[serde(
50        rename = "guaranteedExecutionFees",
51        skip_serializing_if = "Option::is_none"
52    )]
53    pub guaranteed_execution_fees: Option<AccountUnits>,
54
55    /// The `long` field.
56    #[serde(rename = "long", skip_serializing_if = "Option::is_none")]
57    pub long: Option<PositionSide>,
58
59    /// The `short` field.
60    #[serde(rename = "short", skip_serializing_if = "Option::is_none")]
61    pub short: Option<PositionSide>,
62
63    /// The total amount of dividend adjustment paid. This field is returned by
64    /// the live v20 API but is not present in OANDA's official documentation.
65    #[serde(rename = "dividendAdjustment", skip_serializing_if = "Option::is_none")]
66    pub dividend_adjustment: Option<DecimalNumber>,
67
68    /// The unrealized profit/loss inclusive of unsettled amounts. This field is
69    /// returned by the live v20 API but is not present in OANDA's official
70    /// documentation.
71    #[serde(rename = "trueUnrealizedPL", skip_serializing_if = "Option::is_none")]
72    pub true_unrealized_pl: Option<DecimalNumber>,
73}
74
75/// The representation of a Position for a single direction (long or short).
76#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
77#[non_exhaustive]
78pub struct PositionSide {
79    /// Number of units in the position (negative value indicates short
80    /// position, positive indicates long position).
81    #[serde(rename = "units", skip_serializing_if = "Option::is_none")]
82    pub units: Option<DecimalNumber>,
83
84    /// Volume-weighted average of the underlying Trade open prices for the
85    /// Position.
86    #[serde(rename = "averagePrice", skip_serializing_if = "Option::is_none")]
87    pub average_price: Option<PriceValue>,
88
89    /// List of the open Trade IDs which contribute to the open Position.
90    #[serde(rename = "tradeIDs", default, skip_serializing_if = "Vec::is_empty")]
91    pub trade_ids: Vec<TradeId>,
92
93    /// Profit/loss realized by the PositionSide over the lifetime of the
94    /// Account.
95    #[serde(rename = "pl", skip_serializing_if = "Option::is_none")]
96    pub pl: Option<AccountUnits>,
97
98    /// The unrealized profit/loss of all open Trades that contribute to this
99    /// PositionSide.
100    #[serde(rename = "unrealizedPL", skip_serializing_if = "Option::is_none")]
101    pub unrealized_pl: Option<AccountUnits>,
102
103    /// Profit/loss realized by the PositionSide since the Account's
104    /// resettablePL was last reset by the client.
105    #[serde(rename = "resettablePL", skip_serializing_if = "Option::is_none")]
106    pub resettable_pl: Option<AccountUnits>,
107
108    /// The total amount of financing paid/collected for this PositionSide over
109    /// the lifetime of the Account.
110    #[serde(rename = "financing", skip_serializing_if = "Option::is_none")]
111    pub financing: Option<AccountUnits>,
112
113    /// The total amount of fees charged over the lifetime of the Account for
114    /// the execution of guaranteed Stop Loss Orders attached to Trades for this
115    /// PositionSide.
116    #[serde(
117        rename = "guaranteedExecutionFees",
118        skip_serializing_if = "Option::is_none"
119    )]
120    pub guaranteed_execution_fees: Option<AccountUnits>,
121
122    /// The total amount of dividend adjustment paid. This field is returned by
123    /// the live v20 API but is not present in OANDA's official documentation.
124    #[serde(rename = "dividendAdjustment", skip_serializing_if = "Option::is_none")]
125    pub dividend_adjustment: Option<DecimalNumber>,
126
127    /// The unrealized profit/loss inclusive of unsettled amounts. This field is
128    /// returned by the live v20 API but is not present in OANDA's official
129    /// documentation.
130    #[serde(rename = "trueUnrealizedPL", skip_serializing_if = "Option::is_none")]
131    pub true_unrealized_pl: Option<DecimalNumber>,
132}
133
134/// The dynamic (calculated) state of a Position
135#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
136#[non_exhaustive]
137pub struct CalculatedPositionState {
138    /// The Position's Instrument.
139    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
140    pub instrument: Option<InstrumentName>,
141
142    /// The Position's net unrealized profit/loss
143    #[serde(rename = "netUnrealizedPL", skip_serializing_if = "Option::is_none")]
144    pub net_unrealized_pl: Option<AccountUnits>,
145
146    /// The unrealized profit/loss of the Position's long open Trades
147    #[serde(rename = "longUnrealizedPL", skip_serializing_if = "Option::is_none")]
148    pub long_unrealized_pl: Option<AccountUnits>,
149
150    /// The unrealized profit/loss of the Position's short open Trades
151    #[serde(rename = "shortUnrealizedPL", skip_serializing_if = "Option::is_none")]
152    pub short_unrealized_pl: Option<AccountUnits>,
153
154    /// Margin currently used by the Position.
155    #[serde(rename = "marginUsed", skip_serializing_if = "Option::is_none")]
156    pub margin_used: Option<AccountUnits>,
157}
158
159string_enum! {
160    /// The way that position values for an Account are calculated and
161    /// aggregated.
162    pub enum PositionAggregationMode {
163        AbsoluteSum => "ABSOLUTE_SUM",
164        MaximalSide => "MAXIMAL_SIDE",
165        NetSum => "NET_SUM",
166    }
167}
168
169/// OpenTradeFinancing is used to pay/collect daily financing charge for a
170/// Position within an Account
171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
172#[non_exhaustive]
173pub struct PositionFinancing {
174    /// The instrument of the Position that financing is being paid/collected
175    /// for.
176    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
177    pub instrument: Option<InstrumentName>,
178
179    /// The amount of financing paid/collected for the Position.
180    #[serde(rename = "financing", skip_serializing_if = "Option::is_none")]
181    pub financing: Option<AccountUnits>,
182
183    /// The financing paid/collecte for each open Trade within the Position.
184    #[serde(
185        rename = "openTradeFinancings",
186        default,
187        skip_serializing_if = "Vec::is_empty"
188    )]
189    pub open_trade_financings: Vec<OpenTradeFinancing>,
190
191    /// The amount of financing paid/collected in the Instrument's base
192    /// currency. This field is returned by the live v20 API but is not present
193    /// in OANDA's official documentation.
194    #[serde(rename = "baseFinancing", skip_serializing_if = "Option::is_none")]
195    pub base_financing: Option<DecimalNumber>,
196
197    /// The `accountFinancingMode` field.
198    #[serde(
199        rename = "accountFinancingMode",
200        skip_serializing_if = "Option::is_none"
201    )]
202    pub account_financing_mode: Option<AccountFinancingMode>,
203
204    /// The `homeConversionFactors` field.
205    #[serde(
206        rename = "homeConversionFactors",
207        skip_serializing_if = "Option::is_none"
208    )]
209    pub home_conversion_factors: Option<HomeConversionFactors>,
210
211    /// The total cost of currency conversions for the Position's financing, in
212    /// the Account's home currency. This field is returned by the live v20 API
213    /// but is not present in OANDA's official documentation.
214    #[serde(rename = "homeConversionCost", skip_serializing_if = "Option::is_none")]
215    pub home_conversion_cost: Option<DecimalNumber>,
216
217    /// The cost of converting the base financing to the Account's home
218    /// currency. This field is returned by the live v20 API but is not present
219    /// in OANDA's official documentation.
220    #[serde(
221        rename = "baseHomeConversionCost",
222        skip_serializing_if = "Option::is_none"
223    )]
224    pub base_home_conversion_cost: Option<DecimalNumber>,
225}
226
227/// Used to pay or collect a dividend adjustment amount for an open Trade within
228/// the Account.
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230#[non_exhaustive]
231pub struct OpenTradeDividendAdjustment {
232    /// The ID of the Trade for which the dividend adjustment is to be paid or
233    /// collected.
234    #[serde(rename = "tradeID", skip_serializing_if = "Option::is_none")]
235    pub trade_id: Option<String>,
236
237    /// The dividend adjustment amount to pay or collect for the Trade, in the
238    /// Account's home currency.
239    #[serde(rename = "dividendAdjustment", skip_serializing_if = "Option::is_none")]
240    pub dividend_adjustment: Option<DecimalNumber>,
241
242    /// The dividend adjustment amount to pay or collect for the Trade, in the
243    /// Instrument's quote currency.
244    #[serde(
245        rename = "quoteDividendAdjustment",
246        skip_serializing_if = "Option::is_none"
247    )]
248    pub quote_dividend_adjustment: Option<DecimalNumber>,
249
250    /// The cost of converting the dividend adjustment to the Account's home
251    /// currency. This field is returned by the live v20 API but is not present
252    /// in OANDA's official documentation.
253    #[serde(rename = "homeConversionCost", skip_serializing_if = "Option::is_none")]
254    pub home_conversion_cost: Option<DecimalNumber>,
255}