jquants_api_client/api/
breakdown_trading_data.rs

1//! Breakdown Trading Data API.
2
3use serde::{Deserialize, Serialize};
4
5use super::{
6    shared::traits::{
7        builder::JQuantsBuilder,
8        pagination::{HasPaginationKey, MergePage, Paginatable},
9    },
10    JQuantsApiClient, JQuantsPlanClient,
11};
12
13/// Builder for Breakdown Trading Data API.
14#[derive(Clone, Serialize)]
15pub struct BreakdownTradingDataBuilder {
16    #[serde(skip)]
17    client: JQuantsApiClient,
18
19    /// Issue code (e.g. "27890" or "2789")
20    #[serde(skip_serializing_if = "Option::is_none")]
21    code: Option<String>,
22    /// Starting point of data period (e.g. "20210901" or "2021-09-01")
23    #[serde(skip_serializing_if = "Option::is_none")]
24    from: Option<String>,
25    /// End point of data period (e.g. "20210907" or "2021-09-07")
26    #[serde(skip_serializing_if = "Option::is_none")]
27    to: Option<String>,
28    /// Date of data (e.g. "20210907" or "2021-09-07")
29    #[serde(skip_serializing_if = "Option::is_none")]
30    date: Option<String>,
31
32    /// Pagination key.
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pagination_key: Option<String>,
35}
36
37impl JQuantsBuilder<BreakdownTradingDataResponse> for BreakdownTradingDataBuilder {
38    async fn send(self) -> Result<BreakdownTradingDataResponse, crate::JQuantsError> {
39        self.send_ref().await
40    }
41
42    async fn send_ref(&self) -> Result<BreakdownTradingDataResponse, crate::JQuantsError> {
43        self.client.inner.get("markets/breakdown", self).await
44    }
45}
46
47impl Paginatable<BreakdownTradingDataResponse> for BreakdownTradingDataBuilder {
48    fn pagination_key(mut self, pagination_key: impl Into<String>) -> Self {
49        self.pagination_key = Some(pagination_key.into());
50        self
51    }
52}
53
54impl BreakdownTradingDataBuilder {
55    /// Create a new builder.
56    pub(crate) fn new(client: JQuantsApiClient) -> Self {
57        Self {
58            client,
59            code: None,
60            from: None,
61            to: None,
62            date: None,
63            pagination_key: None,
64        }
65    }
66
67    /// Set issue code (e.g. "27890" or "2789")
68    pub fn code(mut self, code: impl Into<String>) -> Self {
69        self.code = Some(code.into());
70        self
71    }
72
73    /// Set starting point of data period (e.g. "20210901" or "2021-09-01")
74    pub fn from(mut self, from: impl Into<String>) -> Self {
75        self.from = Some(from.into());
76        self
77    }
78
79    /// Set end point of data period (e.g. "20210907" or "2021-09-07")
80    pub fn to(mut self, to: impl Into<String>) -> Self {
81        self.to = Some(to.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
92/// Builder for Breakdown Trading Data API.
93pub trait BreakdownTradingDataApi: JQuantsPlanClient {
94    /// Get API builder for Breakdown Trading Data.
95    ///
96    /// Use [Breakdown Trading Data (/markets/breakdown) API](https://jpx.gitbook.io/j-quants-en/api-reference/breakdown)
97    fn get_breakdown_trading_data(&self) -> BreakdownTradingDataBuilder {
98        BreakdownTradingDataBuilder::new(self.get_api_client().clone())
99    }
100}
101
102/// Breakdown Trading Data response.
103///
104/// See: [API Reference](https://jpx.gitbook.io/j-quants-en/api-reference/breakdown)
105#[derive(Debug, Clone, PartialEq, Deserialize)]
106pub struct BreakdownTradingDataResponse {
107    /// List of breakdown trading data
108    pub breakdown: Vec<BreakdownTradingDataItem>,
109    /// Pagination key for fetching next set of data
110    pub pagination_key: Option<String>,
111}
112
113impl HasPaginationKey for BreakdownTradingDataResponse {
114    fn get_pagination_key(&self) -> Option<&str> {
115        self.pagination_key.as_deref()
116    }
117}
118
119impl MergePage for BreakdownTradingDataResponse {
120    fn merge_page(
121        page: Result<Vec<Self>, crate::JQuantsError>,
122    ) -> Result<Self, crate::JQuantsError> {
123        let mut page = page?;
124        let mut merged = page.pop().unwrap();
125        for p in page {
126            merged.breakdown.extend(p.breakdown);
127        }
128        merged.pagination_key = None;
129
130        Ok(merged)
131    }
132}
133
134/// Represents a single breakdown trading data item.
135#[derive(Debug, Clone, PartialEq, Deserialize)]
136pub struct BreakdownTradingDataItem {
137    /// Trade date (YYYY-MM-DD)
138    #[serde(rename = "Date")]
139    pub date: String,
140
141    /// Issue code
142    #[serde(rename = "Code")]
143    pub code: String,
144
145    /// Long selling trading value
146    #[serde(rename = "LongSellValue")]
147    pub long_sell_value: f64,
148
149    /// Trading value of short selling (excluding new margin sell)
150    #[serde(rename = "ShortSellWithoutMarginValue")]
151    pub short_sell_without_margin_value: f64,
152
153    /// Trading value of new margin selling (sell orders that create new margin sell positions)
154    #[serde(rename = "MarginSellNewValue")]
155    pub margin_sell_new_value: f64,
156
157    /// Trading value of closing margin selling (sell orders that close existing margin buy positions)
158    #[serde(rename = "MarginSellCloseValue")]
159    pub margin_sell_close_value: f64,
160
161    /// Long buying trading value
162    #[serde(rename = "LongBuyValue")]
163    pub long_buy_value: f64,
164
165    /// Trading value of new margin buying (buy orders that create new margin buy positions)
166    #[serde(rename = "MarginBuyNewValue")]
167    pub margin_buy_new_value: f64,
168
169    /// Trading value of closing margin buying (buy orders that close existing margin sell positions)
170    #[serde(rename = "MarginBuyCloseValue")]
171    pub margin_buy_close_value: f64,
172
173    /// Long selling trading volume
174    #[serde(rename = "LongSellVolume")]
175    pub long_sell_volume: f64,
176
177    /// Trading volume of short selling (excluding new margin selling)
178    #[serde(rename = "ShortSellWithoutMarginVolume")]
179    pub short_sell_without_margin_volume: f64,
180
181    /// Trading volume of new margin selling (sell orders that create new margin sell positions)
182    #[serde(rename = "MarginSellNewVolume")]
183    pub margin_sell_new_volume: f64,
184
185    /// Trading volume of closing margin selling (sell orders that close existing margin buy positions)
186    #[serde(rename = "MarginSellCloseVolume")]
187    pub margin_sell_close_volume: f64,
188
189    /// Long buying trading volume
190    #[serde(rename = "LongBuyVolume")]
191    pub long_buy_volume: f64,
192
193    /// Trading volume of new margin buying (buy orders that create new margin buy positions)
194    #[serde(rename = "MarginBuyNewVolume")]
195    pub margin_buy_new_volume: f64,
196
197    /// Trading volume of closing margin buying (buy orders that close existing margin sell positions)
198    #[serde(rename = "MarginBuyCloseVolume")]
199    pub margin_buy_close_volume: f64,
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    #[test]
207    fn test_deserialize_breakdown_trading_data_response() {
208        let json = r#"
209            {
210                "breakdown": [
211                    {
212                        "Date": "2015-04-01", 
213                        "Code": "13010", 
214                        "LongSellValue": 115164000.0,
215                        "ShortSellWithoutMarginValue": 93561000.0, 
216                        "MarginSellNewValue": 6412000.0, 
217                        "MarginSellCloseValue": 23009000.0, 
218                        "LongBuyValue": 185114000.0, 
219                        "MarginBuyNewValue": 35568000.0, 
220                        "MarginBuyCloseValue": 17464000.0, 
221                        "LongSellVolume": 415000.0, 
222                        "ShortSellWithoutMarginVolume": 337000.0, 
223                        "MarginSellNewVolume": 23000.0, 
224                        "MarginSellCloseVolume": 83000.0, 
225                        "LongBuyVolume": 667000.0, 
226                        "MarginBuyNewVolume": 128000.0, 
227                        "MarginBuyCloseVolume": 63000.0
228                    }
229                ],
230                "pagination_key": "value1.value2."
231            }
232        "#;
233
234        let response: BreakdownTradingDataResponse = serde_json::from_str(json).unwrap();
235        let expected_response = BreakdownTradingDataResponse {
236            breakdown: vec![BreakdownTradingDataItem {
237                date: "2015-04-01".to_string(),
238                code: "13010".to_string(),
239                long_sell_value: 115164000.0,
240                short_sell_without_margin_value: 93561000.0,
241                margin_sell_new_value: 6412000.0,
242                margin_sell_close_value: 23009000.0,
243                long_buy_value: 185114000.0,
244                margin_buy_new_value: 35568000.0,
245                margin_buy_close_value: 17464000.0,
246                long_sell_volume: 415000.0,
247                short_sell_without_margin_volume: 337000.0,
248                margin_sell_new_volume: 23000.0,
249                margin_sell_close_volume: 83000.0,
250                long_buy_volume: 667000.0,
251                margin_buy_new_volume: 128000.0,
252                margin_buy_close_volume: 63000.0,
253            }],
254            pagination_key: Some("value1.value2.".to_string()),
255        };
256
257        pretty_assertions::assert_eq!(response, expected_response);
258    }
259
260    #[test]
261    fn test_deserialize_breakdown_trading_data_response_no_pagination_key() {
262        let json = r#"
263            {
264                "breakdown": [
265                    {
266                        "Date": "2015-04-01", 
267                        "Code": "13010", 
268                        "LongSellValue": 115164000.0,
269                        "ShortSellWithoutMarginValue": 93561000.0, 
270                        "MarginSellNewValue": 6412000.0, 
271                        "MarginSellCloseValue": 23009000.0, 
272                        "LongBuyValue": 185114000.0, 
273                        "MarginBuyNewValue": 35568000.0, 
274                        "MarginBuyCloseValue": 17464000.0, 
275                        "LongSellVolume": 415000.0, 
276                        "ShortSellWithoutMarginVolume": 337000.0, 
277                        "MarginSellNewVolume": 23000.0, 
278                        "MarginSellCloseVolume": 83000.0, 
279                        "LongBuyVolume": 667000.0, 
280                        "MarginBuyNewVolume": 128000.0, 
281                        "MarginBuyCloseVolume": 63000.0
282                    }
283                ]
284            }
285        "#;
286
287        let response: BreakdownTradingDataResponse = serde_json::from_str(json).unwrap();
288        let expected_response = BreakdownTradingDataResponse {
289            breakdown: vec![BreakdownTradingDataItem {
290                date: "2015-04-01".to_string(),
291                code: "13010".to_string(),
292                long_sell_value: 115164000.0,
293                short_sell_without_margin_value: 93561000.0,
294                margin_sell_new_value: 6412000.0,
295                margin_sell_close_value: 23009000.0,
296                long_buy_value: 185114000.0,
297                margin_buy_new_value: 35568000.0,
298                margin_buy_close_value: 17464000.0,
299                long_sell_volume: 415000.0,
300                short_sell_without_margin_volume: 337000.0,
301                margin_sell_new_volume: 23000.0,
302                margin_sell_close_volume: 83000.0,
303                long_buy_volume: 667000.0,
304                margin_buy_new_volume: 128000.0,
305                margin_buy_close_volume: 63000.0,
306            }],
307            pagination_key: None,
308        };
309
310        pretty_assertions::assert_eq!(response, expected_response);
311    }
312
313    #[test]
314    fn test_deserialize_breakdown_trading_data_response_multiple_items() {
315        let json = r#"
316            {
317                "breakdown": [
318                    {
319                        "Date": "2015-03-25",
320                        "Code": "13010",
321                        "LongSellValue": 110000000.0,
322                        "ShortSellWithoutMarginValue": 90000000.0,
323                        "MarginSellNewValue": 6000000.0,
324                        "MarginSellCloseValue": 22000000.0,
325                        "LongBuyValue": 180000000.0,
326                        "MarginBuyNewValue": 35000000.0,
327                        "MarginBuyCloseValue": 17000000.0,
328                        "LongSellVolume": 400000.0,
329                        "ShortSellWithoutMarginVolume": 330000.0,
330                        "MarginSellNewVolume": 22000.0,
331                        "MarginSellCloseVolume": 82000.0,
332                        "LongBuyVolume": 660000.0,
333                        "MarginBuyNewVolume": 125000.0,
334                        "MarginBuyCloseVolume": 62000.0
335                    },
336                    {
337                        "Date": "2015-04-01", 
338                        "Code": "13010", 
339                        "LongSellValue": 115164000.0,
340                        "ShortSellWithoutMarginValue": 93561000.0, 
341                        "MarginSellNewValue": 6412000.0, 
342                        "MarginSellCloseValue": 23009000.0, 
343                        "LongBuyValue": 185114000.0, 
344                        "MarginBuyNewValue": 35568000.0, 
345                        "MarginBuyCloseValue": 17464000.0, 
346                        "LongSellVolume": 415000.0, 
347                        "ShortSellWithoutMarginVolume": 337000.0, 
348                        "MarginSellNewVolume": 23000.0, 
349                        "MarginSellCloseVolume": 83000.0, 
350                        "LongBuyVolume": 667000.0, 
351                        "MarginBuyNewVolume": 128000.0, 
352                        "MarginBuyCloseVolume": 63000.0
353                    }
354                ],
355                "pagination_key": "value1.value2."
356            }
357        "#;
358
359        let response: BreakdownTradingDataResponse = serde_json::from_str(json).unwrap();
360        let expected_response = BreakdownTradingDataResponse {
361            breakdown: vec![
362                BreakdownTradingDataItem {
363                    date: "2015-03-25".to_string(),
364                    code: "13010".to_string(),
365                    long_sell_value: 110000000.0,
366                    short_sell_without_margin_value: 90000000.0,
367                    margin_sell_new_value: 6000000.0,
368                    margin_sell_close_value: 22000000.0,
369                    long_buy_value: 180000000.0,
370                    margin_buy_new_value: 35000000.0,
371                    margin_buy_close_value: 17000000.0,
372                    long_sell_volume: 400000.0,
373                    short_sell_without_margin_volume: 330000.0,
374                    margin_sell_new_volume: 22000.0,
375                    margin_sell_close_volume: 82000.0,
376                    long_buy_volume: 660000.0,
377                    margin_buy_new_volume: 125000.0,
378                    margin_buy_close_volume: 62000.0,
379                },
380                BreakdownTradingDataItem {
381                    date: "2015-04-01".to_string(),
382                    code: "13010".to_string(),
383                    long_sell_value: 115164000.0,
384                    short_sell_without_margin_value: 93561000.0,
385                    margin_sell_new_value: 6412000.0,
386                    margin_sell_close_value: 23009000.0,
387                    long_buy_value: 185114000.0,
388                    margin_buy_new_value: 35568000.0,
389                    margin_buy_close_value: 17464000.0,
390                    long_sell_volume: 415000.0,
391                    short_sell_without_margin_volume: 337000.0,
392                    margin_sell_new_volume: 23000.0,
393                    margin_sell_close_volume: 83000.0,
394                    long_buy_volume: 667000.0,
395                    margin_buy_new_volume: 128000.0,
396                    margin_buy_close_volume: 63000.0,
397                },
398            ],
399            pagination_key: Some("value1.value2.".to_string()),
400        };
401
402        pretty_assertions::assert_eq!(response, expected_response);
403    }
404
405    #[test]
406    fn test_deserialize_breakdown_trading_data_response_no_data() {
407        let json = r#"
408            {
409                "breakdown": []
410            }
411        "#;
412
413        let response: BreakdownTradingDataResponse = serde_json::from_str(json).unwrap();
414        let expected_response = BreakdownTradingDataResponse {
415            breakdown: vec![],
416            pagination_key: None,
417        };
418
419        pretty_assertions::assert_eq!(response, expected_response);
420    }
421}