use super::helpers::*;
use super::types::*;
use crate::client::AkShareClient;
use crate::error::{Error, Result};
impl AkShareClient {
pub async fn stock_fund_flow_individual(&self, symbol: &str) -> Result<Vec<FundFlowEntry>> {
let sort_field = match symbol {
"3日排行" => "f62",
"5日排行" => "f62",
"10日排行" => "f62",
"20日排行" => "f62",
_ => "f62",
};
let data = self
.cistock_fetch(
"m:0+t:6,m:0+t:80,m:1+t:2,m:1+t:23,m:0+t:81+s:2048",
"f2,f3,f12,f14,f62,f184,f66,f69,f72,f75,f78,f164,f166",
"5000",
sort_field,
)
.await?;
Ok(data
.iter()
.map(|v| FundFlowEntry {
code: json_str(v, "f12"),
name: json_str(v, "f14"),
latest_price: json_f64_opt(v, "f2"),
change_pct: json_f64_opt(v, "f3"),
turnover_rate: None,
inflow: json_f64_opt(v, "f62"),
outflow: None,
net_flow: json_f64_opt(v, "f62"),
amount: None,
})
.collect())
}
pub async fn stock_fund_flow_concept(&self, symbol: &str) -> Result<Vec<SectorFundFlowRank>> {
let data = self.sector_fund_flow_fetch("concept", symbol).await?;
Ok(data)
}
pub async fn stock_fund_flow_industry(&self, symbol: &str) -> Result<Vec<SectorFundFlowRank>> {
let data = self.sector_fund_flow_fetch("industry", symbol).await?;
Ok(data)
}
pub async fn stock_fund_flow_big_deal(&self, symbol: &str) -> Result<Vec<FundFlowEntry>> {
let _ = symbol; let data = self
.cistock_fetch(
"m:0+t:6,m:0+t:80,m:1+t:2,m:1+t:23,m:0+t:81+s:2048",
"f2,f3,f12,f14,f62,f184,f66,f69",
"100",
"f62",
)
.await?;
Ok(data
.iter()
.map(|v| FundFlowEntry {
code: json_str(v, "f12"),
name: json_str(v, "f14"),
latest_price: json_f64_opt(v, "f2"),
change_pct: json_f64_opt(v, "f3"),
turnover_rate: None,
inflow: json_f64_opt(v, "f62"),
outflow: None,
net_flow: json_f64_opt(v, "f62"),
amount: None,
})
.collect())
}
pub async fn stock_sector_fund_flow_hist(
&self,
symbol: &str,
) -> Result<Vec<SectorFundFlowHist>> {
let url = "https://push2his.eastmoney.com/api/qt/stock/fflow/daykline/get".to_string();
let resp = self
.get(&url)
.query(&[
("lmt", "0"),
("klt", "101"),
("secid", symbol),
("fields1", "f1,f2,f3,f7"),
(
"fields2",
"f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64,f65",
),
])
.send()
.await
.map_err(Error::from)?
.error_for_status()
.map_err(Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(Error::from)?;
let klines = json
.get("data")
.and_then(|d| d.get("klines"))
.and_then(|k| k.as_array())
.cloned()
.unwrap_or_default();
Ok(klines
.iter()
.map(|v| SectorFundFlowHist { data: v.clone() })
.collect())
}
pub async fn stock_sector_fund_flow_rank(
&self,
indicator: &str,
) -> Result<Vec<SectorFundFlowRank>> {
let data = self.sector_fund_flow_fetch("industry", indicator).await?;
Ok(data)
}
pub async fn stock_sector_fund_flow_summary(&self) -> Result<Vec<SectorFundFlowSummary>> {
let data = self
.dc_fetch_all(
"RPT_SECTOR_FUND_FLOW",
"ALL",
"",
"NET_INFLOW",
"-1",
500,
2,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| SectorFundFlowSummary { data: v.clone() })
.collect())
}
pub async fn stock_main_fund_flow(&self, symbol: &str) -> Result<Vec<MainFundFlow>> {
let market_code = if symbol.starts_with("6") { "1" } else { "0" };
let secid = format!("{}.{}", market_code, symbol);
let url = "https://push2his.eastmoney.com/api/qt/stock/fflow/daykline/get";
let resp = self
.get(url)
.query(&[
("lmt", "0"),
("klt", "101"),
("secid", secid.as_str()),
("fields1", "f1,f2,f3,f7"),
(
"fields2",
"f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64,f65",
),
])
.send()
.await
.map_err(Error::from)?
.error_for_status()
.map_err(Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(Error::from)?;
let klines = json
.get("data")
.and_then(|d| d.get("klines"))
.and_then(|k| k.as_array())
.cloned()
.unwrap_or_default();
Ok(klines
.iter()
.map(|v| MainFundFlow { data: v.clone() })
.collect())
}
pub async fn stock_market_fund_flow(&self) -> Result<Vec<MarketFundFlow>> {
let data = self
.dc_fetch_all(
"RPT_MARKET_FUND_FLOW",
"ALL",
"",
"TRADE_DATE",
"-1",
500,
5,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| MarketFundFlow { data: v.clone() })
.collect())
}
pub async fn stock_concept_fund_flow_hist(
&self,
symbol: &str,
) -> Result<Vec<ConceptFundFlowHist>> {
let url = "https://push2his.eastmoney.com/api/qt/stock/fflow/daykline/get";
let resp = self
.get(url)
.query(&[
("lmt", "0"),
("klt", "101"),
("secid", symbol),
("fields1", "f1,f2,f3,f7"),
(
"fields2",
"f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64,f65",
),
])
.send()
.await
.map_err(Error::from)?
.error_for_status()
.map_err(Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(Error::from)?;
let klines = json
.get("data")
.and_then(|d| d.get("klines"))
.and_then(|k| k.as_array())
.cloned()
.unwrap_or_default();
Ok(klines
.iter()
.map(|v| ConceptFundFlowHist { data: v.clone() })
.collect())
}
async fn sector_fund_flow_fetch(
&self,
_kind: &str,
_indicator: &str,
) -> Result<Vec<SectorFundFlowRank>> {
let data = self
.cistock_fetch(
"m:90+t:2",
"f2,f3,f12,f14,f62,f184,f66,f69,f72,f75,f78,f164,f166",
"5000",
"f62",
)
.await?;
Ok(data
.iter()
.map(|v| SectorFundFlowRank {
name: json_str(v, "f14"),
change_pct: json_f64_opt(v, "f3"),
inflow: json_f64_opt(v, "f62"),
outflow: None,
net_flow: json_f64_opt(v, "f62"),
leader: None,
leader_change_pct: None,
})
.collect())
}
async fn cistock_fetch(
&self,
fs: &str,
fields: &str,
page_size: &str,
sort_field: &str,
) -> Result<Vec<serde_json::Value>> {
let resp = self
.get("https://push2.eastmoney.com/api/qt/clist/get")
.query(&[
("pn", "1"),
("pz", page_size),
("po", "1"),
("np", "1"),
("ut", "bd1d9ddb04089700cf9c27f6f7426281"),
("fltt", "2"),
("invt", "2"),
("fid", sort_field),
("fs", fs),
("fields", fields),
])
.send()
.await
.map_err(Error::from)?
.error_for_status()
.map_err(Error::from)?;
let payload: super::helpers::ClistSpotEnvelope = resp.json().await.map_err(Error::from)?;
let items = payload.data.and_then(|d| d.diff).unwrap_or_default();
if items.is_empty() {
return Err(Error::not_found("eastmoney clist returned no data"));
}
Ok(items)
}
}