package eastmoney import ( "context" "fmt" "net/url" "strconv" "strings" "stock-analysis/internal/model" ) // Kline 周期常量(东财 klt 参数)。 const ( KltDay = "101" // 日K KltWeek = "102" // 周K KltMonth = "103" // 月K ) // klineResp 是 K线接口响应结构。 type klineResp struct { Data *struct { Code string `json:"code"` Name string `json:"name"` Klines []string `json:"klines"` } `json:"data"` } // FetchKline 拉取指定标的历史K线。 // code 为标的代码(如 113667),klt 为周期,beg/end 形如 20200101。 // 返回按时间升序排列的K线。 func (c *Client) FetchKline(ctx context.Context, code, klt, beg, end string) ([]model.Kline, error) { secid := secidOf(code) params := url.Values{} params.Set("secid", secid) params.Set("klt", klt) params.Set("fqt", "0") // 不复权(转债回测用不复权价) params.Set("beg", beg) params.Set("end", end) params.Set("fields1", "f1,f2,f3,f4,f5,f6") params.Set("fields2", "f51,f52,f53,f54,f55,f56,f57") var resp klineResp if err := c.getJSON(ctx, historyHosts, "/api/qt/stock/kline/get", params, &resp); err != nil { return nil, err } if resp.Data == nil || len(resp.Data.Klines) == 0 { return nil, fmt.Errorf("kline empty: secid=%s", secid) } klines := make([]model.Kline, 0, len(resp.Data.Klines)) for _, line := range resp.Data.Klines { // 格式:日期,开,收,高,低,成交量,成交额 parts := strings.Split(line, ",") if len(parts) < 7 { continue } k, err := parseKline(parts) if err != nil { continue } klines = append(klines, k) } return klines, nil } // secidOf 根据代码生成东财 secid。 // 沪市(11/118 开头)前缀 1,深市(12 开头)前缀 0。 func secidOf(code string) string { if strings.HasPrefix(code, "12") { return "0." + code } return "1." + code } // parseKline 解析一行K线数据。 func parseKline(parts []string) (model.Kline, error) { open, err1 := strconv.ParseFloat(parts[1], 64) close_, err2 := strconv.ParseFloat(parts[2], 64) high, err3 := strconv.ParseFloat(parts[3], 64) low, err4 := strconv.ParseFloat(parts[4], 64) if err1 != nil || err2 != nil || err3 != nil || err4 != nil { return model.Kline{}, fmt.Errorf("bad kline numbers") } volume, err := strconv.ParseInt(parts[5], 10, 64) if err != nil { volume = 0 } amount, _ := strconv.ParseFloat(parts[6], 64) return model.Kline{ Date: parts[0], Open: open, Close: close_, High: high, Low: low, Volume: volume, Amount: amount, }, nil }