jquants_api_client/api/
weekly_margin_trading_outstandings.rs

1//! Margin Trading Outstandings API.
2
3use serde::{Deserialize, Serialize};
4
5use super::{
6    shared::{
7        traits::{
8            builder::JQuantsBuilder,
9            pagination::{HasPaginationKey, MergePage, Paginatable},
10        },
11        types::issue_type::IssueType,
12    },
13    JQuantsApiClient, JQuantsPlanClient,
14};
15
16/// Builder for Margin Trading Outstandings API.
17#[derive(Clone, Serialize)]
18pub struct WeeklyMarginTradingOutstandingsBuilder {
19    #[serde(skip)]
20    client: JQuantsApiClient,
21
22    /// Issue code (e.g. 27800 or 2780)
23    #[serde(skip_serializing_if = "Option::is_none")]
24    code: Option<String>,
25    /// Date of data (e.g. 20210907 or 2021-09-07)
26    #[serde(skip_serializing_if = "Option::is_none")]
27    date: Option<String>,
28    /// Starting point of data period (e.g. 20210901 or 2021-09-01)
29    #[serde(skip_serializing_if = "Option::is_none")]
30    from: Option<String>,
31    /// End point of data period (e.g. 20210907 or 2021-09-07)
32    #[serde(skip_serializing_if = "Option::is_none")]
33    to: Option<String>,
34
35    /// Pagination key.
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pagination_key: Option<String>,
38}
39
40impl JQuantsBuilder<WeeklyMarginTradingOutstandingsResponse>
41    for WeeklyMarginTradingOutstandingsBuilder
42{
43    async fn send(self) -> Result<WeeklyMarginTradingOutstandingsResponse, crate::JQuantsError> {
44        self.send_ref().await
45    }
46
47    async fn send_ref(
48        &self,
49    ) -> Result<WeeklyMarginTradingOutstandingsResponse, crate::JQuantsError> {
50        self.client
51            .inner
52            .get("markets/weekly_margin_interest", self)
53            .await
54    }
55}
56
57impl Paginatable<WeeklyMarginTradingOutstandingsResponse>
58    for WeeklyMarginTradingOutstandingsBuilder
59{
60    fn pagination_key(mut self, pagination_key: impl Into<String>) -> Self {
61        self.pagination_key = Some(pagination_key.into());
62        self
63    }
64}
65
66impl WeeklyMarginTradingOutstandingsBuilder {
67    /// Create a new builder.
68    pub(crate) fn new(client: JQuantsApiClient) -> Self {
69        Self {
70            client,
71            code: None,
72            date: None,
73            from: None,
74            to: None,
75            pagination_key: None,
76        }
77    }
78
79    /// Set issue code (e.g. 27800 or 2780)
80    pub fn code(mut self, code: impl Into<String>) -> Self {
81        self.code = Some(code.into());
82        self
83    }
84
85    /// Set date of data (e.g. 20210907 or 2021-09-07)
86    pub fn date(mut self, date: impl Into<String>) -> Self {
87        self.date = Some(date.into());
88        self
89    }
90
91    /// Set starting point of data period (e.g. 20210901 or 2021-09-01)
92    pub fn from(mut self, from: impl Into<String>) -> Self {
93        self.from = Some(from.into());
94        self
95    }
96
97    /// Set end point of data period (e.g. 20210907 or 2021-09-07)
98    pub fn to(mut self, to: impl Into<String>) -> Self {
99        self.to = Some(to.into());
100        self
101    }
102}
103
104/// Builder for Margin Trading Outstandings API.
105pub trait WeeklyMarginTradingOutstandingsApi: JQuantsPlanClient {
106    /// Get API builder for Margin Trading Outstandings.
107    ///
108    /// Use [Margin Trading Outstandings (/markets/weekly_margin_interest) API](https://jpx.gitbook.io/j-quants-en/api-reference/weekly_margin_interest)
109    fn get_weekly_margin_trading_outstandings(&self) -> WeeklyMarginTradingOutstandingsBuilder {
110        WeeklyMarginTradingOutstandingsBuilder::new(self.get_api_client().clone())
111    }
112}
113
114/// Margin Trading Outstandings response.
115///
116/// See: [API Reference](https://jpx.gitbook.io/j-quants-en/api-reference/weekly_margin_interest)
117#[derive(Debug, Clone, PartialEq, Deserialize)]
118pub struct WeeklyMarginTradingOutstandingsResponse {
119    /// List of weekly margin trading outstanding
120    pub weekly_margin_interest: Vec<WeeklyMarginTradingOutstandingItem>,
121    /// Pagination key for fetching next set of data
122    pub pagination_key: Option<String>,
123}
124
125impl HasPaginationKey for WeeklyMarginTradingOutstandingsResponse {
126    fn get_pagination_key(&self) -> Option<&str> {
127        self.pagination_key.as_deref()
128    }
129}
130
131impl MergePage for WeeklyMarginTradingOutstandingsResponse {
132    fn merge_page(
133        page: Result<Vec<Self>, crate::JQuantsError>,
134    ) -> Result<Self, crate::JQuantsError> {
135        let mut page = page?;
136        let mut merged = page.pop().unwrap();
137        for p in page {
138            merged
139                .weekly_margin_interest
140                .extend(p.weekly_margin_interest);
141        }
142        merged.pagination_key = None;
143
144        Ok(merged)
145    }
146}
147
148/// Represents a single weekly margin trading outstanding.
149#[derive(Debug, Clone, PartialEq, Deserialize)]
150pub struct WeeklyMarginTradingOutstandingItem {
151    /// Record Date (YYYY-MM-DD)
152    #[serde(rename = "Date")]
153    pub date: String,
154
155    /// Issue code
156    #[serde(rename = "Code")]
157    pub code: String,
158
159    /// Total margin trading (negotiable and standardized) weekend short positions
160    #[serde(rename = "ShortMarginTradeVolume")]
161    pub short_margin_trade_volume: f64,
162
163    /// Total margin trading (negotiable and standardized) weekend long positions
164    #[serde(rename = "LongMarginTradeVolume")]
165    pub long_margin_trade_volume: f64,
166
167    /// Negotiable margin trading weekend short positions
168    #[serde(rename = "ShortNegotiableMarginTradeVolume")]
169    pub short_negotiable_margin_trade_volume: f64,
170
171    /// Negotiable margin trading weekend long positions
172    #[serde(rename = "LongNegotiableMarginTradeVolume")]
173    pub long_negotiable_margin_trade_volume: f64,
174
175    /// Standardized margin trading weekend short positions
176    #[serde(rename = "ShortStandardizedMarginTradeVolume")]
177    pub short_standardized_margin_trade_volume: f64,
178
179    /// Standardized margin trading weekend long positions
180    #[serde(rename = "LongStandardizedMarginTradeVolume")]
181    pub long_standardized_margin_trade_volume: f64,
182
183    /// Issue Classifications
184    #[serde(rename = "IssueType")]
185    pub issue_type: IssueType,
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191
192    #[test]
193    fn test_deserialize_weekly_margin_trading_outstandings_response() {
194        let json = r#"
195            {
196                "weekly_margin_interest": [
197                    {
198                        "Date": "2023-02-17",
199                        "Code": "13010",
200                        "ShortMarginTradeVolume": 4100.0,
201                        "LongMarginTradeVolume": 27600.0,
202                        "ShortNegotiableMarginTradeVolume": 1300.0,
203                        "LongNegotiableMarginTradeVolume": 7600.0,
204                        "ShortStandardizedMarginTradeVolume": 2800.0,
205                        "LongStandardizedMarginTradeVolume": 20000.0,
206                        "IssueType": "2"
207                    }
208                ],
209                "pagination_key": "value1.value2."
210            }
211        "#;
212
213        let response: WeeklyMarginTradingOutstandingsResponse = serde_json::from_str(json).unwrap();
214        let expected_response = WeeklyMarginTradingOutstandingsResponse {
215            weekly_margin_interest: vec![WeeklyMarginTradingOutstandingItem {
216                date: "2023-02-17".to_string(),
217                code: "13010".to_string(),
218                short_margin_trade_volume: 4100.0,
219                long_margin_trade_volume: 27600.0,
220                short_negotiable_margin_trade_volume: 1300.0,
221                long_negotiable_margin_trade_volume: 7600.0,
222                short_standardized_margin_trade_volume: 2800.0,
223                long_standardized_margin_trade_volume: 20000.0,
224                issue_type: IssueType::Loan, // Assuming "2" corresponds to Loan
225            }],
226            pagination_key: Some("value1.value2.".to_string()),
227        };
228
229        pretty_assertions::assert_eq!(response, expected_response);
230    }
231
232    #[test]
233    fn test_deserialize_weekly_margin_trading_outstandings_response_no_pagination_key() {
234        let json = r#"
235            {
236                "weekly_margin_interest": [
237                    {
238                        "Date": "2023-02-17",
239                        "Code": "13010",
240                        "ShortMarginTradeVolume": 4100.0,
241                        "LongMarginTradeVolume": 27600.0,
242                        "ShortNegotiableMarginTradeVolume": 1300.0,
243                        "LongNegotiableMarginTradeVolume": 7600.0,
244                        "ShortStandardizedMarginTradeVolume": 2800.0,
245                        "LongStandardizedMarginTradeVolume": 20000.0,
246                        "IssueType": "2"
247                    }
248                ]
249            }
250        "#;
251
252        let response: WeeklyMarginTradingOutstandingsResponse = serde_json::from_str(json).unwrap();
253        let expected_response = WeeklyMarginTradingOutstandingsResponse {
254            weekly_margin_interest: vec![WeeklyMarginTradingOutstandingItem {
255                date: "2023-02-17".to_string(),
256                code: "13010".to_string(),
257                short_margin_trade_volume: 4100.0,
258                long_margin_trade_volume: 27600.0,
259                short_negotiable_margin_trade_volume: 1300.0,
260                long_negotiable_margin_trade_volume: 7600.0,
261                short_standardized_margin_trade_volume: 2800.0,
262                long_standardized_margin_trade_volume: 20000.0,
263                issue_type: IssueType::Loan, // Assuming "2" corresponds to Loan
264            }],
265            pagination_key: None,
266        };
267
268        pretty_assertions::assert_eq!(response, expected_response);
269    }
270
271    #[test]
272    fn test_deserialize_weekly_margin_trading_outstandings_response_multiple_items() {
273        let json = r#"
274            {
275                "weekly_margin_interest": [
276                    {
277                        "Date": "2023-02-10",
278                        "Code": "13010",
279                        "ShortMarginTradeVolume": 4000.0,
280                        "LongMarginTradeVolume": 27000.0,
281                        "ShortNegotiableMarginTradeVolume": 1200.0,
282                        "LongNegotiableMarginTradeVolume": 7500.0,
283                        "ShortStandardizedMarginTradeVolume": 2800.0,
284                        "LongStandardizedMarginTradeVolume": 19500.0,
285                        "IssueType": "2"
286                    },
287                    {
288                        "Date": "2023-02-17",
289                        "Code": "13010",
290                        "ShortMarginTradeVolume": 4100.0,
291                        "LongMarginTradeVolume": 27600.0,
292                        "ShortNegotiableMarginTradeVolume": 1300.0,
293                        "LongNegotiableMarginTradeVolume": 7600.0,
294                        "ShortStandardizedMarginTradeVolume": 2800.0,
295                        "LongStandardizedMarginTradeVolume": 20000.0,
296                        "IssueType": "2"
297                    }
298                ],
299                "pagination_key": "value1.value2."
300            }
301        "#;
302
303        let response: WeeklyMarginTradingOutstandingsResponse = serde_json::from_str(json).unwrap();
304        let expected_response = WeeklyMarginTradingOutstandingsResponse {
305            weekly_margin_interest: vec![
306                WeeklyMarginTradingOutstandingItem {
307                    date: "2023-02-10".to_string(),
308                    code: "13010".to_string(),
309                    short_margin_trade_volume: 4000.0,
310                    long_margin_trade_volume: 27000.0,
311                    short_negotiable_margin_trade_volume: 1200.0,
312                    long_negotiable_margin_trade_volume: 7500.0,
313                    short_standardized_margin_trade_volume: 2800.0,
314                    long_standardized_margin_trade_volume: 19500.0,
315                    issue_type: IssueType::Loan, // Assuming "2" corresponds to Loan
316                },
317                WeeklyMarginTradingOutstandingItem {
318                    date: "2023-02-17".to_string(),
319                    code: "13010".to_string(),
320                    short_margin_trade_volume: 4100.0,
321                    long_margin_trade_volume: 27600.0,
322                    short_negotiable_margin_trade_volume: 1300.0,
323                    long_negotiable_margin_trade_volume: 7600.0,
324                    short_standardized_margin_trade_volume: 2800.0,
325                    long_standardized_margin_trade_volume: 20000.0,
326                    issue_type: IssueType::Loan, // Assuming "2" corresponds to Loan
327                },
328            ],
329            pagination_key: Some("value1.value2.".to_string()),
330        };
331
332        pretty_assertions::assert_eq!(response, expected_response);
333    }
334
335    #[test]
336    fn test_deserialize_weekly_margin_trading_outstandings_response_no_data() {
337        let json = r#"
338            {
339                "weekly_margin_interest": []
340            }
341        "#;
342
343        let response: WeeklyMarginTradingOutstandingsResponse = serde_json::from_str(json).unwrap();
344        let expected_response = WeeklyMarginTradingOutstandingsResponse {
345            weekly_margin_interest: vec![],
346            pagination_key: None,
347        };
348
349        pretty_assertions::assert_eq!(response, expected_response);
350    }
351}