- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换) - 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制) - 增量同步:支持 -incr/-since 参数按存档日期增量入库 - 策略回测:净值曲线/10指标/基准对比,含基本面过滤 - 用户认证:用户名密码 + 邮箱验证码双登录方式 - 前端:三Tab SPA(实时行情/策略回测/数据管理)
116 lines
3.2 KiB
Go
116 lines
3.2 KiB
Go
package eastmoney
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"net/url"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"stock-analysis/internal/model"
|
||
)
|
||
|
||
// datacenterFundamentalResp 是 datacenter 财务数据接口响应。
|
||
type datacenterFundamentalResp struct {
|
||
Result struct {
|
||
Pages int `json:"pages"`
|
||
Data []datacenterFundamentalItem `json:"data"`
|
||
} `json:"result"`
|
||
Success bool `json:"success"`
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
// datacenterFundamentalItem 是单条财务数据记录。
|
||
type datacenterFundamentalItem struct {
|
||
SecurityCode string `json:"SECURITY_CODE"`
|
||
SecurityName string `json:"SECURITY_NAME_ABBR"`
|
||
ROE *float64 `json:"ROEJQ"` // 加权净资产收益率(%)
|
||
DebtRatio *float64 `json:"ZCFZL"` // 资产负债率(%)
|
||
NetProfit *float64 `json:"PARENTNETPROFIT"` // 归属净利润(元)
|
||
IsBZ string `json:"IS_BZ"` // ST状态:0=正常, 1=ST, 2=*ST
|
||
ReportDate string `json:"REPORT_DATE"`
|
||
}
|
||
|
||
// FetchStockFundamentals 批量拉取指定正股的最新年度基本面数据。
|
||
// 使用 datacenter API(非 push2),按 REPORT_DATE 倒序取每只股票的最新一条年报。
|
||
func (c *Client) FetchStockFundamentals(ctx context.Context, codes []string) ([]model.StockFundamental, error) {
|
||
if len(codes) == 0 {
|
||
return nil, nil
|
||
}
|
||
|
||
codeSet := make(map[string]bool, len(codes))
|
||
for _, code := range codes {
|
||
codeSet[code] = true
|
||
}
|
||
|
||
const pageSize = 5000
|
||
seen := make(map[string]bool, len(codes))
|
||
var funds []model.StockFundamental
|
||
|
||
// 按 REPORT_DATE 倒序拉取,每只股票取第一条(最新年报)。
|
||
for page := 1; page <= 3; page++ {
|
||
params := url.Values{}
|
||
params.Set("reportName", "RPT_F10_FINANCE_MAINFINADATA")
|
||
params.Set("columns", "SECURITY_CODE,SECURITY_NAME_ABBR,ROEJQ,ZCFZL,PARENTNETPROFIT,IS_BZ,REPORT_DATE")
|
||
params.Set("filter", `(REPORT_TYPE="年报")`)
|
||
params.Set("pageNumber", strconv.Itoa(page))
|
||
params.Set("pageSize", strconv.Itoa(pageSize))
|
||
params.Set("sortColumns", "REPORT_DATE")
|
||
params.Set("sortTypes", "-1")
|
||
|
||
var resp datacenterFundamentalResp
|
||
if err := c.getJSON(ctx, []string{datacenterHost}, "/api/data/v1/get", params, &resp); err != nil {
|
||
return nil, fmt.Errorf("datacenter page %d: %w", page, err)
|
||
}
|
||
if !resp.Success {
|
||
return nil, fmt.Errorf("datacenter API error: %s", resp.Message)
|
||
}
|
||
if len(resp.Result.Data) == 0 {
|
||
break
|
||
}
|
||
|
||
for _, d := range resp.Result.Data {
|
||
code := d.SecurityCode
|
||
if code == "" || seen[code] || !codeSet[code] {
|
||
continue
|
||
}
|
||
seen[code] = true
|
||
|
||
roe := 0.0
|
||
if d.ROE != nil {
|
||
roe = *d.ROE
|
||
}
|
||
debtRatio := 0.0
|
||
if d.DebtRatio != nil {
|
||
debtRatio = *d.DebtRatio
|
||
}
|
||
netProfit := 0.0
|
||
if d.NetProfit != nil {
|
||
netProfit = *d.NetProfit
|
||
}
|
||
status := 1
|
||
if d.IsBZ == "1" || d.IsBZ == "2" {
|
||
status = 2
|
||
}
|
||
|
||
updateDate := strings.Split(d.ReportDate, " ")[0]
|
||
|
||
funds = append(funds, model.StockFundamental{
|
||
Code: code,
|
||
Name: d.SecurityName,
|
||
ROE: roe,
|
||
DebtRatio: debtRatio,
|
||
NetProfit: netProfit,
|
||
Status: status,
|
||
UpdateDate: updateDate,
|
||
})
|
||
}
|
||
|
||
// 已找到全部目标股票,停止翻页。
|
||
if len(seen) >= len(codes) {
|
||
break
|
||
}
|
||
}
|
||
|
||
return funds, nil
|
||
} |