1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::BTreeMap;
4
5#[derive(Debug, Serialize, Deserialize, Clone, Default)]
7pub struct Paging {
8 pub index: Option<u32>,
9 pub matches: Option<u32>,
10}
11
12#[derive(Debug, Serialize, Deserialize, Clone, Default)]
14pub struct DepotsResponse {
15 pub paging: Option<Paging>,
16 #[serde(default)]
17 pub values: Vec<Depot>,
18}
19
20#[derive(Debug, Serialize, Deserialize, Clone, Default)]
22#[serde(rename_all = "camelCase")]
23pub struct Depot {
24 pub depot_id: Option<String>,
25 pub depot_display_id: Option<String>,
26 pub client_id: Option<String>,
27 pub depot_type: Option<String>,
28 pub default_settlement_account_id: Option<String>,
29 #[serde(default)]
30 pub settlement_account_ids: Vec<String>,
31 pub target_market: Option<String>,
32}
33
34#[derive(Debug, Serialize, Deserialize, Clone, Default)]
36pub struct DepotPositionsResponse {
37 pub paging: Option<Paging>,
38 pub aggregated: Option<DepotPositionsAggregated>,
39 #[serde(default)]
40 pub values: Vec<Position>,
41}
42
43#[derive(Debug, Serialize, Deserialize, Clone, Default)]
45#[serde(rename_all = "camelCase")]
46pub struct DepotPositionsAggregated {
47 pub depot: Option<Depot>,
48 pub prev_day_value: Option<Amount>,
49 pub current_value: Option<Amount>,
50 pub purchase_value: Option<Amount>,
51 pub profit_loss_purchase_abs: Option<Amount>,
52 pub profit_loss_purchase_rel: Option<String>,
53 pub profit_loss_prev_day_abs: Option<Amount>,
54 pub profit_loss_prev_day_rel: Option<String>,
55 #[serde(flatten)]
56 pub extra: BTreeMap<String, Value>,
57}
58
59#[derive(Debug, Serialize, Deserialize, Clone, Default)]
61#[serde(rename_all = "camelCase")]
62pub struct Position {
63 pub depot_id: Option<String>,
64 pub position_id: Option<String>,
65 pub wkn: Option<String>,
66 pub custody_type: Option<String>,
67 pub position_type: Option<String>,
68 pub has_purchase_price: Option<bool>,
69 pub quantity: Option<Amount>,
70 pub available_quantity: Option<Amount>,
71 pub current_price: Option<CurrentPrice>,
72 pub purchase_price: Option<Amount>,
73 pub prev_day_price: Option<CurrentPrice>,
74 pub current_value: Option<Amount>,
75 pub purchase_value: Option<Amount>,
76 pub purchase_value_editable: Option<bool>,
77 pub profit_loss_purchase_abs: Option<Amount>,
78 pub profit_loss_purchase_rel: Option<String>,
79 pub profit_loss_prev_day_abs: Option<Amount>,
80 pub profit_loss_prev_day_rel: Option<String>,
81 pub profit_loss_prev_day_total_abs: Option<Amount>,
82 pub version: Option<String>,
83 pub hedgeability: Option<String>,
84 pub available_quantity_to_hedge: Option<Amount>,
85 pub current_price_determinable: Option<bool>,
86 pub has_intra_day_executed_order: Option<bool>,
87 pub purchase_possible: Option<bool>,
88 pub sell_possible: Option<bool>,
89 #[serde(flatten)]
90 pub extra: BTreeMap<String, Value>,
91}
92
93#[derive(Debug, Serialize, Deserialize, Clone, Default)]
95#[serde(rename_all = "camelCase")]
96pub struct CurrentPrice {
97 pub price: Amount,
98 pub price_date_time: Option<String>,
99 pub venue: Option<Venue>,
100}
101
102#[derive(Debug, Serialize, Deserialize, Clone, Default)]
104pub struct Amount {
105 pub value: String,
106 pub unit: Option<String>,
107}
108
109#[derive(Debug, Serialize, Deserialize, Clone, Default)]
111pub struct Venue {
112 pub name: Option<String>,
113 pub venue_id: Option<String>,
114 pub country: Option<String>,
115 #[serde(rename = "type")]
116 pub venue_type: Option<String>,
117}
118
119#[derive(Debug, Serialize, Deserialize, Clone, Default)]
121pub struct InstrumentResponse {
122 pub paging: Option<Paging>,
123 #[serde(default)]
124 pub values: Vec<Instrument>,
125}
126
127#[derive(Debug, Serialize, Deserialize, Clone, Default)]
129#[serde(rename_all = "camelCase")]
130pub struct Instrument {
131 pub instrument_id: Option<String>,
132 pub wkn: Option<String>,
133 pub isin: Option<String>,
134 pub mnemonic: Option<String>,
135 pub name: Option<String>,
136 pub short_name: Option<String>,
137 pub issuer: Option<String>,
138 pub static_data: Option<InstrumentStaticData>,
139}
140
141#[derive(Debug, Serialize, Deserialize, Clone, Default)]
143#[serde(rename_all = "camelCase")]
144pub struct InstrumentStaticData {
145 pub notation: Option<String>,
146 pub currency: Option<String>,
147 pub settlement_currency: Option<String>,
148 pub instrument_type: Option<String>,
149 pub priips_relevant: Option<bool>,
150 pub kid_available: Option<bool>,
151 pub shipping_waiver_required: Option<bool>,
152 pub fund_redemption_limited: Option<bool>,
153 pub savings_plan_eligibility: Option<String>,
154}
155
156#[cfg(test)]
157mod tests {
158 use super::{DepotPositionsResponse, DepotsResponse, InstrumentResponse};
159
160 #[test]
161 fn depots_deserializes_real_shape() {
162 let raw = r#"{"paging":{"index":0,"matches":1},"values":[{"depotId":"d1","depotDisplayId":"299","clientId":"c1"}]}"#;
163 let parsed: DepotsResponse = serde_json::from_str(raw).unwrap();
164 assert_eq!(parsed.values.len(), 1);
165 assert_eq!(parsed.values[0].depot_id.as_deref(), Some("d1"));
166 }
167
168 #[test]
169 fn positions_deserializes_aggregated_and_venue() {
170 let raw = r#"{"paging":{"index":0,"matches":1},"aggregated":{"currentValue":{"value":"642.450","unit":"EUR"}},"values":[{"depotId":"d1","positionId":"p1","wkn":"A1JKSU","quantity":{"value":"1.567","unit":"XXX"},"currentPrice":{"price":{"value":"50.6","unit":"EUR"},"priceDateTime":"2026-03-06T17:35:49+01","venue":{"name":"Xetra","venueId":"v1","country":"DE","type":"EXCHANGE"}}}]}"#;
171 let parsed: DepotPositionsResponse = serde_json::from_str(raw).unwrap();
172 assert_eq!(parsed.values.len(), 1);
173 assert_eq!(
174 parsed.values[0]
175 .current_price
176 .as_ref()
177 .and_then(|p| p.venue.as_ref())
178 .and_then(|v| v.venue_type.as_deref()),
179 Some("EXCHANGE")
180 );
181 }
182
183 #[test]
184 fn instrument_deserializes_static_data() {
185 let raw = r#"{"paging":{"index":0,"matches":1},"values":[{"instrumentId":"i1","wkn":"A1JKSU","isin":"IE00B6YX5M31","name":"ETF","staticData":{"instrumentType":"ETF","kidAvailable":true}}]}"#;
186 let parsed: InstrumentResponse = serde_json::from_str(raw).unwrap();
187 assert_eq!(parsed.values.len(), 1);
188 assert_eq!(parsed.values[0].wkn.as_deref(), Some("A1JKSU"));
189 }
190}