1use crate::error::Error;
2use jqdata_derive::*;
3#[allow(unused_imports)]
4use serde::{Deserialize, Serialize};
5#[allow(unused_imports)]
6use serde_derive::*;
7use std::io::BufRead;
8use std::str::FromStr;
9
10pub trait Request {
14 fn request(&self, token: &str) -> Result<String, Error>;
16}
17
18pub trait Response {
22 type Output;
23 fn response(&self, response: reqwest::blocking::Response) -> Result<Self::Output, Error>;
25}
26
27#[allow(dead_code)]
29pub(crate) fn consume_csv<T>(response: &mut reqwest::blocking::Response) -> Result<Vec<T>, Error>
30where
31 for<'de> T: Deserialize<'de>,
32{
33 let mut reader = csv::ReaderBuilder::new()
34 .from_reader(response);
36 let header_cols: Vec<&str> = reader.headers()?.into_iter().collect();
38 if header_cols.is_empty() {
39 return Err(Error::Server("empty response body returned".to_owned()));
40 }
41 let first_col = header_cols.first().cloned().unwrap();
42 if first_col.starts_with("error") {
43 return Err(Error::Server(first_col.to_owned()));
44 }
45 let mut rs = Vec::new();
46 for r in reader.deserialize() {
47 let s: T = r?;
48 rs.push(s);
49 }
50 Ok(rs)
51}
52
53#[allow(dead_code)]
55pub(crate) fn consume_line(
56 response: &mut reqwest::blocking::Response,
57) -> Result<Vec<String>, Error> {
58 let reader = std::io::BufReader::new(response);
59 let mut rs = Vec::new();
60 for line in reader.lines() {
61 rs.push(line?);
62 }
63 Ok(rs)
64}
65
66pub(crate) fn consume_single<T>(response: &mut reqwest::blocking::Response) -> Result<T, Error>
68where
69 T: FromStr,
70 Error: From<T::Err>,
71{
72 let mut vec = Vec::new();
73 std::io::copy(response, &mut vec)?;
74 let s = String::from_utf8(vec)?;
75 let result = s.parse::<T>()?;
76 Ok(result)
77}
78
79#[allow(dead_code)]
81pub(crate) fn consume_json<T>(response: &mut reqwest::blocking::Response) -> Result<T, Error>
82where
83 for<'de> T: Deserialize<'de>,
84{
85 let result = serde_json::from_reader(response)?;
86 Ok(result)
87}
88
89#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
91#[serde(rename_all = "snake_case")]
92pub enum SecurityKind {
93 Stock,
94 Fund,
95 Index,
96 Futures,
97 #[serde(rename = "etf")]
98 ETF,
99 #[serde(rename = "lof")]
100 LOF,
101 #[serde(rename = "fja")]
102 FJA,
103 #[serde(rename = "fjb")]
104 FJB,
105 #[serde(rename = "QDII_fund")]
106 QDIIFund,
107 OpenFund,
108 BondFund,
109 StockFund,
110 MoneyMarketFund,
111 MixtureFund,
112 Options,
113}
114
115#[derive(Debug, Serialize, Deserialize, PartialEq)]
117pub struct Security {
118 pub code: String,
119 pub display_name: String,
120 pub name: String,
121 pub start_date: String,
122 pub end_date: String,
123 #[serde(rename = "type")]
124 pub kind: SecurityKind,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub parent: Option<String>,
127}
128
129#[derive(Debug, Serialize, Deserialize, Request, Response)]
133#[request(get_all_securities)]
134#[response(format = "csv", type = "Security")]
135pub struct GetAllSecurities {
136 pub code: SecurityKind,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub date: Option<String>,
139}
140
141#[derive(Debug, Serialize, Deserialize, Request, Response)]
143#[request(get_security_info)]
144#[response(format = "csv", type = "Security")]
145pub struct GetSecurityInfo {
146 pub code: String,
147}
148
149#[derive(Debug, Serialize, Deserialize, Request, Response)]
151#[request(get_index_stocks)]
152#[response(format = "line")]
153pub struct GetIndexStocks {
154 pub code: String,
155 pub date: String,
156}
157
158#[derive(Debug, Serialize, Deserialize, Request, Response)]
161#[request(get_margincash_stocks)]
162#[response(format = "line")]
163pub struct GetMargincashStocks {
164 #[serde(skip_serializing_if = "Option::is_none")]
165 pub date: Option<String>,
166}
167
168#[derive(Debug, Serialize, Deserialize, Request, Response)]
170#[request(get_locked_shares)]
171#[response(format = "csv", type = "LockedShare")]
172pub struct GetLockedShares {
173 pub code: String,
174 pub date: String,
175 pub end_date: String,
176}
177
178#[derive(Debug, Serialize, Deserialize)]
179pub struct LockedShare {
180 pub day: String,
181 pub code: String,
182 pub num: f64,
183 pub rate1: f64,
184 pub rate2: f64,
185}
186
187#[derive(Debug, Serialize, Deserialize, Request, Response)]
191#[request(get_index_weights)]
192#[response(format = "csv", type = "IndexWeight")]
193pub struct GetIndexWeights {
194 pub code: String,
195 pub date: String,
196}
197
198#[derive(Debug, Serialize, Deserialize)]
199pub struct IndexWeight {
200 pub code: String,
201 pub display_name: String,
202 pub date: String,
203 pub weight: f64,
204}
205
206#[derive(Debug, Serialize, Deserialize, Request, Response)]
215#[request(get_industries)]
216#[response(format = "csv", type = "IndustryIndex")]
217pub struct GetIndustries {
218 pub code: String,
219}
220
221#[derive(Debug, Serialize, Deserialize)]
222pub struct IndustryIndex {
223 pub index: String,
224 pub name: String,
225 pub start_date: String,
226}
227
228#[derive(Debug, Serialize, Deserialize, Request, Response)]
233#[request(get_industry)]
234#[response(format = "csv", type = "Industry")]
235pub struct GetIndustry {
236 pub code: String,
237 pub date: String,
238}
239
240#[derive(Debug, Serialize, Deserialize)]
241pub struct Industry {
242 pub industry: String,
243 pub industry_code: String,
244 pub industry_name: String,
245}
246
247#[derive(Debug, Serialize, Deserialize, Request, Response)]
252#[request(get_industry_stocks)]
253#[response(format = "line")]
254pub struct GetIndustryStocks {
255 pub code: String,
256 pub date: String,
257}
258
259#[derive(Debug, Serialize, Deserialize, Request, Response)]
264#[request(get_concepts)]
265#[response(format = "csv", type = "Concept")]
266pub struct GetConcepts {}
267
268#[derive(Debug, Serialize, Deserialize)]
269pub struct Concept {
270 pub code: String,
271 pub name: String,
272 pub start_date: String,
273}
274
275#[derive(Debug, Serialize, Deserialize, Request, Response)]
280#[request(get_concept_stocks)]
281#[response(format = "line")]
282pub struct GetConceptStocks {
283 pub code: String,
284 pub date: String,
285}
286
287#[derive(Debug, Serialize, Deserialize, Request, Response)]
292#[request(get_trade_days)]
293#[response(format = "line")]
294pub struct GetTradeDays {
295 pub date: String,
296 #[serde(skip_serializing_if = "Option::is_none")]
297 pub end_date: Option<String>,
298}
299
300#[derive(Debug, Serialize, Deserialize, Request, Response)]
302#[request(get_all_trade_days)]
303#[response(format = "line")]
304pub struct GetAllTradeDays {}
305
306#[derive(Debug, Serialize, Deserialize, Request, Response)]
322#[request(get_mtss)]
323#[response(format = "csv", type = "Mtss")]
324pub struct GetMtss {
325 pub code: String,
326 pub date: String,
327 pub end_date: String,
328}
329
330#[derive(Debug, Serialize, Deserialize)]
331pub struct Mtss {
332 pub date: String,
333 pub sec_code: String,
334 pub fin_value: f64,
335 pub fin_refund_value: f64,
336 pub sec_value: f64,
337 pub sec_sell_value: f64,
338 pub sec_refund_value: f64,
339 pub fin_sec_value: f64,
340}
341
342#[derive(Debug, Serialize, Deserialize, Request, Response)]
362#[request(get_money_flow)]
363#[response(format = "csv", type = "MoneyFlow")]
364pub struct GetMoneyFlow {
365 pub code: String,
366 pub date: String,
367 pub end_date: String,
368}
369
370#[derive(Debug, Serialize, Deserialize)]
371pub struct MoneyFlow {
372 pub date: String,
373 pub sec_code: String,
374 pub change_pct: f64,
375 pub net_amount_main: f64,
376 pub net_pct_main: f64,
377 pub net_amount_xl: f64,
378 pub net_pct_xl: f64,
379 pub net_amount_l: f64,
380 pub net_pct_l: f64,
381 pub net_amount_m: f64,
382 pub net_pct_m: f64,
383 pub net_amount_s: f64,
384 pub net_pct_s: f64,
385}
386
387#[derive(Debug, Serialize, Deserialize, Request, Response)]
407#[request(get_billboard_list)]
408#[response(format = "csv", type = "BillboardStock")]
409pub struct GetBillboardList {
410 pub code: String,
411 pub date: String,
412 pub end_date: String,
413}
414
415#[derive(Debug, Serialize, Deserialize)]
416pub struct BillboardStock {
417 pub code: String,
418 pub day: String,
419 pub direction: String,
420 pub rank: i32,
421 pub abnormal_code: String,
422 pub abnormal_name: String,
423 pub sales_depart_name: String,
424 pub buy_value: f64,
425 pub buy_rate: f64,
426 pub sell_value: f64,
427 pub sell_rate: f64,
428 pub total_value: f64,
429 pub net_value: f64,
430 pub amount: f64,
431}
432
433#[derive(Debug, Serialize, Deserialize, Request, Response)]
438#[request(get_future_contracts)]
439#[response(format = "line")]
440pub struct GetFutureContracts {
441 pub code: String,
442 pub date: String,
443}
444
445#[derive(Debug, Serialize, Deserialize, Request, Response)]
450#[request(get_dominant_future)]
451#[response(format = "line")]
452pub struct GetDominantFuture {
453 pub code: String,
454 pub date: String,
455}
456
457#[derive(Debug, Serialize, Deserialize, Request, Response)]
477#[request(get_fund_info)]
478#[response(format = "json", type = "FundInfo")]
479pub struct GetFundInfo {
480 pub code: String,
481 pub date: String,
482}
483
484#[derive(Debug, Serialize, Deserialize)]
485pub struct FundInfo {
486 pub fund_name: String,
487 pub fund_type: String,
488 pub fund_establishment_day: String,
489 pub fund_manager: String,
490 pub fund_management_fee: String,
491 pub fund_custodian_fee: String,
492 pub fund_status: String,
493 pub fund_size: String,
494 pub fund_share: f64,
495 pub fund_asset_allocation_proportion: String,
496 pub heavy_hold_stocks: Vec<String>,
497 pub heavy_hold_stocks_proportion: f64,
498 pub heavy_hold_bond: Vec<String>,
499 pub heavy_hold_bond_proportion: f64,
500}
501
502#[derive(Debug, Serialize, Deserialize, Request, Response)]
518#[request(get_current_tick)]
519#[response(format = "csv", type = "Tick")]
520pub struct GetCurrentTick {
521 pub code: String,
522}
523
524#[derive(Debug, Serialize, Deserialize)]
525pub struct Tick {
526 pub time: f64,
527 pub current: f64,
528 pub high: f64,
529 pub low: f64,
530 pub volumn: f64,
531 pub money: f64,
532 pub position: f64,
533 pub a1_v: f64,
534 pub a2_v: f64,
535 pub a3_v: f64,
536 pub a4_v: f64,
537 pub a5_v: f64,
538 pub a1_p: f64,
539 pub a2_p: f64,
540 pub a3_p: f64,
541 pub a4_p: f64,
542 pub a5_p: f64,
543 pub b1_v: f64,
544 pub b2_v: f64,
545 pub b3_v: f64,
546 pub b4_v: f64,
547 pub b5_v: f64,
548 pub b1_p: f64,
549 pub b2_p: f64,
550 pub b3_p: f64,
551 pub b4_p: f64,
552 pub b5_p: f64,
553}
554
555#[derive(Debug, Serialize, Deserialize, Request, Response)]
559#[request(get_current_ticks)]
560#[response(format = "csv", type = "Tick")]
561pub struct GetCurrentTicks {
562 pub code: String,
563}
564
565#[derive(Debug, Serialize, Deserialize, Request, Response)]
579#[request(get_extras)]
580#[response(format = "csv", type = "Extra")]
581pub struct GetExtras {
582 pub code: String,
583 pub date: String,
584 pub end_date: String,
585}
586
587#[derive(Debug, Serialize, Deserialize)]
588pub struct Extra {
589 pub date: String,
590 pub is_st: Option<i8>,
591 pub acc_net_value: Option<f64>,
592 pub unit_net_value: Option<f64>,
593 pub futures_sett_price: Option<f64>,
594 pub futures_positions: Option<f64>,
595 pub adj_net_value: Option<f64>,
596}
597
598#[derive(Debug, Serialize, Deserialize, Request, Response)]
622#[request(get_price)]
623#[response(format = "csv", type = "Price")]
624pub struct GetPrice {
625 pub date: String,
626 pub count: u32,
627 pub unit: String,
628 #[serde(skip_serializing_if = "Option::is_none")]
629 pub end_date: Option<String>,
630 #[serde(skip_serializing_if = "Option::is_none")]
631 pub fq_ref_date: Option<String>,
632}
633
634#[derive(Debug, Serialize, Deserialize)]
635pub struct Price {
636 pub date: String,
637 pub open: f64,
638 pub close: f64,
639 pub high: f64,
640 pub low: f64,
641 pub volume: f64,
642 pub money: f64,
643 pub paused: Option<u8>,
644 pub high_limit: Option<f64>,
645 pub low_limit: Option<f64>,
646 pub avg: Option<f64>,
647 pub pre_close: Option<f64>,
648 pub open_interest: Option<f64>,
649}
650
651#[derive(Debug, Serialize, Deserialize, Request, Response)]
675#[request(get_price_period)]
676#[response(format = "csv", type = "Price")]
677pub struct GetPricePeriod {
678 pub code: String,
679 pub unit: String,
680 pub date: String,
681 pub end_date: String,
682 #[serde(skip_serializing_if = "Option::is_none")]
683 pub fq_ref_date: Option<String>,
684}
685
686#[derive(Debug, Serialize, Deserialize, Request, Response)]
698#[request(get_ticks)]
699#[response(format = "csv", type = "Tick")]
700pub struct GetTicks {
701 pub code: String,
702 #[serde(skip_serializing_if = "Option::is_none")]
703 pub count: Option<u32>,
704 pub end_date: String,
705 pub skip: bool,
706}
707
708#[derive(Debug, Serialize, Deserialize, Request, Response)]
721#[request(get_ticks_period)]
722#[response(format = "csv", type = "Tick")]
723pub struct GetTicksPeriod {
724 pub code: String,
725 pub date: String,
726 pub end_date: String,
727 pub skip: bool,
728}
729
730#[derive(Debug, Serialize, Deserialize, Request, Response)]
744#[request(get_factor_values)]
745#[response(format = "csv", type = "FactorValue")]
746pub struct GetFactorValues {
747 pub code: String,
748 pub columns: String,
749 pub date: String,
750 pub end_date: String,
751}
752
753#[derive(Debug, Serialize, Deserialize)]
754pub struct FactorValue {
755 pub date: String,
756 pub cfo_to_ev: Option<f64>,
757 pub net_profit_ratio: Option<f64>,
758}
759
760#[derive(Debug, Serialize, Deserialize, Request, Response)]
770#[request(run_query)]
771#[response(format = "line")]
772pub struct RunQuery {
773 pub table: String,
774 pub columns: String,
775 #[serde(skip_serializing_if = "Option::is_none")]
776 pub conditions: Option<String>,
777 #[serde(skip_serializing_if = "Option::is_none")]
778 pub count: Option<u32>,
779}
780
781#[derive(Debug, Serialize, Deserialize, Request, Response)]
783#[request(run_query)]
784#[response(format = "single", type = "i32")]
785pub struct GetQueryCount {}
786
787#[cfg(test)]
788mod tests {
789 use super::*;
790 use serde_json::json;
791
792 #[test]
793 fn test_security_kind_rename() {
794 assert_serde_security_kind("stock", &SecurityKind::Stock);
795 assert_serde_security_kind("fund", &SecurityKind::Fund);
796 assert_serde_security_kind("index", &SecurityKind::Index);
797 assert_serde_security_kind("futures", &SecurityKind::Futures);
798 assert_serde_security_kind("etf", &SecurityKind::ETF);
799 assert_serde_security_kind("lof", &SecurityKind::LOF);
800 assert_serde_security_kind("fja", &SecurityKind::FJA);
801 assert_serde_security_kind("fjb", &SecurityKind::FJB);
802 assert_serde_security_kind("QDII_fund", &SecurityKind::QDIIFund);
803 assert_serde_security_kind("open_fund", &SecurityKind::OpenFund);
804 assert_serde_security_kind("bond_fund", &SecurityKind::BondFund);
805 assert_serde_security_kind("stock_fund", &SecurityKind::StockFund);
806 assert_serde_security_kind("money_market_fund", &SecurityKind::MoneyMarketFund);
807 assert_serde_security_kind("mixture_fund", &SecurityKind::MixtureFund);
808 assert_serde_security_kind("options", &SecurityKind::Options);
809 }
810
811 #[test]
812 fn test_get_all_securities() {
813 let gas = GetAllSecurities {
814 code: SecurityKind::Stock,
815 date: Some(String::from("2020-02-16")),
816 };
817 assert_eq!(
818 serde_json::to_string(&json!({
819 "method": "get_all_securities",
820 "token": "abc",
821 "code": "stock",
822 "date": "2020-02-16",
823 }))
824 .unwrap(),
825 gas.request("abc").unwrap()
826 );
827 }
828
829 fn assert_serde_security_kind(s: &str, k: &SecurityKind) {
830 let str_repr = serde_json::to_string(s).unwrap();
831 assert_eq!(str_repr, serde_json::to_string(k).unwrap());
832 assert_eq!(k, &serde_json::from_str::<SecurityKind>(&str_repr).unwrap());
833 }
834}