use serde::Deserialize;
use crate::client::AkShareClient;
use crate::error::{Error, Result};
use crate::types::CxIndexPoint;
#[derive(Debug, Deserialize)]
struct CxEnvelope {
data: Option<Vec<CxRow>>,
}
#[derive(Debug, Deserialize)]
struct CxRow {
#[serde(default)]
v: Option<f64>, #[serde(default)]
s: Option<f64>, #[serde(default)]
t: Option<i64>, #[serde(default)]
value: Option<f64>,
#[serde(default)]
change: Option<f64>,
#[serde(default)]
date: Option<i64>,
}
async fn fetch_cx_index(
client: &AkShareClient,
cx_type: &str,
extra: &[(&str, &str)],
) -> Result<Vec<CxIndexPoint>> {
let mut params = vec![("type", cx_type)];
params.extend(extra.iter().map(|(k, v)| (*k, *v)));
let response = crate::util::send_and_check(
client
.get("https://yun.ccxe.com.cn/api/index/pro/cxIndexTrendInfo")
.query(¶ms),
)
.await?;
let payload: CxEnvelope = response.json().await.map_err(Error::from)?;
let rows = payload.data.unwrap_or_default();
let points: Vec<CxIndexPoint> = rows
.into_iter()
.filter_map(|r| {
let ts = r.t.or(r.date)?;
let val = r.v.or(r.value)?;
let chg = r.s.or(r.change).unwrap_or(0.0);
let dt = chrono::DateTime::from_timestamp_millis(ts)?;
let date = dt
.with_timezone(&chrono::FixedOffset::east_opt(8 * 3600)?)
.format("%Y-%m-%d")
.to_string();
Some(CxIndexPoint {
date,
value: val,
change: chg,
})
})
.collect();
if points.is_empty() {
return Err(Error::not_found("cx returned no data points"));
}
Ok(points)
}
macro_rules! cx_index {
($(#[$meta:meta])* $name:ident, $code:expr) => {
$(#[$meta])*
pub async fn $name(&self) -> Result<Vec<CxIndexPoint>> {
fetch_cx_index(self, $code, &[]).await
}
};
($(#[$meta:meta])* $name:ident, $code:expr, $extra:expr) => {
$(#[$meta])*
pub async fn $name(&self) -> Result<Vec<CxIndexPoint>> {
fetch_cx_index(self, $code, $extra).await
}
};
}
impl AkShareClient {
cx_index!( index_pmi_com_cx, "com");
cx_index!( index_pmi_man_cx, "man");
cx_index!( index_pmi_ser_cx, "ser");
cx_index!( index_dei_cx, "dei");
cx_index!( index_ii_cx, "ii");
cx_index!( index_si_cx, "si");
cx_index!( index_fi_cx, "fi");
cx_index!( index_bi_cx, "bi");
cx_index!( index_nei_cx, "nei");
cx_index!( index_li_cx, "li");
cx_index!( index_ci_cx, "ci");
cx_index!( index_ti_cx, "ti");
cx_index!( index_neaw_cx, "neaw");
cx_index!( index_awpr_cx, "awpr");
cx_index!( index_cci_cx, "cci", &[("code", "1000050"), ("month", "-1")]);
cx_index!( index_qli_cx, "qli", &[("code", "1000050"), ("month", "-1")]);
cx_index!( index_ai_cx, "ai", &[("code", "1000050"), ("month", "-1")]);
cx_index!( index_bei_cx, "ind", &[("code", "930927"), ("month", "-1")]);
cx_index!( index_neei_cx, "ind", &[("code", "930928"), ("month", "1")]);
}
#[cfg(test)]
mod tests {
#[test]
fn test_placeholder() {
}
}