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 处理服务器响应的数据
|
// handlerDealMessage 处理服务器响应的数据
|
||||||
func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
|
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())
|
f, err := protocol.Decode(msg.Payload())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Err(err)
|
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))
|
val, _ := this.m.GetAndDel(conv.String(f.MsgID))
|
||||||
code := conv.String(val)
|
|
||||||
|
|
||||||
var resp any
|
var resp any
|
||||||
switch f.Type {
|
switch f.Type {
|
||||||
@@ -101,10 +106,13 @@ func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
|
|||||||
resp, err = protocol.MStockMinute.Decode(f.Data)
|
resp, err = protocol.MStockMinute.Decode(f.Data)
|
||||||
|
|
||||||
case protocol.TypeStockMinuteTrade:
|
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:
|
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:
|
default:
|
||||||
err = fmt.Errorf("通讯类型未解析:0x%X", f.Type)
|
err = fmt.Errorf("通讯类型未解析:0x%X", f.Type)
|
||||||
@@ -244,3 +252,61 @@ func (this *Client) GetStockHistoryMinuteTradeAll(exchange protocol.Exchange, co
|
|||||||
}
|
}
|
||||||
return resp, nil
|
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
|
Decode
|
||||||
帧头 |未知 |消息ID |控制码 |数据类型 |未解压长度 |解压长度 |数据域
|
帧头 |未知 |消息ID |控制码 |数据类型 |未解压长度 |解压长度 |数据域
|
||||||
b1cb7400 |1c |00000000 |00 |0d00 |5100 |bd00 |789c6378c1cecb252ace6066c5b4898987b9050ed1f90cc5b74c18a5bc18c1b43490fecff09c81819191f13fc3c9f3bb169f5e7dfefeb5ef57f7199a305009308208e5b32bb6bcbf70148712002d7f1e13
|
b1cb7400 |1c |00000000 |00 |0d00 |5100 |bd00 |789c6378c1cecb252ace6066c5b4898987b9050ed1f90cc5b74c18a5bc18c1b43490fecff09c81819191f13fc3c9f3bb169f5e7dfefeb5ef57f7199a305009308208e5b32bb6bcbf70148712002d7f1e13
|
||||||
*/
|
*/
|
||||||
func Decode(bs []byte) (*Response, error) {
|
func Decode(bs []byte) (*Response, error) {
|
||||||
if len(bs) < 16 {
|
if len(bs) < 16 {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package protocol
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/injoyai/base/g"
|
"github.com/injoyai/base/g"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -9,19 +10,18 @@ import (
|
|||||||
type StockKlineReq struct {
|
type StockKlineReq struct {
|
||||||
Exchange Exchange
|
Exchange Exchange
|
||||||
Code string
|
Code string
|
||||||
Type uint16 //类型 1分 5分 等等
|
|
||||||
Start uint16
|
Start uint16
|
||||||
Count uint16
|
Count uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *StockKlineReq) Bytes() (g.Bytes, error) {
|
func (this *StockKlineReq) Bytes(Type TypeKline) (g.Bytes, error) {
|
||||||
if len(this.Code) != 6 {
|
if len(this.Code) != 6 {
|
||||||
return nil, errors.New("股票代码长度错误")
|
return nil, errors.New("股票代码长度错误")
|
||||||
}
|
}
|
||||||
data := []byte{this.Exchange.Uint8(), 0x0}
|
data := []byte{this.Exchange.Uint8(), 0x0}
|
||||||
data = append(data, Bytes(this.Code)...)
|
data = append(data, []byte(this.Code)...) //这里怎么是正序了?
|
||||||
data = append(data, Bytes(this.Type)...)
|
data = append(data, Bytes(Type.Uint16())...)
|
||||||
data = append(data, 0x0, 0x0)
|
data = append(data, 0x01, 0x0)
|
||||||
data = append(data, Bytes(this.Start)...)
|
data = append(data, Bytes(this.Start)...)
|
||||||
data = append(data, Bytes(this.Count)...)
|
data = append(data, Bytes(this.Count)...)
|
||||||
data = append(data, make([]byte, 10)...) //未知啥含义
|
data = append(data, make([]byte, 10)...) //未知啥含义
|
||||||
@@ -34,24 +34,27 @@ type StockKlineResp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type StockKline struct {
|
type StockKline struct {
|
||||||
Open Price
|
Open Price //开盘价
|
||||||
High Price
|
High Price //最高价
|
||||||
Low Price
|
Low Price //最低价
|
||||||
Close Price
|
Close Price //收盘价
|
||||||
Volume int //成交量
|
Volume float64 //成交量
|
||||||
Number int //成交数
|
Amount float64 //成交额
|
||||||
Time time.Time
|
Time time.Time //时间
|
||||||
Year int
|
}
|
||||||
Month int
|
|
||||||
Day int
|
func (this *StockKline) String() string {
|
||||||
Hour int
|
return fmt.Sprintf("%s 开盘价:%s 最高价:%s 最低价:%s 收盘价:%s 成交量:%s 成交额:%s",
|
||||||
Minute int
|
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{}
|
type stockKline struct{}
|
||||||
|
|
||||||
func (stockKline) Frame(req *StockKlineReq) (*Frame, error) {
|
func (stockKline) Frame(Type TypeKline, req *StockKlineReq) (*Frame, error) {
|
||||||
bs, err := req.Bytes()
|
bs, err := req.Bytes(Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -62,7 +65,7 @@ func (stockKline) Frame(req *StockKlineReq) (*Frame, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (stockKline) Decode(bs []byte) (*StockKlineResp, error) {
|
func (stockKline) Decode(bs []byte, Type TypeKline) (*StockKlineResp, error) {
|
||||||
|
|
||||||
if len(bs) < 2 {
|
if len(bs) < 2 {
|
||||||
return nil, errors.New("数据长度不足")
|
return nil, errors.New("数据长度不足")
|
||||||
@@ -72,9 +75,39 @@ func (stockKline) Decode(bs []byte) (*StockKlineResp, error) {
|
|||||||
Count: Uint16(bs[:2]),
|
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 //深圳交易所
|
ExchangeSZ //深圳交易所
|
||||||
ExchangeBJ //北京交易所
|
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/encoding/simplifiedchinese"
|
||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// String 字节先转小端,再转字符
|
// String 字节先转小端,再转字符
|
||||||
@@ -56,10 +57,11 @@ func FloatUnit(f float64) (float64, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FloatUnitString(f float64) string {
|
func FloatUnitString(f float64) string {
|
||||||
m := []string{"万", "亿"}
|
m := []string{"万", "亿", "万亿", "亿亿", "万亿亿", "亿亿亿"}
|
||||||
unit := ""
|
unit := ""
|
||||||
for i := 0; f > 1e4 && i < len(m); f /= 1e4 {
|
for i := 0; f > 1e4 && i < len(m); i++ {
|
||||||
unit = m[i]
|
unit = m[i]
|
||||||
|
f /= 1e4
|
||||||
}
|
}
|
||||||
if unit == "" {
|
if unit == "" {
|
||||||
return conv.String(f)
|
return conv.String(f)
|
||||||
@@ -78,8 +80,28 @@ func GetDate(bs [2]byte) string {
|
|||||||
return fmt.Sprintf("%02d:%02d", h, m)
|
return fmt.Sprintf("%02d:%02d", h, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTime(bs [4]byte) string {
|
func GetTime(bs [4]byte, Type TypeKline) time.Time {
|
||||||
return ""
|
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 {
|
func basePrice(code string) Price {
|
||||||
|
|||||||
Reference in New Issue
Block a user