use super::helpers::{
fmt_date, json_f64, json_f64_opt, json_i64, json_i64_opt, json_str, json_str_opt,
};
use super::types::{
GdfxHoldingAnalyse, GdfxHoldingChange, GdfxHoldingDetail, GdfxHoldingStatistic, GdfxTeamwork,
GdfxTop10, HoldChangeCninfo, HoldControlCninfo, ManagementDetail,
};
use crate::client::AkShareClient;
use crate::error::Result;
impl AkShareClient {
pub async fn stock_gdfx_free_holding_statistics_em(
&self,
date: &str,
) -> Result<Vec<GdfxHoldingStatistic>> {
let filter = format!(
"(HOLDNUM_CHANGE_TYPE=\"001\")(END_DATE='{}')",
fmt_date(date)
);
let data = self
.dc_fetch_all(
"RPT_COOPFREEHOLDERS_ANALYSIS",
"ALL",
&filter,
"STATISTICS_TIMES,COOPERATION_HOLDER_MARK",
"-1,-1",
500,
10,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| GdfxHoldingStatistic {
holder_name: json_str(v, "HOLDER_NAME"),
holder_type: json_str_opt(v, "HOLDER_TYPE"),
statistics_count: json_i64_opt(v, "STATISTICS_TIMES"),
avg_change_10d: json_f64_opt(v, "HOLD_10_DAYS_AVG_CHR"),
max_change_10d: json_f64_opt(v, "HOLD_10_DAYS_MAX_CHR"),
min_change_10d: json_f64_opt(v, "HOLD_10_DAYS_MIN_CHR"),
avg_change_30d: json_f64_opt(v, "HOLD_30_DAYS_AVG_CHR"),
max_change_30d: json_f64_opt(v, "HOLD_30_DAYS_MAX_CHR"),
min_change_30d: json_f64_opt(v, "HOLD_30_DAYS_MIN_CHR"),
avg_change_60d: json_f64_opt(v, "HOLD_60_DAYS_AVG_CHR"),
max_change_60d: json_f64_opt(v, "HOLD_60_DAYS_MAX_CHR"),
min_change_60d: json_f64_opt(v, "HOLD_60_DAYS_MIN_CHR"),
holding_stocks: json_str_opt(v, "HOLD_SECURITIES"),
})
.collect())
}
pub async fn stock_gdfx_holding_statistics_em(
&self,
date: &str,
) -> Result<Vec<GdfxHoldingStatistic>> {
let filter = format!(
"(HOLDNUM_CHANGE_TYPE=\"001\")(END_DATE='{}')",
fmt_date(date)
);
let data = self
.dc_fetch_all(
"RPT_COOPHOLDERS_ANALYSIS",
"ALL",
&filter,
"STATISTICS_TIMES,COOPERATION_HOLDER_MARK",
"-1,-1",
500,
10,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| GdfxHoldingStatistic {
holder_name: json_str(v, "HOLDER_NAME"),
holder_type: json_str_opt(v, "HOLDER_TYPE"),
statistics_count: json_i64_opt(v, "STATISTICS_TIMES"),
avg_change_10d: json_f64_opt(v, "HOLD_10_DAYS_AVG_CHR"),
max_change_10d: json_f64_opt(v, "HOLD_10_DAYS_MAX_CHR"),
min_change_10d: json_f64_opt(v, "HOLD_10_DAYS_MIN_CHR"),
avg_change_30d: json_f64_opt(v, "HOLD_30_DAYS_AVG_CHR"),
max_change_30d: json_f64_opt(v, "HOLD_30_DAYS_MAX_CHR"),
min_change_30d: json_f64_opt(v, "HOLD_30_DAYS_MIN_CHR"),
avg_change_60d: json_f64_opt(v, "HOLD_60_DAYS_AVG_CHR"),
max_change_60d: json_f64_opt(v, "HOLD_60_DAYS_MAX_CHR"),
min_change_60d: json_f64_opt(v, "HOLD_60_DAYS_MIN_CHR"),
holding_stocks: json_str_opt(v, "HOLD_SECURITIES"),
})
.collect())
}
pub async fn stock_gdfx_free_holding_change_em(
&self,
date: &str,
) -> Result<Vec<GdfxHoldingChange>> {
let filter = format!("(END_DATE='{}')", fmt_date(date));
let data = self
.dc_fetch_all(
"RPT_FREEHOLDERS_BASIC_INFO",
"ALL",
&filter,
"HOLDER_NUM,HOLDER_NEW",
"-1,-1",
500,
10,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| GdfxHoldingChange {
holder_name: json_str(v, "HOLDER_NAME"),
holder_type: json_str_opt(v, "HOLDER_TYPE"),
total_holdings: json_i64(v, "HOLDER_NUM"),
new_positions: json_i64(v, "HOLDER_NEW"),
increased: json_i64(v, "HOLDER_INCREASE"),
unchanged: json_i64(v, "HOLDER_UNCHANGED"),
decreased: json_i64(v, "HOLDER_DECREASE"),
circulating_market_cap: json_f64_opt(v, "FREE_MARKET_CAP"),
holding_stocks: json_str_opt(v, "HOLD_SECURITIES"),
})
.collect())
}
pub async fn stock_gdfx_holding_change_em(&self, date: &str) -> Result<Vec<GdfxHoldingChange>> {
let filter = format!("(END_DATE='{}')", fmt_date(date));
let data = self
.dc_fetch_all(
"RPT_HOLDERS_BASIC_INFO",
"ALL",
&filter,
"HOLDER_NUM,HOLDER_NEW",
"-1,-1",
500,
10,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| GdfxHoldingChange {
holder_name: json_str(v, "HOLDER_NAME"),
holder_type: json_str_opt(v, "HOLDER_TYPE"),
total_holdings: json_i64(v, "HOLDER_NUM"),
new_positions: json_i64(v, "HOLDER_NEW"),
increased: json_i64(v, "HOLDER_INCREASE"),
unchanged: json_i64(v, "HOLDER_UNCHANGED"),
decreased: json_i64(v, "HOLDER_DECREASE"),
circulating_market_cap: json_f64_opt(v, "FREE_MARKET_CAP"),
holding_stocks: json_str_opt(v, "HOLD_SECURITIES"),
})
.collect())
}
pub async fn stock_gdfx_free_top_10_em(
&self,
symbol: &str,
date: &str,
) -> Result<Vec<GdfxTop10>> {
let url = "https://emweb.securities.eastmoney.com/PC_HSF10/ShareholderResearch/PageSDLTGD";
let resp = self
.get(url)
.query(&[
("code", symbol.to_uppercase().as_str()),
("date", fmt_date(date).as_str()),
])
.send()
.await
.map_err(crate::error::Error::from)?
.error_for_status()
.map_err(crate::error::Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(crate::error::Error::from)?;
let arr = json
.get("sdltgd")
.and_then(|d| d.as_array())
.cloned()
.unwrap_or_default();
Ok(arr
.iter()
.enumerate()
.map(|(i, v)| GdfxTop10 {
rank: (i + 1) as i64,
holder_name: json_str(v, "HOLDER_NAME"),
holder_nature: json_str_opt(v, "HOLDER_NATURE"),
share_type: json_str_opt(v, "SHARES_TYPE"),
holding_count: json_f64(v, "HOLD_NUM"),
holding_ratio: json_f64(v, "FREE_HOLDNUM_RATIO"),
change: json_str_opt(v, "XZCHANGE"),
change_ratio: json_f64_opt(v, "CHANGE_RATIO"),
})
.collect())
}
pub async fn stock_gdfx_top_10_em(&self, symbol: &str, date: &str) -> Result<Vec<GdfxTop10>> {
let url = "https://emweb.securities.eastmoney.com/PC_HSF10/ShareholderResearch/PageSDGD";
let resp = self
.get(url)
.query(&[
("code", symbol.to_uppercase().as_str()),
("date", fmt_date(date).as_str()),
])
.send()
.await
.map_err(crate::error::Error::from)?
.error_for_status()
.map_err(crate::error::Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(crate::error::Error::from)?;
let arr = json
.get("sdgd")
.and_then(|d| d.as_array())
.cloned()
.unwrap_or_default();
Ok(arr
.iter()
.enumerate()
.map(|(i, v)| GdfxTop10 {
rank: (i + 1) as i64,
holder_name: json_str(v, "HOLDER_NAME"),
holder_nature: None,
share_type: json_str_opt(v, "SHARES_TYPE"),
holding_count: json_f64(v, "HOLD_NUM"),
holding_ratio: json_f64(v, "HOLD_RATIO"),
change: json_str_opt(v, "XZCHANGE"),
change_ratio: json_f64_opt(v, "CHANGE_RATIO"),
})
.collect())
}
pub async fn stock_gdfx_free_holding_detail_em(
&self,
date: &str,
) -> Result<Vec<GdfxHoldingDetail>> {
let filter = format!("(END_DATE='{}')", fmt_date(date));
let data = self
.dc_fetch_all(
"RPT_F10_EH_FREEHOLDERS",
"ALL",
&filter,
"UPDATE_DATE,SECURITY_CODE,HOLDER_RANK",
"-1,1,1",
500,
10,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| GdfxHoldingDetail {
holder_name: json_str(v, "HOLDER_NAME"),
holder_type: json_str_opt(v, "HOLDER_TYPE"),
code: json_str(v, "SECURITY_CODE"),
name: json_str(v, "SECURITY_NAME_ABBR"),
report_date: json_str(v, "END_DATE"),
holding_count: json_f64(v, "HOLD_NUM"),
holding_change: json_f64_opt(v, "XZCHANGE"),
holding_change_ratio: json_f64_opt(v, "CHANGE_RATIO"),
holding_change_name: json_str_opt(v, "HOLDNUM_CHANGE_NAME"),
circulating_market_cap: json_f64_opt(v, "HOLDER_MARKET_CAP"),
notice_date: json_str(v, "UPDATE_DATE"),
})
.collect())
}
pub async fn stock_gdfx_holding_detail_em(
&self,
date: &str,
_indicator: &str,
symbol: &str,
) -> Result<Vec<GdfxHoldingDetail>> {
let filter = format!(
"(END_DATE='{}')(HOLDNUM_CHANGE_TYPE=\"{}\")",
fmt_date(date),
symbol
);
let report = "RPT_F10_EH_HOLDERS";
let data = self
.dc_fetch_all(
report,
"ALL",
&filter,
"UPDATE_DATE,SECURITY_CODE,HOLDER_RANK",
"-1,1,1",
500,
10,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| GdfxHoldingDetail {
holder_name: json_str(v, "HOLDER_NAME"),
holder_type: json_str_opt(v, "HOLDER_TYPE"),
code: json_str(v, "SECURITY_CODE"),
name: json_str(v, "SECURITY_NAME_ABBR"),
report_date: json_str(v, "END_DATE"),
holding_count: json_f64(v, "HOLD_NUM"),
holding_change: json_f64_opt(v, "XZCHANGE"),
holding_change_ratio: json_f64_opt(v, "CHANGE_RATIO"),
holding_change_name: json_str_opt(v, "HOLDNUM_CHANGE_NAME"),
circulating_market_cap: json_f64_opt(v, "HOLDER_MARKET_CAP"),
notice_date: json_str(v, "UPDATE_DATE"),
})
.collect())
}
pub async fn stock_gdfx_free_holding_analyse_em(
&self,
date: &str,
) -> Result<Vec<GdfxHoldingAnalyse>> {
let filter = format!("(END_DATE='{}')", fmt_date(date));
let data = self
.dc_fetch_all(
"RPT_F10_EH_FREEHOLDERS_ANALYSE",
"ALL",
&filter,
"SECURITY_CODE",
"1",
500,
10,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| GdfxHoldingAnalyse {
code: json_str(v, "SECURITY_CODE"),
name: json_str(v, "SECURITY_NAME_ABBR"),
report_date: json_str(v, "END_DATE"),
holder_count: json_i64_opt(v, "HOLDER_NUM"),
holder_count_change: json_i64_opt(v, "HOLDER_NUM_CHANGE"),
holder_count_ratio: json_f64_opt(v, "HOLDER_NUM_RATIO"),
avg_holding_amount: json_f64_opt(v, "AVG_HOLD_NUM"),
avg_market_cap: json_f64_opt(v, "AVG_MARKET_CAP"),
total_market_cap: json_f64_opt(v, "TOTAL_MARKET_CAP"),
total_shares: json_f64_opt(v, "TOTAL_A_SHARES"),
price: json_f64_opt(v, "CLOSE_PRICE"),
change_pct: json_f64_opt(v, "CHANGE_RATE"),
})
.collect())
}
pub async fn stock_gdfx_holding_analyse_em(
&self,
date: &str,
) -> Result<Vec<GdfxHoldingAnalyse>> {
let filter = format!("(END_DATE='{}')", fmt_date(date));
let data = self
.dc_fetch_all(
"RPT_F10_EH_HOLDERS_ANALYSE",
"ALL",
&filter,
"SECURITY_CODE",
"1",
500,
10,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| GdfxHoldingAnalyse {
code: json_str(v, "SECURITY_CODE"),
name: json_str(v, "SECURITY_NAME_ABBR"),
report_date: json_str(v, "END_DATE"),
holder_count: json_i64_opt(v, "HOLDER_NUM"),
holder_count_change: json_i64_opt(v, "HOLDER_NUM_CHANGE"),
holder_count_ratio: json_f64_opt(v, "HOLDER_NUM_RATIO"),
avg_holding_amount: json_f64_opt(v, "AVG_HOLD_NUM"),
avg_market_cap: json_f64_opt(v, "AVG_MARKET_CAP"),
total_market_cap: json_f64_opt(v, "TOTAL_MARKET_CAP"),
total_shares: json_f64_opt(v, "TOTAL_A_SHARES"),
price: json_f64_opt(v, "CLOSE_PRICE"),
change_pct: json_f64_opt(v, "CHANGE_RATE"),
})
.collect())
}
pub async fn stock_gdfx_free_holding_teamwork_em(
&self,
symbol: &str,
) -> Result<Vec<GdfxTeamwork>> {
let filter = format!("(HOLDER_TYPE=\"{symbol}\")");
let data = self
.dc_fetch_all(
"RPT_FREEHOLDERS_TEAMWORK",
"ALL",
&filter,
"HOLDER_NUM",
"-1",
500,
1,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| GdfxTeamwork {
holder_name: json_str(v, "HOLDER_NAME"),
holder_type: json_str_opt(v, "HOLDER_TYPE"),
holding_count: json_i64(v, "HOLDER_NUM"),
total_market_cap: json_f64_opt(v, "FREE_MARKET_CAP"),
holding_stocks: json_str_opt(v, "HOLD_SECURITIES"),
})
.collect())
}
pub async fn stock_gdfx_holding_teamwork_em(&self, symbol: &str) -> Result<Vec<GdfxTeamwork>> {
let filter = format!("(HOLDER_TYPE=\"{symbol}\")");
let data = self
.dc_fetch_all(
"RPT_HOLDERS_TEAMWORK",
"ALL",
&filter,
"HOLDER_NUM",
"-1",
500,
1,
&[],
)
.await?;
Ok(data
.iter()
.map(|v| GdfxTeamwork {
holder_name: json_str(v, "HOLDER_NAME"),
holder_type: json_str_opt(v, "HOLDER_TYPE"),
holding_count: json_i64(v, "HOLDER_NUM"),
total_market_cap: json_f64_opt(v, "FREE_MARKET_CAP"),
holding_stocks: json_str_opt(v, "HOLD_SECURITIES"),
})
.collect())
}
pub async fn stock_hold_change_cninfo(&self, symbol: &str) -> Result<Vec<HoldChangeCninfo>> {
let url = "http://webapi.cninfo.com.cn/api/sysapi/p_sysapi1087";
let resp = self
.post(url)
.form(&[("seccode", symbol)])
.header("User-Agent", "Mozilla/5.0")
.send()
.await
.map_err(crate::error::Error::from)?
.error_for_status()
.map_err(crate::error::Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(crate::error::Error::from)?;
let records = json
.get("records")
.and_then(|r| r.as_array())
.cloned()
.unwrap_or_default();
Ok(records
.iter()
.map(|v| HoldChangeCninfo { data: v.clone() })
.collect())
}
pub async fn stock_hold_control_cninfo(&self, symbol: &str) -> Result<Vec<HoldControlCninfo>> {
let url = "http://webapi.cninfo.com.cn/api/sysapi/p_sysapi1088";
let resp = self
.post(url)
.form(&[("seccode", symbol)])
.header("User-Agent", "Mozilla/5.0")
.send()
.await
.map_err(crate::error::Error::from)?
.error_for_status()
.map_err(crate::error::Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(crate::error::Error::from)?;
let records = json
.get("records")
.and_then(|r| r.as_array())
.cloned()
.unwrap_or_default();
Ok(records
.iter()
.map(|v| HoldControlCninfo { data: v.clone() })
.collect())
}
pub async fn stock_hold_management_detail_cninfo(
&self,
symbol: &str,
) -> Result<Vec<ManagementDetail>> {
let url = "http://webapi.cninfo.com.cn/api/sysapi/p_sysapi1089";
let resp = self
.post(url)
.form(&[("seccode", symbol)])
.header("User-Agent", "Mozilla/5.0")
.send()
.await
.map_err(crate::error::Error::from)?
.error_for_status()
.map_err(crate::error::Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(crate::error::Error::from)?;
let records = json
.get("records")
.and_then(|r| r.as_array())
.cloned()
.unwrap_or_default();
Ok(records
.iter()
.map(|v| ManagementDetail { data: v.clone() })
.collect())
}
pub async fn stock_hold_management_detail_em(
&self,
symbol: &str,
) -> Result<Vec<ManagementDetail>> {
let url = "https://emweb.securities.eastmoney.com/PC_HSF10/CompanyManagement/PageAjax";
let resp = self
.get(url)
.query(&[("code", symbol.to_uppercase().as_str())])
.send()
.await
.map_err(crate::error::Error::from)?
.error_for_status()
.map_err(crate::error::Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(crate::error::Error::from)?;
let arr = json.as_array().cloned().unwrap_or_default();
Ok(arr
.iter()
.map(|v| ManagementDetail { data: v.clone() })
.collect())
}
pub async fn stock_hold_management_person_em(
&self,
symbol: &str,
) -> Result<Vec<ManagementDetail>> {
let url = "https://emweb.securities.eastmoney.com/PC_HSF10/CompanyManagement/PageAjax";
let resp = self
.get(url)
.query(&[("code", symbol.to_uppercase().as_str())])
.send()
.await
.map_err(crate::error::Error::from)?
.error_for_status()
.map_err(crate::error::Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(crate::error::Error::from)?;
let arr = json.as_array().cloned().unwrap_or_default();
Ok(arr
.iter()
.map(|v| ManagementDetail { data: v.clone() })
.collect())
}
pub async fn stock_hold_num_cninfo(
&self,
symbol: &str,
date: &str,
) -> Result<Vec<HoldChangeCninfo>> {
let url = "http://webapi.cninfo.com.cn/api/sysapi/p_sysapi1090";
let sd = fmt_date(date);
let resp = self
.post(url)
.form(&[("seccode", symbol), ("sdate", sd.as_str())])
.header("User-Agent", "Mozilla/5.0")
.send()
.await
.map_err(crate::error::Error::from)?
.error_for_status()
.map_err(crate::error::Error::from)?;
let json: serde_json::Value = resp.json().await.map_err(crate::error::Error::from)?;
let records = json
.get("records")
.and_then(|r| r.as_array())
.cloned()
.unwrap_or_default();
Ok(records
.iter()
.map(|v| HoldChangeCninfo { data: v.clone() })
.collect())
}
}