mirror of
https://github.com/injoyai/tdx.git
synced 2025-11-26 21:25:35 +08:00
增加了获取k线图,简单的对了下日k线,成交量暂时不知道是否解析成功,其它的数据对的上
This commit is contained in:
72
client.go
72
client.go
@@ -72,6 +72,12 @@ type Client struct {
|
||||
// handlerDealMessage 处理服务器响应的数据
|
||||
func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
|
||||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
logs.Err(e)
|
||||
}
|
||||
}()
|
||||
|
||||
f, err := protocol.Decode(msg.Payload())
|
||||
if err != nil {
|
||||
logs.Err(err)
|
||||
@@ -79,7 +85,6 @@ func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
|
||||
}
|
||||
|
||||
val, _ := this.m.GetAndDel(conv.String(f.MsgID))
|
||||
code := conv.String(val)
|
||||
|
||||
var resp any
|
||||
switch f.Type {
|
||||
@@ -101,10 +106,13 @@ func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
|
||||
resp, err = protocol.MStockMinute.Decode(f.Data)
|
||||
|
||||
case protocol.TypeStockMinuteTrade:
|
||||
resp, err = protocol.MStockMinuteTrade.Decode(f.Data, code) //todo
|
||||
resp, err = protocol.MStockMinuteTrade.Decode(f.Data, conv.String(val)) //todo
|
||||
|
||||
case protocol.TypeStockHistoryMinuteTrade:
|
||||
resp, err = protocol.MStockHistoryMinuteTrade.Decode(f.Data, code)
|
||||
resp, err = protocol.MStockHistoryMinuteTrade.Decode(f.Data, conv.String(val))
|
||||
|
||||
case protocol.TypeStockKline:
|
||||
resp, err = protocol.MStockKline.Decode(f.Data, protocol.TypeKline(conv.Uint16(val)))
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("通讯类型未解析:0x%X", f.Type)
|
||||
@@ -244,3 +252,61 @@ func (this *Client) GetStockHistoryMinuteTradeAll(exchange protocol.Exchange, co
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetStockKline 获取k线数据
|
||||
func (this *Client) GetStockKline(Type protocol.TypeKline, req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) {
|
||||
f, err := protocol.MStockKline.Frame(Type, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := this.SendFrame(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.(*protocol.StockKlineResp), nil
|
||||
}
|
||||
|
||||
// GetStockKlineMinute 获取一分钟k线数据
|
||||
func (this *Client) GetStockKlineMinute(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) {
|
||||
return this.GetStockKline(protocol.TypeKlineMinute, req)
|
||||
}
|
||||
|
||||
// GetStockKline5Minute 获取五分钟k线数据
|
||||
func (this *Client) GetStockKline5Minute(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) {
|
||||
return this.GetStockKline(protocol.TypeKline5Minute, req)
|
||||
}
|
||||
|
||||
// GetStockKline15Minute 获取十五分钟k线数据
|
||||
func (this *Client) GetStockKline15Minute(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) {
|
||||
return this.GetStockKline(protocol.TypeKline15Minute, req)
|
||||
}
|
||||
|
||||
// GetStockKline30Minute 获取三十分钟k线数据
|
||||
func (this *Client) GetStockKline30Minute(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) {
|
||||
return this.GetStockKline(protocol.TypeKline30Minute, req)
|
||||
}
|
||||
|
||||
// GetStockKlineDay 获取日k线数据
|
||||
func (this *Client) GetStockKlineDay(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) {
|
||||
return this.GetStockKline(protocol.TypeKlineDay, req)
|
||||
}
|
||||
|
||||
// GetStockKlineWeek 获取周k线数据
|
||||
func (this *Client) GetStockKlineWeek(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) {
|
||||
return this.GetStockKline(protocol.TypeKlineWeek, req)
|
||||
}
|
||||
|
||||
// GetStockKlineMonth 获取月k线数据
|
||||
func (this *Client) GetStockKlineMonth(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) {
|
||||
return this.GetStockKline(protocol.TypeKlineMonth, req)
|
||||
}
|
||||
|
||||
// GetStockKlineQuarter 获取季k线数据
|
||||
func (this *Client) GetStockKlineQuarter(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) {
|
||||
return this.GetStockKline(protocol.TypeKlineQuarter, req)
|
||||
}
|
||||
|
||||
// GetStockKlineYear 获取年k线数据
|
||||
func (this *Client) GetStockKlineYear(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) {
|
||||
return this.GetStockKline(protocol.TypeKlineYear, req)
|
||||
}
|
||||
|
||||
26
example/GetStockKline/main.go
Normal file
26
example/GetStockKline/main.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/injoyai/logs"
|
||||
"github.com/injoyai/tdx"
|
||||
"github.com/injoyai/tdx/example/common"
|
||||
"github.com/injoyai/tdx/protocol"
|
||||
)
|
||||
|
||||
func main() {
|
||||
common.Test(func(c *tdx.Client) {
|
||||
resp, err := c.GetStockKlineDay(&protocol.StockKlineReq{
|
||||
Exchange: protocol.ExchangeSH,
|
||||
Code: "000001",
|
||||
Start: 0,
|
||||
Count: 10,
|
||||
})
|
||||
logs.PanicErr(err)
|
||||
|
||||
for _, v := range resp.List {
|
||||
logs.Debug(v)
|
||||
}
|
||||
|
||||
logs.Debug("总数:", resp.Count)
|
||||
})
|
||||
}
|
||||
@@ -58,18 +58,5 @@ const (
|
||||
|
||||
)
|
||||
|
||||
const (
|
||||
KLINE_TYPE_5MIN = 0 // 5分钟K 线
|
||||
KLINE_TYPE_15MIN = 1 // 15分钟K 线
|
||||
KLINE_TYPE_30MIN = 2 // 30分钟K 线
|
||||
KLINE_TYPE_1HOUR = 3 // 1小时K 线
|
||||
KLINE_TYPE_DAILY = 4 // 日K 线
|
||||
KLINE_TYPE_WEEKLY = 5 // 周K 线
|
||||
KLINE_TYPE_MONTHLY = 6 // 月K 线
|
||||
KLINE_TYPE_EXHQ_1MIN = 7 // 1分钟
|
||||
KLINE_TYPE_1MIN = 8 // 1分钟K 线
|
||||
KLINE_TYPE_RI_K = 9 // 日K 线
|
||||
KLINE_TYPE_3MONTH = 10 // 季K 线
|
||||
KLINE_TYPE_YEARLY = 11 // 年K 线
|
||||
)
|
||||
|
||||
*/
|
||||
|
||||
@@ -65,8 +65,8 @@ type Response struct {
|
||||
|
||||
/*
|
||||
Decode
|
||||
帧头 |未知 |消息ID |控制码 |数据类型 |未解压长度 |解压长度 |数据域
|
||||
b1cb7400 |1c |00000000 |00 |0d00 |5100 |bd00 |789c6378c1cecb252ace6066c5b4898987b9050ed1f90cc5b74c18a5bc18c1b43490fecff09c81819191f13fc3c9f3bb169f5e7dfefeb5ef57f7199a305009308208e5b32bb6bcbf70148712002d7f1e13
|
||||
帧头 |未知 |消息ID |控制码 |数据类型 |未解压长度 |解压长度 |数据域
|
||||
b1cb7400 |1c |00000000 |00 |0d00 |5100 |bd00 |789c6378c1cecb252ace6066c5b4898987b9050ed1f90cc5b74c18a5bc18c1b43490fecff09c81819191f13fc3c9f3bb169f5e7dfefeb5ef57f7199a305009308208e5b32bb6bcbf70148712002d7f1e13
|
||||
*/
|
||||
func Decode(bs []byte) (*Response, error) {
|
||||
if len(bs) < 16 {
|
||||
|
||||
@@ -2,6 +2,7 @@ package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/injoyai/base/g"
|
||||
"time"
|
||||
)
|
||||
@@ -9,19 +10,18 @@ import (
|
||||
type StockKlineReq struct {
|
||||
Exchange Exchange
|
||||
Code string
|
||||
Type uint16 //类型 1分 5分 等等
|
||||
Start uint16
|
||||
Count uint16
|
||||
}
|
||||
|
||||
func (this *StockKlineReq) Bytes() (g.Bytes, error) {
|
||||
func (this *StockKlineReq) Bytes(Type TypeKline) (g.Bytes, error) {
|
||||
if len(this.Code) != 6 {
|
||||
return nil, errors.New("股票代码长度错误")
|
||||
}
|
||||
data := []byte{this.Exchange.Uint8(), 0x0}
|
||||
data = append(data, Bytes(this.Code)...)
|
||||
data = append(data, Bytes(this.Type)...)
|
||||
data = append(data, 0x0, 0x0)
|
||||
data = append(data, []byte(this.Code)...) //这里怎么是正序了?
|
||||
data = append(data, Bytes(Type.Uint16())...)
|
||||
data = append(data, 0x01, 0x0)
|
||||
data = append(data, Bytes(this.Start)...)
|
||||
data = append(data, Bytes(this.Count)...)
|
||||
data = append(data, make([]byte, 10)...) //未知啥含义
|
||||
@@ -34,24 +34,27 @@ type StockKlineResp struct {
|
||||
}
|
||||
|
||||
type StockKline struct {
|
||||
Open Price
|
||||
High Price
|
||||
Low Price
|
||||
Close Price
|
||||
Volume int //成交量
|
||||
Number int //成交数
|
||||
Time time.Time
|
||||
Year int
|
||||
Month int
|
||||
Day int
|
||||
Hour int
|
||||
Minute int
|
||||
Open Price //开盘价
|
||||
High Price //最高价
|
||||
Low Price //最低价
|
||||
Close Price //收盘价
|
||||
Volume float64 //成交量
|
||||
Amount float64 //成交额
|
||||
Time time.Time //时间
|
||||
}
|
||||
|
||||
func (this *StockKline) String() string {
|
||||
return fmt.Sprintf("%s 开盘价:%s 最高价:%s 最低价:%s 收盘价:%s 成交量:%s 成交额:%s",
|
||||
this.Time.Format("2006-01-02 15:04:05"),
|
||||
this.Open, this.High, this.Low, this.Close,
|
||||
FloatUnitString(this.Volume), FloatUnitString(this.Amount),
|
||||
)
|
||||
}
|
||||
|
||||
type stockKline struct{}
|
||||
|
||||
func (stockKline) Frame(req *StockKlineReq) (*Frame, error) {
|
||||
bs, err := req.Bytes()
|
||||
func (stockKline) Frame(Type TypeKline, req *StockKlineReq) (*Frame, error) {
|
||||
bs, err := req.Bytes(Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -62,7 +65,7 @@ func (stockKline) Frame(req *StockKlineReq) (*Frame, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (stockKline) Decode(bs []byte) (*StockKlineResp, error) {
|
||||
func (stockKline) Decode(bs []byte, Type TypeKline) (*StockKlineResp, error) {
|
||||
|
||||
if len(bs) < 2 {
|
||||
return nil, errors.New("数据长度不足")
|
||||
@@ -72,9 +75,39 @@ func (stockKline) Decode(bs []byte) (*StockKlineResp, error) {
|
||||
Count: Uint16(bs[:2]),
|
||||
}
|
||||
|
||||
for i := uint16(0); i < resp.Count; i++ {
|
||||
bs = bs[2:]
|
||||
|
||||
var last Price
|
||||
|
||||
for i := uint16(0); i < resp.Count; i++ {
|
||||
k := &StockKline{
|
||||
Time: GetTime([4]byte(bs[:4]), Type),
|
||||
}
|
||||
|
||||
var open Price
|
||||
bs, open = GetPrice(bs[4:])
|
||||
var _close Price
|
||||
bs, _close = GetPrice(bs)
|
||||
var high Price
|
||||
bs, high = GetPrice(bs)
|
||||
var low Price
|
||||
bs, low = GetPrice(bs)
|
||||
|
||||
k.Open = (open + last) / 10
|
||||
k.Close = (open + last + _close) / 10
|
||||
k.High = (open + last + high) / 10
|
||||
k.Low = (open + last + low) / 10
|
||||
|
||||
last = last + open + _close
|
||||
|
||||
//logs.Debug(Reverse(bs[:4]), getVolume(Uint32(bs[:4])))
|
||||
//logs.Debug(Reverse(bs[4:8]), getVolume(Uint32(bs[4:8])))
|
||||
k.Volume = getVolume(Uint32(bs[:4])) / 100
|
||||
k.Amount = getVolume(Uint32(bs[4:8]))
|
||||
|
||||
bs = bs[8:]
|
||||
resp.List = append(resp.List, k)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
36
protocol/model_stock_kline_test.go
Normal file
36
protocol/model_stock_kline_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_stockKline_Frame(t *testing.T) {
|
||||
//预期0c02000000001c001c002d050000303030303031 0900 0100 0000 0a00 00000000000000000000
|
||||
// 0c00000000011c001c002d050000313030303030 0900 0000 0000 0a00 00000000000000000000
|
||||
f, _ := MStockKline.Frame(TypeKlineDay2, &StockKlineReq{
|
||||
Exchange: ExchangeSH,
|
||||
Code: "000001",
|
||||
Start: 0,
|
||||
Count: 10,
|
||||
})
|
||||
t.Log(f.Bytes().HEX())
|
||||
}
|
||||
|
||||
func Test_stockKline_Decode(t *testing.T) {
|
||||
s := "0a0078da340198b8018404bc055ee8b3e949ad2b094f79da34010af801a002cc0260dec949859ded4e7ada34016882028e04e603b8f91e4a111f394f7dda3401e401c20200f604f84d2b4ad4d0444f7eda3401721eaa0268d87bc549ee80e34e7fda34011e288601c601d08db849230ed54e80da3401727c32da013023584999a0784e81da3401147c0ad001d0fa86498d989a4e84da34015e6800d60278c28e491ca6a14e85da340154d001b801da01403e924989d6a54e"
|
||||
bs, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
resp, err := MStockKline.Decode(bs, 9)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log(len(resp.List))
|
||||
for _, v := range resp.List {
|
||||
t.Log(v)
|
||||
}
|
||||
}
|
||||
@@ -45,3 +45,22 @@ const (
|
||||
ExchangeSZ //深圳交易所
|
||||
ExchangeBJ //北京交易所
|
||||
)
|
||||
|
||||
type TypeKline uint8
|
||||
|
||||
func (this TypeKline) Uint16() uint16 { return uint16(this) }
|
||||
|
||||
const (
|
||||
TypeKline5Minute TypeKline = 0 // 5分钟K 线
|
||||
TypeKline15Minute TypeKline = 1 // 15分钟K 线
|
||||
TypeKline30Minute TypeKline = 2 // 30分钟K 线
|
||||
TypeKlineHour TypeKline = 3 // 1小时K 线
|
||||
TypeKlineDay TypeKline = 4 // 日K 线
|
||||
TypeKlineWeek TypeKline = 5 // 周K 线
|
||||
TypeKlineMonth TypeKline = 6 // 月K 线
|
||||
TypeKlineMinute TypeKline = 7 // 1分钟
|
||||
TypeKlineMinute2 TypeKline = 8 // 1分钟K 线
|
||||
TypeKlineDay2 TypeKline = 9 // 日K 线
|
||||
TypeKlineQuarter TypeKline = 10 // 季K 线
|
||||
TypeKlineYear TypeKline = 11 // 年K 线
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// String 字节先转小端,再转字符
|
||||
@@ -56,10 +57,11 @@ func FloatUnit(f float64) (float64, string) {
|
||||
}
|
||||
|
||||
func FloatUnitString(f float64) string {
|
||||
m := []string{"万", "亿"}
|
||||
m := []string{"万", "亿", "万亿", "亿亿", "万亿亿", "亿亿亿"}
|
||||
unit := ""
|
||||
for i := 0; f > 1e4 && i < len(m); f /= 1e4 {
|
||||
for i := 0; f > 1e4 && i < len(m); i++ {
|
||||
unit = m[i]
|
||||
f /= 1e4
|
||||
}
|
||||
if unit == "" {
|
||||
return conv.String(f)
|
||||
@@ -78,8 +80,28 @@ func GetDate(bs [2]byte) string {
|
||||
return fmt.Sprintf("%02d:%02d", h, m)
|
||||
}
|
||||
|
||||
func GetTime(bs [4]byte) string {
|
||||
return ""
|
||||
func GetTime(bs [4]byte, Type TypeKline) time.Time {
|
||||
switch Type {
|
||||
case TypeKlineDay, TypeKlineMinute, TypeKlineMinute2:
|
||||
|
||||
yearMonthDay := Uint16(bs[:2])
|
||||
hourMinute := Uint16(bs[:2])
|
||||
year := int(yearMonthDay>>11 + 2004)
|
||||
month := yearMonthDay % 2048 / 100
|
||||
day := int((yearMonthDay % 2048) % 100)
|
||||
hour := int(hourMinute / 60)
|
||||
minute := int(hourMinute % 60)
|
||||
return time.Date(year, time.Month(month), day, hour, minute, 0, 0, time.Local)
|
||||
|
||||
default:
|
||||
|
||||
yearMonthDay := Uint32(bs[:4])
|
||||
year := int(yearMonthDay / 10000)
|
||||
month := int((yearMonthDay % 10000) / 100)
|
||||
day := int(yearMonthDay % 100)
|
||||
return time.Date(year, time.Month(month), day, 15, 0, 0, 0, time.Local)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func basePrice(code string) Price {
|
||||
|
||||
Reference in New Issue
Block a user