mirror of
https://github.com/injoyai/tdx.git
synced 2025-11-26 21:25:35 +08:00
101
This commit is contained in:
27
client.go
27
client.go
@@ -31,7 +31,11 @@ func Dial(addr string, op ...client.Option) (*Client, error) {
|
|||||||
w: wait.New(time.Second * 2),
|
w: wait.New(time.Second * 2),
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cli.Connect()
|
err = cli.connect()
|
||||||
|
if err != nil {
|
||||||
|
c.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return cli, err
|
return cli, err
|
||||||
}
|
}
|
||||||
@@ -55,7 +59,7 @@ type Client struct {
|
|||||||
msgID uint32
|
msgID uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Client) SendFrame(f protocol.Frame) (any, error) {
|
func (this *Client) SendFrame(f *protocol.Frame) (any, error) {
|
||||||
this.msgID++
|
this.msgID++
|
||||||
f.MsgID = this.msgID
|
f.MsgID = this.msgID
|
||||||
if _, err := this.c.Write(f.Bytes()); err != nil {
|
if _, err := this.c.Write(f.Bytes()); err != nil {
|
||||||
@@ -79,8 +83,8 @@ func (this *Client) Close() error {
|
|||||||
return this.c.Close()
|
return this.c.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Client) Connect() error {
|
func (this *Client) connect() error {
|
||||||
f := protocol.NewConnect()
|
f := protocol.MConnect.Frame()
|
||||||
_, err := this.Write(f.Bytes())
|
_, err := this.Write(f.Bytes())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -91,7 +95,7 @@ func (this *Client) GetSecurityList() (*protocol.SecurityListResp, error) {
|
|||||||
|
|
||||||
f := protocol.Frame{
|
f := protocol.Frame{
|
||||||
Control: 0x01,
|
Control: 0x01,
|
||||||
Type: protocol.Connect,
|
Type: protocol.TypeConnect,
|
||||||
Data: nil,
|
Data: nil,
|
||||||
}
|
}
|
||||||
_ = f
|
_ = f
|
||||||
@@ -105,3 +109,16 @@ func (this *Client) GetSecurityList() (*protocol.SecurityListResp, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSecurityQuotes 获取盘口五档报价
|
||||||
|
func (this *Client) GetSecurityQuotes(m map[protocol.Exchange]string) (*protocol.SecurityQuotesResp, error) {
|
||||||
|
f, err := protocol.MSecurityQuote.Frame(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result, err := this.SendFrame(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result.(*protocol.SecurityQuotesResp), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ func (this Exchange) Uint8() uint8 { return uint8(this) }
|
|||||||
const (
|
const (
|
||||||
ExchangeSH Exchange = iota //上海交易所
|
ExchangeSH Exchange = iota //上海交易所
|
||||||
ExchangeSZ //深圳交易所
|
ExchangeSZ //深圳交易所
|
||||||
|
ExchangeBJ //北京交易所
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -14,9 +15,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Connect = 0x000d //建立连接
|
TypeConnect = 0x000d //建立连接
|
||||||
Handshake = 0xdb0f //握手
|
TypeHandshake = 0xdb0f //握手
|
||||||
SecurityQuote = 0x053e // 行情信息
|
TypeSecurityQuote = 0x053e // 行情信息
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ func (this *Frame) Bytes() g.Bytes {
|
|||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Prefix uint32 //未知,猜测是帧头
|
Prefix uint32 //未知,猜测是帧头
|
||||||
Control uint8 //未知,猜测是响应的控制码
|
I2 uint8 //未知,猜测是响应的控制码
|
||||||
MsgID uint32 //消息ID
|
MsgID uint32 //消息ID
|
||||||
I3 uint8 //未知,猜测是响应的控制码
|
Control uint8 //未知,猜测是响应的控制码
|
||||||
Type uint16 //响应类型,对应请求类型,如建立连接,请求分时数据等
|
Type uint16 //响应类型,对应请求类型,如建立连接,请求分时数据等
|
||||||
ZipLength uint16 //数据长度
|
ZipLength uint16 //数据长度
|
||||||
Length uint16 //未压缩长度
|
Length uint16 //未压缩长度
|
||||||
@@ -73,9 +73,9 @@ func Decode(bs []byte) (*Response, error) {
|
|||||||
}
|
}
|
||||||
resp := &Response{
|
resp := &Response{
|
||||||
Prefix: Uint32(bs[:4]),
|
Prefix: Uint32(bs[:4]),
|
||||||
Control: bs[4],
|
I2: bs[4],
|
||||||
MsgID: Uint32(bs[5:9]),
|
MsgID: Uint32(bs[5:9]),
|
||||||
I3: bs[9],
|
Control: bs[9],
|
||||||
Type: Uint16(bs[10:12]),
|
Type: Uint16(bs[10:12]),
|
||||||
ZipLength: Uint16(bs[12:14]),
|
ZipLength: Uint16(bs[12:14]),
|
||||||
Length: Uint16(bs[14:16]),
|
Length: Uint16(bs[14:16]),
|
||||||
@@ -86,15 +86,17 @@ func Decode(bs []byte) (*Response, error) {
|
|||||||
return nil, fmt.Errorf("压缩数据长度不匹配,预期%d,得到%d", resp.ZipLength+16, len(bs))
|
return nil, fmt.Errorf("压缩数据长度不匹配,预期%d,得到%d", resp.ZipLength+16, len(bs))
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := zlib.NewReader(bytes.NewReader(resp.Data))
|
//进行数据解压
|
||||||
if err != nil {
|
if resp.ZipLength != resp.Length {
|
||||||
return nil, err
|
r, err := zlib.NewReader(bytes.NewReader(resp.Data))
|
||||||
}
|
if err != nil {
|
||||||
defer r.Close()
|
return nil, err
|
||||||
|
}
|
||||||
resp.Data, err = io.ReadAll(r)
|
defer r.Close()
|
||||||
if err != nil {
|
resp.Data, err = io.ReadAll(r)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(resp.Length) != len(resp.Data) {
|
if int(resp.Length) != len(resp.Data) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ func TestFrame_Bytes(t *testing.T) {
|
|||||||
f := Frame{
|
f := Frame{
|
||||||
MsgID: 1,
|
MsgID: 1,
|
||||||
Control: 1,
|
Control: 1,
|
||||||
Type: Connect,
|
Type: TypeConnect,
|
||||||
Data: []byte{0x01},
|
Data: []byte{0x01},
|
||||||
}
|
}
|
||||||
hex := f.Bytes().HEX()
|
hex := f.Bytes().HEX()
|
||||||
@@ -47,7 +47,7 @@ func TestDecode(t *testing.T) {
|
|||||||
|
|
||||||
t.Log(string(UTF8ToGBK(resp.Data[68:])))
|
t.Log(string(UTF8ToGBK(resp.Data[68:])))
|
||||||
|
|
||||||
t.Log(DecodeConnect(resp.Data))
|
t.Log(MConnect.Decode(resp.Data))
|
||||||
|
|
||||||
//result, err := DecodeSecurityList(resp.Data)
|
//result, err := DecodeSecurityList(resp.Data)
|
||||||
//if err != nil {
|
//if err != nil {
|
||||||
|
|||||||
@@ -1,12 +1,31 @@
|
|||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
bytes2 "github.com/injoyai/base/bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
MConnect = connect{}
|
||||||
|
MSecurityQuote = securityQuote{}
|
||||||
|
SecurityList = securityList{}
|
||||||
|
)
|
||||||
|
|
||||||
type ConnectResp struct {
|
type ConnectResp struct {
|
||||||
Info string
|
Info string
|
||||||
}
|
}
|
||||||
|
|
||||||
func DecodeConnect(bs []byte) (*ConnectResp, error) {
|
type connect struct{}
|
||||||
|
|
||||||
|
func (connect) Frame() *Frame {
|
||||||
|
return &Frame{
|
||||||
|
Control: Control,
|
||||||
|
Type: TypeConnect,
|
||||||
|
Data: []byte{0x01},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (connect) Decode(bs []byte) (*ConnectResp, error) {
|
||||||
if len(bs) < 68 {
|
if len(bs) < 68 {
|
||||||
return nil, errors.New("数据长度不足")
|
return nil, errors.New("数据长度不足")
|
||||||
}
|
}
|
||||||
@@ -14,6 +33,12 @@ func DecodeConnect(bs []byte) (*ConnectResp, error) {
|
|||||||
return &ConnectResp{Info: string(UTF8ToGBK(bs[68:]))}, nil
|
return &ConnectResp{Info: string(UTF8ToGBK(bs[68:]))}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
type SecurityListResp struct {
|
type SecurityListResp struct {
|
||||||
Count uint16
|
Count uint16
|
||||||
List []*Security
|
List []*Security
|
||||||
@@ -27,7 +52,17 @@ type Security struct {
|
|||||||
PreClose float64
|
PreClose float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func DecodeSecurityList(bs []byte) (*SecurityListResp, error) {
|
type securityList struct{}
|
||||||
|
|
||||||
|
func (securityList) Frame() *Frame {
|
||||||
|
return &Frame{
|
||||||
|
Control: 0x01,
|
||||||
|
Type: TypeConnect,
|
||||||
|
Data: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (securityList) Decode(bs []byte) (*SecurityListResp, error) {
|
||||||
|
|
||||||
if len(bs) < 2 {
|
if len(bs) < 2 {
|
||||||
return nil, errors.New("数据长度不足")
|
return nil, errors.New("数据长度不足")
|
||||||
@@ -41,18 +76,75 @@ func DecodeSecurityList(bs []byte) (*SecurityListResp, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConnect() *Frame {
|
/*
|
||||||
return &Frame{
|
|
||||||
Control: Control,
|
|
||||||
Type: Connect,
|
|
||||||
Data: []byte{0x01},
|
*/
|
||||||
}
|
|
||||||
|
type SecurityQuotesResp []*SecurityQuote
|
||||||
|
|
||||||
|
type PriceLevel struct {
|
||||||
|
Price float64
|
||||||
|
Vol int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSecurityQuotes(m map[Exchange]string) (*Frame, error) {
|
type SecurityQuote struct {
|
||||||
|
Market uint8 // 市场
|
||||||
|
Code string // 代码
|
||||||
|
Active1 uint16 // 活跃度
|
||||||
|
Price float64 // 现价
|
||||||
|
LastClose float64 // 昨收
|
||||||
|
Open float64 // 开盘
|
||||||
|
High float64 // 最高
|
||||||
|
Low float64 // 最低
|
||||||
|
ServerTime string // 时间
|
||||||
|
ReversedBytes0 int // 保留(时间 ServerTime)
|
||||||
|
ReversedBytes1 int // 保留
|
||||||
|
Vol int // 总量
|
||||||
|
CurVol int // 现量
|
||||||
|
Amount float64 // 总金额
|
||||||
|
SVol int // 内盘
|
||||||
|
BVol int // 外盘
|
||||||
|
ReversedBytes2 int // 保留
|
||||||
|
ReversedBytes3 int // 保留
|
||||||
|
BidLevels []PriceLevel
|
||||||
|
AskLevels []PriceLevel
|
||||||
|
Bid1 float64
|
||||||
|
Ask1 float64
|
||||||
|
BidVol1 int
|
||||||
|
AskVol1 int
|
||||||
|
Bid2 float64
|
||||||
|
Ask2 float64
|
||||||
|
BidVol2 int
|
||||||
|
AskVol2 int
|
||||||
|
Bid3 float64
|
||||||
|
Ask3 float64
|
||||||
|
BidVol3 int
|
||||||
|
AskVol3 int
|
||||||
|
Bid4 float64
|
||||||
|
Ask4 float64
|
||||||
|
BidVol4 int
|
||||||
|
AskVol4 int
|
||||||
|
Bid5 float64
|
||||||
|
Ask5 float64
|
||||||
|
BidVol5 int
|
||||||
|
AskVol5 int
|
||||||
|
ReversedBytes4 uint16 // 保留
|
||||||
|
ReversedBytes5 int // 保留
|
||||||
|
ReversedBytes6 int // 保留
|
||||||
|
ReversedBytes7 int // 保留
|
||||||
|
ReversedBytes8 int // 保留
|
||||||
|
Rate float64 // 涨速
|
||||||
|
Active2 uint16 // 活跃度
|
||||||
|
}
|
||||||
|
|
||||||
|
type securityQuote struct{}
|
||||||
|
|
||||||
|
func (this securityQuote) Frame(m map[Exchange]string) (*Frame, error) {
|
||||||
f := &Frame{
|
f := &Frame{
|
||||||
Control: Control,
|
Control: Control,
|
||||||
Type: SecurityQuote,
|
Type: TypeSecurityQuote,
|
||||||
Data: []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
Data: []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,3 +160,28 @@ func NewSecurityQuotes(m map[Exchange]string) (*Frame, error) {
|
|||||||
|
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Decode
|
||||||
|
b1cb74000c02000000003e05af00af000136020000303030303031320bb2124c56105987e6d10cf212b78fa801ae01293dc54e8bd740acb8670086ca1e0001af36ba0c4102b467b6054203a68a0184094304891992114405862685108d0100000000e8ff320b0136303030303859098005464502468defd10cc005bed2668e05be15804d8ba12cb3b13a0083c3034100badc029d014201bc990384f70443029da503b7af074403a6e501b9db044504a6e2028dd5048d050000000000005909
|
||||||
|
*/
|
||||||
|
func (this securityQuote) Decode(bs []byte) (*SecurityQuotesResp, error) {
|
||||||
|
|
||||||
|
//前2字节是什么?
|
||||||
|
bs = bs[2:]
|
||||||
|
|
||||||
|
number := Uint16(bs[:2])
|
||||||
|
bs = bs[2:]
|
||||||
|
|
||||||
|
for i := uint16(0); i < number; i++ {
|
||||||
|
sec := SecurityQuote{
|
||||||
|
Market: bs[0],
|
||||||
|
Code: string(UTF8ToGBK(bytes2.Reverse(bs[1:7]))),
|
||||||
|
Active1: Uint16(bs[7:9]),
|
||||||
|
}
|
||||||
|
_ = sec
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ import (
|
|||||||
0c02000000011a001a003e05050000000000000002000030303030303101363030303038
|
0c02000000011a001a003e05050000000000000002000030303030303101363030303038
|
||||||
*/
|
*/
|
||||||
func TestNewSecurityQuotes(t *testing.T) {
|
func TestNewSecurityQuotes(t *testing.T) {
|
||||||
f, err := NewSecurityQuotes(map[Exchange]string{
|
f, err := MSecurityQuote.Frame(map[Exchange]string{
|
||||||
ExchangeSH: "000001",
|
ExchangeSH: "000001",
|
||||||
ExchangeSZ: "600008",
|
ExchangeSZ: "600008",
|
||||||
})
|
})
|
||||||
@@ -19,3 +20,20 @@ func TestNewSecurityQuotes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Log(f.Bytes().HEX())
|
t.Log(f.Bytes().HEX())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_securityQuote_Decode(t *testing.T) {
|
||||||
|
s := "b1cb74000c02000000003e05af00af000136020000303030303031320bb2124c56105987e6d10cf212b78fa801ae01293dc54e8bd740acb8670086ca1e0001af36ba0c4102b467b6054203a68a0184094304891992114405862685108d0100000000e8ff320b0136303030303859098005464502468defd10cc005bed2668e05be15804d8ba12cb3b13a0083c3034100badc029d014201bc990384f70443029da503b7af074403a6e501b9db044504a6e2028dd5048d050000000000005909"
|
||||||
|
bs, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f, err := Decode(bs)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Log(hex.EncodeToString(f.Data))
|
||||||
|
//SecurityQuote.Decode(f.Data)
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func String(bs []byte) string {
|
||||||
|
return string(bytes2.Reverse(bs))
|
||||||
|
}
|
||||||
|
|
||||||
func Bytes(n any) []byte {
|
func Bytes(n any) []byte {
|
||||||
return bytes2.Reverse(conv.Bytes(n))
|
return bytes2.Reverse(conv.Bytes(n))
|
||||||
}
|
}
|
||||||
@@ -27,3 +31,42 @@ func UTF8ToGBK(text []byte) []byte {
|
|||||||
content, _ := io.ReadAll(decoder)
|
content, _ := io.ReadAll(decoder)
|
||||||
return bytes.ReplaceAll(content, []byte{0x00}, []byte{})
|
return bytes.ReplaceAll(content, []byte{0x00}, []byte{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getprice(b []byte, pos *int) int {
|
||||||
|
/*
|
||||||
|
0x7f与常量做与运算实质是保留常量(转换为二进制形式)的后7位数,既取值区间为[0,127]
|
||||||
|
0x3f与常量做与运算实质是保留常量(转换为二进制形式)的后6位数,既取值区间为[0,63]
|
||||||
|
|
||||||
|
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 {
|
||||||
|
for {
|
||||||
|
*pos += 1
|
||||||
|
bData = b[*pos]
|
||||||
|
data += (int(bData&0x7f) << posByte)
|
||||||
|
|
||||||
|
posByte += 7
|
||||||
|
|
||||||
|
if (bData & 0x80) <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*pos++
|
||||||
|
|
||||||
|
if bSign {
|
||||||
|
data = -data
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user