akshare 0.1.12

100% pure Rust implementation of akshare — unified access to Chinese and global financial market data APIs
Documentation
//! Shared helpers for fetching macro-economic data from Eastmoney and Jin10.

use serde::Deserialize;

use crate::client::AkShareClient;
use crate::error::Result;
use crate::types::MacroDataPoint;
use crate::types::value_ext::ValueExt;
use crate::types::wire::EmDatacenterResp;

// ---------------------------------------------------------------------------
// Wire types
// ---------------------------------------------------------------------------

#[derive(Debug, Deserialize)]
pub(crate) struct Jin10Resp {
    pub data: Option<Jin10Data>,
}

#[derive(Debug, Deserialize)]
pub(crate) struct Jin10Data {
    #[serde(default)]
    pub values: Vec<Vec<serde_json::Value>>,
}

// ---------------------------------------------------------------------------
// Eastmoney datacenter helpers
// ---------------------------------------------------------------------------

/// Fetch a macro-economic report from Eastmoney datacenter and extract
/// date + indicator value into `MacroDataPoint`.
pub(crate) async fn fetch_em_report(
    client: &AkShareClient,
    report_name: &str,
    sort_column: &str,
    name_label: &str,
) -> Result<Vec<MacroDataPoint>> {
    let url = "https://datacenter-web.eastmoney.com/api/data/v1/get";
    let resp: EmDatacenterResp = client
        .get(url)
        .query(&crate::util::eastmoney_datacenter_params(
            sort_column,
            &[("reportName", report_name)],
        ))
        .send()
        .await?
        .json()
        .await?;

    let data = resp.result.map(|r| r.data).unwrap_or_default();
    let mut items = Vec::with_capacity(data.len());
    for v in &data {
        let date = v.str_or(&["REPORT_DATE", "REPORT_PERIOD", "DATE"], "");
        let value = v.f64_or(
            &["INDICATOR_VALUE", "VALUE", "GDP", "CPI", "PPI", "PMI", "M2"],
            0.0,
        );

        if date.is_empty() {
            continue;
        }

        items.push(MacroDataPoint {
            date: date.get(..10).unwrap_or(&date).to_string(),
            value,
            name: name_label.to_string(),
        });
    }
    Ok(items)
}

/// Fetch a macro-economic report from Eastmoney datacenter with an INDICATOR_ID filter.
/// Used for country-specific economic indicators (UK, HK, Germany, etc.).
pub(crate) async fn fetch_em_indicator(
    client: &AkShareClient,
    report_name: &str,
    indicator_id: &str,
    name_label: &str,
) -> Result<Vec<MacroDataPoint>> {
    let url = "https://datacenter-web.eastmoney.com/api/data/v1/get";
    let filter = format!(r#"(INDICATOR_ID="{indicator_id}")"#);
    let resp: EmDatacenterResp = client
        .get(url)
        .query(&[
            ("reportName", report_name),
            ("columns", "ALL"),
            ("filter", filter.as_str()),
            ("pageNumber", "1"),
            ("pageSize", "5000"),
            ("sortTypes", "-1"),
            ("sortColumns", "REPORT_DATE"),
            ("source", "WEB"),
            ("client", "WEB"),
        ])
        .send()
        .await?
        .json()
        .await?;

    let data = resp.result.map(|r| r.data).unwrap_or_default();
    let mut items = Vec::with_capacity(data.len());
    for v in &data {
        let date = v.str_or(&["REPORT_DATE", "REPORT_DATE_CH"], "");
        let value = v.f64_or(&["VALUE", "INDICATOR_VALUE"], 0.0);

        if date.is_empty() {
            continue;
        }

        items.push(MacroDataPoint {
            date: date.get(..10).unwrap_or(&date).to_string(),
            value,
            name: name_label.to_string(),
        });
    }
    Ok(items)
}

/// Fetch an Eastmoney industry index with a specific INDICATOR_ID filter.
pub(crate) async fn fetch_em_industry_index(
    client: &AkShareClient,
    indicator_id: &str,
    name_label: &str,
) -> Result<Vec<MacroDataPoint>> {
    fetch_em_indicator(client, "RPT_INDUSTRY_INDEX", indicator_id, name_label).await
}

// ---------------------------------------------------------------------------
// Jin10 datacenter helpers
// ---------------------------------------------------------------------------

/// Fetch macro-economic data from Jin10 datacenter API.
/// Returns (date, current_value, forecast, previous_value) tuples.
pub(crate) async fn fetch_jin10_report(
    client: &AkShareClient,
    attr_id: &str,
    name_label: &str,
) -> Result<Vec<MacroDataPoint>> {
    let url = "https://datacenter-api.jin10.com/reports/list_v2";
    let resp: Jin10Resp = client
        .get(url)
        .query(&[("max_date", ""), ("category", "ec"), ("attr_id", attr_id)])
        .header("x-app-id", "rU6QIu7JHe2gOUeR")
        .header("x-csrf-token", "x-csrf-token")
        .header("x-version", "1.0.0")
        .send()
        .await?
        .json()
        .await?;

    let values = resp.data.map(|d| d.values).unwrap_or_default();
    let mut items = Vec::with_capacity(values.len());
    for row in &values {
        if row.len() < 2 {
            continue;
        }
        let date = row[0].as_str().unwrap_or("").to_string();
        let value = row[1].as_f64().unwrap_or(0.0);
        if date.is_empty() {
            continue;
        }
        items.push(MacroDataPoint {
            date: date.get(..10).unwrap_or(&date).to_string(),
            value,
            name: name_label.to_string(),
        });
    }
    Ok(items)
}

/// Fetch Jin10 interest rate decision data.
pub(crate) async fn fetch_jin10_interest_rate(
    client: &AkShareClient,
    attr_id: &str,
    name_label: &str,
) -> Result<Vec<MacroDataPoint>> {
    fetch_jin10_report(client, attr_id, name_label).await
}