From 2efd7e089e773a3a49f5b2092f22703a82b0ced8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=B1=E7=BA=AF=E5=87=80?= <1113655791@qq.com> Date: Thu, 29 May 2025 22:42:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=8C=E8=8A=B1=E9=A1=BA?= =?UTF-8?q?=E7=9A=84=E5=A4=8D=E6=9D=83=E6=97=A5=E7=BA=BF=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3,=E6=8B=89=E5=8F=96=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E9=80=9A=E8=BF=87(=E6=97=A0=E6=88=90=E4=BA=A4=E9=87=91?= =?UTF-8?q?=E9=A2=9D=E6=95=B0=E6=8D=AE),=E5=90=8E=E7=BB=AD=E5=BA=94?= =?UTF-8?q?=E8=AF=A5=E4=BC=9A=E8=BD=AC=E7=A7=BB=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extend/spider_ths.go | 156 ++++++++++++++++++++++++++++++++++++++ extend/spider_ths_test.go | 18 +++++ 2 files changed, 174 insertions(+) create mode 100644 extend/spider_ths.go create mode 100644 extend/spider_ths_test.go diff --git a/extend/spider_ths.go b/extend/spider_ths.go new file mode 100644 index 0000000..941aa96 --- /dev/null +++ b/extend/spider_ths.go @@ -0,0 +1,156 @@ +package extend + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "github.com/injoyai/conv" + "github.com/injoyai/tdx" + "github.com/injoyai/tdx/protocol" + "io" + "net/http" + "strings" + "time" +) + +const ( + UrlTHSDayKline = "http://d.10jqka.com.cn/v6/line/hs_%s/0%d/all.js" + THS_QFQ uint8 = 1 //前复权 + THS_HFQ uint8 = 2 //后复权 +) + +func NewTHSDayKline() *THSDayKline { + return &THSDayKline{ + Client: http.DefaultClient, + } +} + +/* +THSDayKline +前复权,和通达信对的上,和东方财富对不上 +后复权,和通达信,东方财富都对不上 +*/ +type THSDayKline struct { + Codes []string + Client *http.Client +} + +func (this *THSDayKline) GetName() string { + return "同花顺日线" +} + +func (this *THSDayKline) Run(ctx context.Context, m *tdx.Manage) error { + codes := this.Codes + if len(codes) == 0 { + codes = m.Codes.GetStocks() + } + for _, _type := range []uint8{THS_QFQ, THS_HFQ} { + for _, code := range codes { + ls, err := this.Pull(ctx, code, _type) + if err != nil { + return err + } + + _ = ls + //加入数据库 + + <-time.After(time.Millisecond * 300) + } + } + return nil +} + +func (this *THSDayKline) Pull(ctx context.Context, code string, _type uint8) ([]*Kline, error) { + if _type != THS_QFQ && _type != THS_HFQ { + return nil, fmt.Errorf("数据类型错误,例如:前复权1或后复权2") + } + + code = protocol.AddPrefix(code) + if len(code) != 8 { + return nil, fmt.Errorf("股票代码错误,例如:SZ000001或000001") + } + + u := fmt.Sprintf(UrlTHSDayKline, code[2:], _type) + req, err := http.NewRequest(http.MethodGet, u, nil) + if err != nil { + return nil, err + } + /* + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/90.0.4430.212 Safari/537.36', + 'Referer': 'http://stockpage.10jqka.com.cn/', + 'DNT': '1', + */ + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.54") + req.Header.Set("Referer", "http://stockpage.10jqka.com.cn/") + req.Header.Set("DNT", "1") + resp, err := this.Client.Do(req) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + bs, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + n := bytes.IndexByte(bs, '(') + bs = bs[n+1 : len(bs)-1] + + m := map[string]any{} + err = json.Unmarshal(bs, &m) + if err != nil { + return nil, err + } + + total := conv.Int(m["total"]) + priceFactor := conv.Float64(m["priceFactor"]) + prices := strings.Split(conv.String(m["price"]), ",") + dates := strings.Split(conv.String(m["dates"]), ",") + volumes := strings.Split(conv.String(m["volumn"]), ",") + start := conv.String(m["start"]) + t, err := time.Parse("20060102", start) + if err != nil { + return nil, err + } + + //好像到了22点,总数量会比实际多1 + if total == len(dates)+1 && total == len(volumes)+1 { + total -= 1 + } + //判断数量是否对应 + if total*4 != len(prices) || total != len(dates) || total != len(volumes) { + return nil, fmt.Errorf("total=%d prices=%d dates=%d volumns=%d", total, len(prices), len(dates), len(volumes)) + } + + ls := []*Kline(nil) + + year := t.Year() + lastDate := "" + for i := 0; i < total; i++ { + //当日前变小时(12xx变01xx),说明过了1年,除非该股票停牌了1年多则数据错误 + if dates[i] < lastDate { + year++ + } + lastDate = dates[i] + x, err := time.Parse("0102", dates[i]) + if err != nil { + return nil, err + } + x = time.Date(year, x.Month(), x.Day(), 15, 0, 0, 0, time.Local) + low := protocol.Price(conv.Float64(prices[i*4+0]) * 1000 / priceFactor) + ls = append(ls, &Kline{ + Code: protocol.AddPrefix(code), + Date: x.Unix(), + Open: protocol.Price(conv.Float64(prices[i*4+1])*1000/priceFactor) + low, + High: protocol.Price(conv.Float64(prices[i*4+2])*1000/priceFactor) + low, + Low: low, + Close: protocol.Price(conv.Float64(prices[i*4+3])*1000/priceFactor) + low, + Volume: (conv.Int64(volumes[i]) + 50) / 100, + }) + } + + return ls, nil +} diff --git a/extend/spider_ths_test.go b/extend/spider_ths_test.go new file mode 100644 index 0000000..a8d63cc --- /dev/null +++ b/extend/spider_ths_test.go @@ -0,0 +1,18 @@ +package extend + +import ( + "context" + "testing" +) + +func TestNewSpiderTHS(t *testing.T) { + x := NewTHSDayKline() + ls, err := x.Pull(context.Background(), "sz000001", THS_HFQ) + if err != nil { + t.Error(err) + return + } + for _, v := range ls { + t.Log(v) + } +}