use crate::error::Error;
use jqdata_derive::*;
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
#[allow(unused_imports)]
use serde_derive::*;
use std::io::BufRead;
use std::str::FromStr;
pub trait Request {
fn request(&self, token: &str) -> Result<String, Error>;
}
pub trait Response {
type Output;
fn response(&self, response: reqwest::blocking::Response) -> Result<Self::Output, Error>;
}
#[allow(dead_code)]
pub(crate) fn consume_csv<T>(response: &mut reqwest::blocking::Response) -> Result<Vec<T>, Error>
where
for<'de> T: Deserialize<'de>,
{
let mut reader = csv::ReaderBuilder::new()
.from_reader(response);
let header_cols: Vec<&str> = reader.headers()?.into_iter().collect();
if header_cols.is_empty() {
return Err(Error::Server("empty response body returned".to_owned()));
}
let first_col = header_cols.first().cloned().unwrap();
if first_col.starts_with("error") {
return Err(Error::Server(first_col.to_owned()));
}
let mut rs = Vec::new();
for r in reader.deserialize() {
let s: T = r?;
rs.push(s);
}
Ok(rs)
}
#[allow(dead_code)]
pub(crate) fn consume_line(
response: &mut reqwest::blocking::Response,
) -> Result<Vec<String>, Error> {
let reader = std::io::BufReader::new(response);
let mut rs = Vec::new();
for line in reader.lines() {
rs.push(line?);
}
Ok(rs)
}
pub(crate) fn consume_single<T>(response: &mut reqwest::blocking::Response) -> Result<T, Error>
where
T: FromStr,
Error: From<T::Err>,
{
let mut vec = Vec::new();
std::io::copy(response, &mut vec)?;
let s = String::from_utf8(vec)?;
let result = s.parse::<T>()?;
Ok(result)
}
#[allow(dead_code)]
pub(crate) fn consume_json<T>(response: &mut reqwest::blocking::Response) -> Result<T, Error>
where
for<'de> T: Deserialize<'de>,
{
let result = serde_json::from_reader(response)?;
Ok(result)
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SecurityKind {
Stock,
Fund,
Index,
Futures,
#[serde(rename = "etf")]
ETF,
#[serde(rename = "lof")]
LOF,
#[serde(rename = "fja")]
FJA,
#[serde(rename = "fjb")]
FJB,
#[serde(rename = "QDII_fund")]
QDIIFund,
OpenFund,
BondFund,
StockFund,
MoneyMarketFund,
MixtureFund,
Options,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Security {
pub code: String,
pub display_name: String,
pub name: String,
pub start_date: String,
pub end_date: String,
#[serde(rename = "type")]
pub kind: SecurityKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_all_securities)]
#[response(format = "csv", type = "Security")]
pub struct GetAllSecurities {
pub code: SecurityKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub date: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_security_info)]
#[response(format = "csv", type = "Security")]
pub struct GetSecurityInfo {
pub code: String,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_index_stocks)]
#[response(format = "line")]
pub struct GetIndexStocks {
pub code: String,
pub date: String,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_margincash_stocks)]
#[response(format = "line")]
pub struct GetMargincashStocks {
#[serde(skip_serializing_if = "Option::is_none")]
pub date: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_locked_shares)]
#[response(format = "csv", type = "LockedShare")]
pub struct GetLockedShares {
pub code: String,
pub date: String,
pub end_date: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct LockedShare {
pub day: String,
pub code: String,
pub num: f64,
pub rate1: f64,
pub rate2: f64,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_index_weights)]
#[response(format = "csv", type = "IndexWeight")]
pub struct GetIndexWeights {
pub code: String,
pub date: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct IndexWeight {
pub code: String,
pub display_name: String,
pub date: String,
pub weight: f64,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_industries)]
#[response(format = "csv", type = "IndustryIndex")]
pub struct GetIndustries {
pub code: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct IndustryIndex {
pub index: String,
pub name: String,
pub start_date: String,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_industry)]
#[response(format = "csv", type = "Industry")]
pub struct GetIndustry {
pub code: String,
pub date: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Industry {
pub industry: String,
pub industry_code: String,
pub industry_name: String,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_industry_stocks)]
#[response(format = "line")]
pub struct GetIndustryStocks {
pub code: String,
pub date: String,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_concepts)]
#[response(format = "csv", type = "Concept")]
pub struct GetConcepts {}
#[derive(Debug, Serialize, Deserialize)]
pub struct Concept {
pub code: String,
pub name: String,
pub start_date: String,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_concept_stocks)]
#[response(format = "line")]
pub struct GetConceptStocks {
pub code: String,
pub date: String,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_trade_days)]
#[response(format = "line")]
pub struct GetTradeDays {
pub date: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_all_trade_days)]
#[response(format = "line")]
pub struct GetAllTradeDays {}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_mtss)]
#[response(format = "csv", type = "Mtss")]
pub struct GetMtss {
pub code: String,
pub date: String,
pub end_date: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Mtss {
pub date: String,
pub sec_code: String,
pub fin_value: f64,
pub fin_refund_value: f64,
pub sec_value: f64,
pub sec_sell_value: f64,
pub sec_refund_value: f64,
pub fin_sec_value: f64,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_money_flow)]
#[response(format = "csv", type = "MoneyFlow")]
pub struct GetMoneyFlow {
pub code: String,
pub date: String,
pub end_date: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MoneyFlow {
pub date: String,
pub sec_code: String,
pub change_pct: f64,
pub net_amount_main: f64,
pub net_pct_main: f64,
pub net_amount_xl: f64,
pub net_pct_xl: f64,
pub net_amount_l: f64,
pub net_pct_l: f64,
pub net_amount_m: f64,
pub net_pct_m: f64,
pub net_amount_s: f64,
pub net_pct_s: f64,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_billboard_list)]
#[response(format = "csv", type = "BillboardStock")]
pub struct GetBillboardList {
pub code: String,
pub date: String,
pub end_date: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct BillboardStock {
pub code: String,
pub day: String,
pub direction: String,
pub rank: i32,
pub abnormal_code: String,
pub abnormal_name: String,
pub sales_depart_name: String,
pub buy_value: f64,
pub buy_rate: f64,
pub sell_value: f64,
pub sell_rate: f64,
pub total_value: f64,
pub net_value: f64,
pub amount: f64,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_future_contracts)]
#[response(format = "line")]
pub struct GetFutureContracts {
pub code: String,
pub date: String,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_dominant_future)]
#[response(format = "line")]
pub struct GetDominantFuture {
pub code: String,
pub date: String,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_fund_info)]
#[response(format = "json", type = "FundInfo")]
pub struct GetFundInfo {
pub code: String,
pub date: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct FundInfo {
pub fund_name: String,
pub fund_type: String,
pub fund_establishment_day: String,
pub fund_manager: String,
pub fund_management_fee: String,
pub fund_custodian_fee: String,
pub fund_status: String,
pub fund_size: String,
pub fund_share: f64,
pub fund_asset_allocation_proportion: String,
pub heavy_hold_stocks: Vec<String>,
pub heavy_hold_stocks_proportion: f64,
pub heavy_hold_bond: Vec<String>,
pub heavy_hold_bond_proportion: f64,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_current_tick)]
#[response(format = "csv", type = "Tick")]
pub struct GetCurrentTick {
pub code: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Tick {
pub time: f64,
pub current: f64,
pub high: f64,
pub low: f64,
pub volumn: f64,
pub money: f64,
pub position: f64,
pub a1_v: f64,
pub a2_v: f64,
pub a3_v: f64,
pub a4_v: f64,
pub a5_v: f64,
pub a1_p: f64,
pub a2_p: f64,
pub a3_p: f64,
pub a4_p: f64,
pub a5_p: f64,
pub b1_v: f64,
pub b2_v: f64,
pub b3_v: f64,
pub b4_v: f64,
pub b5_v: f64,
pub b1_p: f64,
pub b2_p: f64,
pub b3_p: f64,
pub b4_p: f64,
pub b5_p: f64,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_current_ticks)]
#[response(format = "csv", type = "Tick")]
pub struct GetCurrentTicks {
pub code: String,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_extras)]
#[response(format = "csv", type = "Extra")]
pub struct GetExtras {
pub code: String,
pub date: String,
pub end_date: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Extra {
pub date: String,
pub is_st: Option<i8>,
pub acc_net_value: Option<f64>,
pub unit_net_value: Option<f64>,
pub futures_sett_price: Option<f64>,
pub futures_positions: Option<f64>,
pub adj_net_value: Option<f64>,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_price)]
#[response(format = "csv", type = "Price")]
pub struct GetPrice {
pub date: String,
pub count: u32,
pub unit: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fq_ref_date: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Price {
pub date: String,
pub open: f64,
pub close: f64,
pub high: f64,
pub low: f64,
pub volume: f64,
pub money: f64,
pub paused: Option<u8>,
pub high_limit: Option<f64>,
pub low_limit: Option<f64>,
pub avg: Option<f64>,
pub pre_close: Option<f64>,
pub open_interest: Option<f64>,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_price_period)]
#[response(format = "csv", type = "Price")]
pub struct GetPricePeriod {
pub code: String,
pub unit: String,
pub date: String,
pub end_date: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub fq_ref_date: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_ticks)]
#[response(format = "csv", type = "Tick")]
pub struct GetTicks {
pub code: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub count: Option<u32>,
pub end_date: String,
pub skip: bool,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_ticks_period)]
#[response(format = "csv", type = "Tick")]
pub struct GetTicksPeriod {
pub code: String,
pub date: String,
pub end_date: String,
pub skip: bool,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(get_factor_values)]
#[response(format = "csv", type = "FactorValue")]
pub struct GetFactorValues {
pub code: String,
pub columns: String,
pub date: String,
pub end_date: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct FactorValue {
pub date: String,
pub cfo_to_ev: Option<f64>,
pub net_profit_ratio: Option<f64>,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(run_query)]
#[response(format = "line")]
pub struct RunQuery {
pub table: String,
pub columns: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub conditions: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub count: Option<u32>,
}
#[derive(Debug, Serialize, Deserialize, Request, Response)]
#[request(run_query)]
#[response(format = "single", type = "i32")]
pub struct GetQueryCount {}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_security_kind_rename() {
assert_serde_security_kind("stock", &SecurityKind::Stock);
assert_serde_security_kind("fund", &SecurityKind::Fund);
assert_serde_security_kind("index", &SecurityKind::Index);
assert_serde_security_kind("futures", &SecurityKind::Futures);
assert_serde_security_kind("etf", &SecurityKind::ETF);
assert_serde_security_kind("lof", &SecurityKind::LOF);
assert_serde_security_kind("fja", &SecurityKind::FJA);
assert_serde_security_kind("fjb", &SecurityKind::FJB);
assert_serde_security_kind("QDII_fund", &SecurityKind::QDIIFund);
assert_serde_security_kind("open_fund", &SecurityKind::OpenFund);
assert_serde_security_kind("bond_fund", &SecurityKind::BondFund);
assert_serde_security_kind("stock_fund", &SecurityKind::StockFund);
assert_serde_security_kind("money_market_fund", &SecurityKind::MoneyMarketFund);
assert_serde_security_kind("mixture_fund", &SecurityKind::MixtureFund);
assert_serde_security_kind("options", &SecurityKind::Options);
}
#[test]
fn test_get_all_securities() {
let gas = GetAllSecurities {
code: SecurityKind::Stock,
date: Some(String::from("2020-02-16")),
};
assert_eq!(
serde_json::to_string(&json!({
"method": "get_all_securities",
"token": "abc",
"code": "stock",
"date": "2020-02-16",
}))
.unwrap(),
gas.request("abc").unwrap()
);
}
fn assert_serde_security_kind(s: &str, k: &SecurityKind) {
let str_repr = serde_json::to_string(s).unwrap();
assert_eq!(str_repr, serde_json::to_string(k).unwrap());
assert_eq!(k, &serde_json::from_str::<SecurityKind>(&str_repr).unwrap());
}
}