From 562d11daaf6768900d2ea1ad71567ab7a2a28a7a Mon Sep 17 00:00:00 2001 From: bense Date: Thu, 12 May 2022 21:31:08 +0800 Subject: [PATCH] api --- client.go | 70 ++++++++++++++++ client_test.go | 67 +++++++++++++++ proto/get_history_minute_time_data.go | 112 ++++++++++++++++++++++++++ proto/get_history_transaction_data.go | 106 ++++++++++++++++++++++++ proto/get_index_bars.go | 6 +- proto/get_minute_time_data.go | 102 +++++++++++++++++++++++ proto/get_security_bars.go | 6 +- proto/get_transaction_data.go | 104 ++++++++++++++++++++++++ proto/proto.go | 48 +++++++---- 9 files changed, 600 insertions(+), 21 deletions(-) create mode 100644 proto/get_history_minute_time_data.go create mode 100644 proto/get_history_transaction_data.go create mode 100644 proto/get_minute_time_data.go create mode 100644 proto/get_transaction_data.go diff --git a/client.go b/client.go index 0cf84ad..5aab5dc 100644 --- a/client.go +++ b/client.go @@ -201,3 +201,73 @@ func (client *Client) GetIndexBars(category uint16, market uint16, code string, } return obj.Reply(), err } + +// GetMinuteTimeData 获取分时图数据 +func (client *Client) GetMinuteTimeData(market uint16, code string) (*proto.GetMinuteTimeDataReply, error) { + obj := proto.NewGetMinuteTimeData() + _code := [6]byte{} + copy(_code[:], code) + obj.SetParams(&proto.GetMinuteTimeDataRequest{ + Market: market, + Code: _code, + }) + err := client.do(obj) + if err != nil { + return nil, err + } + return obj.Reply(), err +} + +// GetHistoryMinuteTimeData 获取历史分时图数据 +func (client *Client) GetHistoryMinuteTimeData(date uint32, market uint16, code string) (*proto.GetHistoryMinuteTimeDataReply, error) { + obj := proto.NewGetHistoryMinuteTimeData() + _code := [6]byte{} + copy(_code[:], code) + obj.SetParams(&proto.GetHistoryMinuteTimeDataRequest{ + Date: date, + Market: uint8(market), + Code: _code, + }) + err := client.do(obj) + if err != nil { + return nil, err + } + return obj.Reply(), err +} + +// GetTransactionData 获取分时成交 +func (client *Client) GetTransactionData(market uint16, code string, start uint16, count uint16) (*proto.GetTransactionDataReply, error) { + obj := proto.NewGetTransactionData() + _code := [6]byte{} + copy(_code[:], code) + obj.SetParams(&proto.GetTransactionDataRequest{ + Market: market, + Code: _code, + Start: start, + Count: count, + }) + err := client.do(obj) + if err != nil { + return nil, err + } + return obj.Reply(), err +} + +// GetHistoryTransactionData 获取历史分时成交 +func (client *Client) GetHistoryTransactionData(date uint32, market uint16, code string, start uint16, count uint16) (*proto.GetHistoryTransactionDataReply, error) { + obj := proto.NewGetHistoryTransactionData() + _code := [6]byte{} + copy(_code[:], code) + obj.SetParams(&proto.GetHistoryTransactionDataRequest{ + Date: date, + Market: market, + Code: _code, + Start: start, + Count: count, + }) + err := client.do(obj) + if err != nil { + return nil, err + } + return obj.Reply(), err +} diff --git a/client_test.go b/client_test.go index 1418b70..b6b0ef8 100644 --- a/client_test.go +++ b/client_test.go @@ -110,3 +110,70 @@ func Test_tdx_GetIndexBars(t *testing.T) { _ = api.Disconnect() } + +func Test_tdx_GetMinuteTimeData(t *testing.T) { + api := prepare() + reply, err := api.GetMinuteTimeData(0, "159607") + if err != nil { + fmt.Println(err) + } + + fmt.Println(reply) + for _, bar := range reply.List { + fmt.Println(bar) + } + + _ = api.Disconnect() + +} + +func Test_tdx_GetHistoryMinuteTimeData(t *testing.T) { + api := prepare() + //reply, err := api.GetHistoryMinuteTimeData(20220511, 0, "159607") + reply, err := api.GetHistoryMinuteTimeData(20220511, 0, "159607") + if err != nil { + fmt.Println(err) + } + + fmt.Println(reply) + for _, bar := range reply.List { + fmt.Println(bar) + } + + _ = api.Disconnect() + +} + +func Test_tdx_GetTransactionData(t *testing.T) { + api := prepare() + //reply, err := api.GetHistoryMinuteTimeData(20220511, 0, "159607") + reply, err := api.GetTransactionData(0, "159607", 0, 10) + if err != nil { + fmt.Println(err) + } + + fmt.Println(reply) + for _, bar := range reply.List { + fmt.Println(bar) + } + + _ = api.Disconnect() + +} + +func Test_tdx_GetHistoryTransactionData(t *testing.T) { + api := prepare() + //reply, err := api.GetHistoryMinuteTimeData(20220511, 0, "159607") + reply, err := api.GetHistoryTransactionData(20220511, 0, "159607", 0, 10) + if err != nil { + fmt.Println(err) + } + + fmt.Println(reply) + for _, bar := range reply.List { + fmt.Println(bar) + } + + _ = api.Disconnect() + +} diff --git a/proto/get_history_minute_time_data.go b/proto/get_history_minute_time_data.go new file mode 100644 index 0000000..be66d92 --- /dev/null +++ b/proto/get_history_minute_time_data.go @@ -0,0 +1,112 @@ +package proto + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" +) + +type GetHistoryMinuteTimeData struct { + reqHeader *ReqHeader + respHeader *RespHeader + request *GetHistoryMinuteTimeDataRequest + reply *GetHistoryMinuteTimeDataReply + + contentHex string +} + +type GetHistoryMinuteTimeDataRequest struct { + Date uint32 + Market uint8 + Code [6]byte +} + +type GetHistoryMinuteTimeDataReply struct { + Count uint16 + List []HistoryMinuteTimeData +} + +type HistoryMinuteTimeData struct { + Price float32 + Vol int +} + +func NewGetHistoryMinuteTimeData() *GetHistoryMinuteTimeData { + obj := new(GetHistoryMinuteTimeData) + obj.reqHeader = new(ReqHeader) + obj.respHeader = new(RespHeader) + obj.request = new(GetHistoryMinuteTimeDataRequest) + obj.reply = new(GetHistoryMinuteTimeDataReply) + + obj.reqHeader.Zip = 0x0c + obj.reqHeader.SeqID = seqID() + obj.reqHeader.PacketType = 0x00 + //obj.reqHeader.PkgLen1 = + //obj.reqHeader.PkgLen2 = + obj.reqHeader.Method = KMSG_HISTORYMINUTETIMEDATE + //obj.reqHeader.Method = KMSG_MINUTETIMEDATA + obj.contentHex = "" + return obj +} +func (obj *GetHistoryMinuteTimeData) SetParams(req *GetHistoryMinuteTimeDataRequest) { + obj.request = req +} + +func (obj *GetHistoryMinuteTimeData) Serialize() ([]byte, error) { + obj.reqHeader.PkgLen1 = 0x0d + obj.reqHeader.PkgLen2 = 0x0d + + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.LittleEndian, obj.reqHeader) + err = binary.Write(buf, binary.LittleEndian, obj.request) + b, err := hex.DecodeString(obj.contentHex) + buf.Write(b) + + //b, err := hex.DecodeString(obj.contentHex) + //buf.Write(b) + + //err = binary.Write(buf, binary.LittleEndian, uint16(len(obj.stocks))) + + return buf.Bytes(), err +} + +// 结果数据都是\n,\t分隔的中文字符串,比如查询K线数据,返回的结果字符串就形如 +///“时间\t开盘价\t收盘价\t最高价\t最低价\t成交量\t成交额\n +///20150519\t4.644000\t4.732000\t4.747000\t4.576000\t146667487\t683638848.000000\n +///20150520\t4.756000\t4.850000\t4.960000\t4.756000\t353161092\t1722953216.000000” +func (obj *GetHistoryMinuteTimeData) UnSerialize(header interface{}, data []byte) error { + obj.respHeader = header.(*RespHeader) + fmt.Println(hex.EncodeToString(data)) + + pos := 0 + err := binary.Read(bytes.NewBuffer(data[pos:pos+2]), binary.LittleEndian, &obj.reply.Count) + pos += 2 + // 跳过4个字节 功能未解析 + _, _, _, bType := data[pos], data[pos+1], data[pos+2], data[pos+3] + pos += 4 + + lastprice := 0 + for index := uint16(0); index < obj.reply.Count; index++ { + priceraw := getprice(data, &pos) + _ = getprice(data, &pos) + vol := getprice(data, &pos) + lastprice += priceraw + + var p float32 + if bType > 0x40 { + p = float32(lastprice) / 100.0 + } else { + p = float32(lastprice) / 1000.0 + } + + ele := HistoryMinuteTimeData{Price: p, + Vol: vol} + obj.reply.List = append(obj.reply.List, ele) + } + return err +} + +func (obj *GetHistoryMinuteTimeData) Reply() *GetHistoryMinuteTimeDataReply { + return obj.reply +} diff --git a/proto/get_history_transaction_data.go b/proto/get_history_transaction_data.go new file mode 100644 index 0000000..b91253d --- /dev/null +++ b/proto/get_history_transaction_data.go @@ -0,0 +1,106 @@ +package proto + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" +) + +type GetHistoryTransactionData struct { + reqHeader *ReqHeader + respHeader *RespHeader + request *GetHistoryTransactionDataRequest + reply *GetHistoryTransactionDataReply + + contentHex string +} + +type GetHistoryTransactionDataRequest struct { + Date uint32 + Market uint16 + Code [6]byte + Start uint16 + Count uint16 +} + +type GetHistoryTransactionDataReply struct { + Count uint16 + List []HistoryTransactionData +} + +type HistoryTransactionData struct { + Time string + Price float64 + Vol int + Num int + BuyOrSell int +} + +func NewGetHistoryTransactionData() *GetHistoryTransactionData { + obj := new(GetHistoryTransactionData) + obj.reqHeader = new(ReqHeader) + obj.respHeader = new(RespHeader) + obj.request = new(GetHistoryTransactionDataRequest) + obj.reply = new(GetHistoryTransactionDataReply) + + obj.reqHeader.Zip = 0x0c + obj.reqHeader.SeqID = seqID() + obj.reqHeader.PacketType = 0x00 + //obj.reqHeader.PkgLen1 = + //obj.reqHeader.PkgLen2 = + obj.reqHeader.Method = KMSG_HISTORYTRANSACTIONDATA + //obj.reqHeader.Method = KMSG_MINUTETIMEDATA + obj.contentHex = "" + return obj +} +func (obj *GetHistoryTransactionData) SetParams(req *GetHistoryTransactionDataRequest) { + obj.request = req +} + +func (obj *GetHistoryTransactionData) Serialize() ([]byte, error) { + obj.reqHeader.PkgLen1 = 0x12 + obj.reqHeader.PkgLen2 = 0x12 + + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.LittleEndian, obj.reqHeader) + err = binary.Write(buf, binary.LittleEndian, obj.request) + b, err := hex.DecodeString(obj.contentHex) + buf.Write(b) + + //b, err := hex.DecodeString(obj.contentHex) + //buf.Write(b) + + //err = binary.Write(buf, binary.LittleEndian, uint16(len(obj.stocks))) + + return buf.Bytes(), err +} + +func (obj *GetHistoryTransactionData) UnSerialize(header interface{}, data []byte) error { + obj.respHeader = header.(*RespHeader) + + pos := 0 + err := binary.Read(bytes.NewBuffer(data[pos:pos+2]), binary.LittleEndian, &obj.reply.Count) + // 跳过4个字节 + pos += 6 + + lastprice := 0 + for index := uint16(0); index < obj.reply.Count; index++ { + ele := HistoryTransactionData{} + h, m := gettime(data, &pos) + ele.Time = fmt.Sprintf("%02d:%02d", h, m) + priceraw := getprice(data, &pos) + ele.Vol = getprice(data, &pos) + ele.BuyOrSell = getprice(data, &pos) + getprice(data, &pos) + + lastprice = lastprice + priceraw + ele.Price = float64(lastprice) / baseUnit(string(obj.request.Code[:])) + obj.reply.List = append(obj.reply.List, ele) + } + return err +} + +func (obj *GetHistoryTransactionData) Reply() *GetHistoryTransactionDataReply { + return obj.reply +} diff --git a/proto/get_index_bars.go b/proto/get_index_bars.go index 58d6149..4f2dc32 100644 --- a/proto/get_index_bars.go +++ b/proto/get_index_bars.go @@ -27,10 +27,10 @@ type GetIndexBarsRequest struct { type GetIndexBarsReply struct { Count uint16 - List []GetIndexBar + List []IndexBar } -type GetIndexBar struct { +type IndexBar struct { Open float64 Close float64 High float64 @@ -98,7 +98,7 @@ func (obj *GetIndexBars) UnSerialize(header interface{}, data []byte) error { pre_diff_base := 0 //lasttime := "" for index := uint16(0); index < obj.reply.Count; index++ { - ele := GetIndexBar{} + ele := IndexBar{} ele.Year, ele.Month, ele.Day, ele.Hour, ele.Minute = getdatetime(int(obj.request.Category), data, &pos) diff --git a/proto/get_minute_time_data.go b/proto/get_minute_time_data.go new file mode 100644 index 0000000..a7547a8 --- /dev/null +++ b/proto/get_minute_time_data.go @@ -0,0 +1,102 @@ +package proto + +// todo API未有效解析 + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" +) + +type GetMinuteTimeData struct { + reqHeader *ReqHeader + respHeader *RespHeader + request *GetMinuteTimeDataRequest + reply *GetMinuteTimeDataReply + + contentHex string +} + +type GetMinuteTimeDataRequest struct { + Market uint16 + Code [6]byte + Date uint32 +} + +type GetMinuteTimeDataReply struct { + Count uint16 + List []MinuteTimeData +} + +type MinuteTimeData struct { + Price float32 + Vol int +} + +func NewGetMinuteTimeData() *GetMinuteTimeData { + obj := new(GetMinuteTimeData) + obj.reqHeader = new(ReqHeader) + obj.respHeader = new(RespHeader) + obj.request = new(GetMinuteTimeDataRequest) + obj.reply = new(GetMinuteTimeDataReply) + + obj.reqHeader.Zip = 0x0c + obj.reqHeader.SeqID = seqID() + obj.reqHeader.PacketType = 0x00 + //obj.reqHeader.PkgLen1 = + //obj.reqHeader.PkgLen2 = + obj.reqHeader.Method = 0x051d + //obj.reqHeader.Method = KMSG_MINUTETIMEDATA + obj.contentHex = "" + return obj +} +func (obj *GetMinuteTimeData) SetParams(req *GetMinuteTimeDataRequest) { + obj.request = req +} + +func (obj *GetMinuteTimeData) Serialize() ([]byte, error) { + obj.reqHeader.PkgLen1 = 0x0e + obj.reqHeader.PkgLen2 = 0x0e + + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.LittleEndian, obj.reqHeader) + err = binary.Write(buf, binary.LittleEndian, obj.request) + b, err := hex.DecodeString(obj.contentHex) + buf.Write(b) + + //b, err := hex.DecodeString(obj.contentHex) + //buf.Write(b) + + //err = binary.Write(buf, binary.LittleEndian, uint16(len(obj.stocks))) + + return buf.Bytes(), err +} + +// 结果数据都是\n,\t分隔的中文字符串,比如查询K线数据,返回的结果字符串就形如 +///“时间\t开盘价\t收盘价\t最高价\t最低价\t成交量\t成交额\n +///20150519\t4.644000\t4.732000\t4.747000\t4.576000\t146667487\t683638848.000000\n +///20150520\t4.756000\t4.850000\t4.960000\t4.756000\t353161092\t1722953216.000000” +func (obj *GetMinuteTimeData) UnSerialize(header interface{}, data []byte) error { + obj.respHeader = header.(*RespHeader) + fmt.Println(hex.EncodeToString(data)) + pos := 0 + err := binary.Read(bytes.NewBuffer(data[pos:pos+2]), binary.LittleEndian, &obj.reply.Count) + // 跳过4个字节 + pos += 6 + + lastprice := 0 + for index := uint16(0); index < obj.reply.Count; index++ { + priceraw := getprice(data, &pos) + getprice(data, &pos) + vol := getprice(data, &pos) + lastprice = lastprice + priceraw + ele := MinuteTimeData{float32(lastprice) / 100.0, vol} + obj.reply.List = append(obj.reply.List, ele) + } + return err +} + +func (obj *GetMinuteTimeData) Reply() *GetMinuteTimeDataReply { + return obj.reply +} diff --git a/proto/get_security_bars.go b/proto/get_security_bars.go index 175fac3..4f1ed23 100644 --- a/proto/get_security_bars.go +++ b/proto/get_security_bars.go @@ -27,10 +27,10 @@ type GetSecurityBarsRequest struct { type GetSecurityBarsReply struct { Count uint16 - List []GetSecurityBar + List []SecurityBar } -type GetSecurityBar struct { +type SecurityBar struct { Open float64 Close float64 High float64 @@ -100,7 +100,7 @@ func (obj *GetSecurityBars) UnSerialize(header interface{}, data []byte) error { pre_diff_base := 0 for index := uint16(0); index < obj.reply.Count; index++ { - ele := GetSecurityBar{} + ele := SecurityBar{} ele.Year, ele.Month, ele.Day, ele.Hour, ele.Minute = getdatetime(int(obj.request.Category), data, &pos) ele.DateTime = fmt.Sprintf("%d-%02d-%02d %02d:%02d:00", ele.Year, ele.Month, ele.Day, ele.Hour, ele.Minute) diff --git a/proto/get_transaction_data.go b/proto/get_transaction_data.go new file mode 100644 index 0000000..4f5c065 --- /dev/null +++ b/proto/get_transaction_data.go @@ -0,0 +1,104 @@ +package proto + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" +) + +type GetTransactionData struct { + reqHeader *ReqHeader + respHeader *RespHeader + request *GetTransactionDataRequest + reply *GetTransactionDataReply + + contentHex string +} + +type GetTransactionDataRequest struct { + Market uint16 + Code [6]byte + Start uint16 + Count uint16 +} + +type GetTransactionDataReply struct { + Count uint16 + List []TransactionData +} + +type TransactionData struct { + Time string + Price float64 + Vol int + Num int + BuyOrSell int +} + +func NewGetTransactionData() *GetTransactionData { + obj := new(GetTransactionData) + obj.reqHeader = new(ReqHeader) + obj.respHeader = new(RespHeader) + obj.request = new(GetTransactionDataRequest) + obj.reply = new(GetTransactionDataReply) + + obj.reqHeader.Zip = 0x0c + obj.reqHeader.SeqID = seqID() + obj.reqHeader.PacketType = 0x00 + //obj.reqHeader.PkgLen1 = + //obj.reqHeader.PkgLen2 = + obj.reqHeader.Method = KMSG_TRANSACTIONDATA + //obj.reqHeader.Method = KMSG_MINUTETIMEDATA + obj.contentHex = "" + return obj +} +func (obj *GetTransactionData) SetParams(req *GetTransactionDataRequest) { + obj.request = req +} + +func (obj *GetTransactionData) Serialize() ([]byte, error) { + obj.reqHeader.PkgLen1 = 0x0e + obj.reqHeader.PkgLen2 = 0x0e + + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.LittleEndian, obj.reqHeader) + err = binary.Write(buf, binary.LittleEndian, obj.request) + b, err := hex.DecodeString(obj.contentHex) + buf.Write(b) + + //b, err := hex.DecodeString(obj.contentHex) + //buf.Write(b) + + //err = binary.Write(buf, binary.LittleEndian, uint16(len(obj.stocks))) + + return buf.Bytes(), err +} + +func (obj *GetTransactionData) UnSerialize(header interface{}, data []byte) error { + obj.respHeader = header.(*RespHeader) + + pos := 0 + err := binary.Read(bytes.NewBuffer(data[pos:pos+2]), binary.LittleEndian, &obj.reply.Count) + pos += 2 + + lastprice := 0 + for index := uint16(0); index < obj.reply.Count; index++ { + ele := TransactionData{} + hour, minute := gettime(data, &pos) + ele.Time = fmt.Sprintf("%02d:%02d", hour, minute) + priceraw := getprice(data, &pos) + ele.Vol = getprice(data, &pos) + ele.Num = getprice(data, &pos) + ele.BuyOrSell = getprice(data, &pos) + lastprice += priceraw + ele.Price = float64(lastprice) / baseUnit(string(obj.request.Code[:])) + _ = getprice(data, &pos) + obj.reply.List = append(obj.reply.List, ele) + } + return err +} + +func (obj *GetTransactionData) Reply() *GetTransactionDataReply { + return obj.reply +} diff --git a/proto/proto.go b/proto/proto.go index 0ced4ed..b169e4b 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -102,34 +102,42 @@ func seqID() uint32 { // pytdx : 类似utf-8的编码方式保存有符号数字 func getprice(b []byte, pos *int) int { - posbype := 6 - bdata := b[*pos] - intdata := int(bdata & 0x3f) + /* + 0x7f与常量做与运算实质是保留常量(转换为二进制形式)的后7位数,既取值区间为[0,127] + 0x3f与常量做与运算实质是保留常量(转换为二进制形式)的后6位数,既取值区间为[0,63] - sign := false - if (bdata & 0x40) > 0 { - sign = true + 0x80 1000 0000 + 0x7f 0111 1111 + 0x40 100 0000 + 0x3f 011 1111 + */ + posByte := 6 + bData := b[*pos] + data := int(bData & 0x3f) + bSign := false + if (bData & 0x40) > 0 { + bSign = true } - if (bdata & 0x80) > 0 { + if (bData & 0x80) > 0 { for { *pos += 1 - bdata = b[*pos] - intdata += (int(bdata&0x7f) << posbype) + bData = b[*pos] + data += (int(bData&0x7f) << posByte) - posbype += 7 + posByte += 7 - if (bdata & 0x80) <= 0 { + if (bData & 0x80) <= 0 { break } } } - *pos += 1 + *pos++ - if sign { - intdata = -intdata + if bSign { + data = -data } - return intdata + return data } func gettime(b []byte, pos *int) (h uint16, m uint16) { @@ -278,3 +286,13 @@ func getvolume(ivol int) (volume float64) { volume = dbl_xmm6 + dbl_xmm4 + dbl_xmm3 + dbl_xmm1 return } + +func baseUnit(code string) float64 { + c := code[:2] + switch c { + case "60", "30", "68", "00": + return 100.0 + + } + return 1000.0 +}