Files
WarrenZhou e28bfec957 Initial commit: 可转债轮动分析系统
- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换)
- 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制)
- 增量同步:支持 -incr/-since 参数按存档日期增量入库
- 策略回测:净值曲线/10指标/基准对比,含基本面过滤
- 用户认证:用户名密码 + 邮箱验证码双登录方式
- 前端:三Tab SPA(实时行情/策略回测/数据管理)
2026-08-17 20:23:22 +08:00

100 lines
2.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}