解析了5档价格,还未和实际校对

This commit is contained in:
钱纯净
2024-10-23 23:36:56 +08:00
parent ad6225ac22
commit f00bc3099e
6 changed files with 309 additions and 40 deletions

View File

@@ -13,35 +13,41 @@ import (
) )
// Dial 与服务器建立连接 // Dial 与服务器建立连接
func Dial(addr string, op ...client.Option) (*Client, error) { func Dial(addr string, op ...client.Option) (cli *Client, err error) {
c, err := dial.TCP(addr, func(c *client.Client) {
cli = &Client{
w: wait.New(time.Second * 2),
}
cli.c, err = dial.TCP(addr, func(c *client.Client) {
c.Logger.WithHEX() //以HEX显示 c.Logger.WithHEX() //以HEX显示
c.SetOption(op...) //自定义选项 c.SetOption(op...) //自定义选项
//c.Event.OnReadFrom = protocol.ReadFrom //分包 //c.Event.OnReadFrom = protocol.ReadFrom //分包
c.Event.OnDealMessage = handlerDealMessage //处理分包数据 c.Event.OnDealMessage = cli.handlerDealMessage //处理分包数据
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
go c.Run() go cli.c.Run()
cli := &Client{
c: c,
w: wait.New(time.Second * 2),
}
err = cli.connect() err = cli.connect()
if err != nil { if err != nil {
c.Close() cli.c.Close()
return nil, err return nil, err
} }
return cli, err return cli, err
} }
type Client struct {
c *client.Client
w *wait.Entity
msgID uint32
}
// handlerDealMessage 处理服务器响应的数据 // handlerDealMessage 处理服务器响应的数据
func handlerDealMessage(c *client.Client, msg ios.Acker) { func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
f, err := protocol.Decode(msg.Payload()) f, err := protocol.Decode(msg.Payload())
if err != nil { if err != nil {
@@ -49,14 +55,18 @@ func handlerDealMessage(c *client.Client, msg ios.Acker) {
return return
} }
_ = f logs.Debug(f.Type)
switch f.Type {
case protocol.TypeSecurityQuote:
resp := protocol.MSecurityQuote.Decode(f.Data)
logs.Debug(resp)
this.w.Done(conv.String(f.MsgID), resp)
return
} }
type Client struct { _ = f
c *client.Client
w *wait.Entity
msgID uint32
} }
func (this *Client) SendFrame(f *protocol.Frame) (any, error) { func (this *Client) SendFrame(f *protocol.Frame) (any, error) {
@@ -111,7 +121,7 @@ func (this *Client) GetSecurityList() (*protocol.SecurityListResp, error) {
} }
// GetSecurityQuotes 获取盘口五档报价 // GetSecurityQuotes 获取盘口五档报价
func (this *Client) GetSecurityQuotes(m map[protocol.Exchange]string) (*protocol.SecurityQuotesResp, error) { func (this *Client) GetSecurityQuotes(m map[protocol.Exchange]string) (protocol.SecurityQuotesResp, error) {
f, err := protocol.MSecurityQuote.Frame(m) f, err := protocol.MSecurityQuote.Frame(m)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -120,5 +130,5 @@ func (this *Client) GetSecurityQuotes(m map[protocol.Exchange]string) (*protocol
if err != nil { if err != nil {
return nil, err return nil, err
} }
return result.(*protocol.SecurityQuotesResp), nil return result.(protocol.SecurityQuotesResp), nil
} }

View File

@@ -9,6 +9,7 @@ import (
"github.com/injoyai/ios/server/listen" "github.com/injoyai/ios/server/listen"
"github.com/injoyai/logs" "github.com/injoyai/logs"
"github.com/injoyai/tdx" "github.com/injoyai/tdx"
"github.com/injoyai/tdx/protocol"
) )
func main() { func main() {
@@ -26,8 +27,15 @@ func main() {
b1cb74001c00000000000d005100bd00789c6378c1cecb252ace6066c5b4898987b9050ed1f90cc5b74c18a5bc18c1b43490fecff09c81819191f13fc3c9f3bb169f5e7dfefeb5ef57f7199a305009308208e5b32bb6bcbf70148712002d7f1e13 b1cb74001c00000000000d005100bd00789c6378c1cecb252ace6066c5b4898987b9050ed1f90cc5b74c18a5bc18c1b43490fecff09c81819191f13fc3c9f3bb169f5e7dfefeb5ef57f7199a305009308208e5b32bb6bcbf70148712002d7f1e13
b1cb74000c02000000003e05ac00ac000102020000303030303031601294121a1c2d4eadabcf0ed412aae5fc01afb0024561124fbcc08301afa47900b2e3174100bf68871a4201b741b6144302bb09af334403972e96354504ac09b619560e00000000f8ff601201363030303038b60fba04060607429788a70efa04ada37ab2531c12974d91e7449dbc354184b6010001844bad324102b5679ea1014203a65abd8d0143048a6ba4dd01440587e101b3d2029613000000000000b60f b1cb74000c02000000003e05ac00ac000102020000303030303031601294121a1c2d4eadabcf0ed412aae5fc01afb0024561124fbcc08301afa47900b2e3174100bf68871a4201b741b6144302bb09af334403972e96354504ac09b619560e00000000f8ff601201363030303038b60fba04060607429788a70efa04ada37ab2531c12974d91e7449dbc354184b6010001844bad324102b5679ea1014203a65abd8d0143048a6ba4dd01440587e101b3d2029613000000000000b60f
*/ */
_, err = c.GetSecurityList() resp, err := c.GetSecurityQuotes(map[protocol.Exchange]string{
logs.PanicErr(err) protocol.ExchangeSH: "000001",
protocol.ExchangeSZ: "600008",
})
logs.PrintErr(err)
for _, v := range resp {
logs.Debug(*v)
}
select {} select {}

View File

@@ -2,6 +2,7 @@ package protocol
import ( import (
"errors" "errors"
"fmt"
bytes2 "github.com/injoyai/base/bytes" bytes2 "github.com/injoyai/base/bytes"
) )
@@ -84,20 +85,17 @@ func (securityList) Decode(bs []byte) (*SecurityListResp, error) {
type SecurityQuotesResp []*SecurityQuote type SecurityQuotesResp []*SecurityQuote
type PriceLevel struct {
Price float64
Vol int
}
type SecurityQuote struct { type SecurityQuote struct {
Market uint8 // 市场 Market uint8 // 市场
Code string // 代码 Code string // 代码
Active1 uint16 // 活跃度 Active1 uint16 // 活跃度
Price float64 // 现价 //Price float64 // 现价
LastClose float64 // 昨收 //Close float64 // 昨收
Open float64 // 开盘 //Open float64 // 开盘
High float64 // 最高 //High float64 // 最高
Low float64 // 最低 //Low float64 // 最低
K K //k线
ServerTime string // 时间 ServerTime string // 时间
ReversedBytes0 int // 保留(时间 ServerTime) ReversedBytes0 int // 保留(时间 ServerTime)
ReversedBytes1 int // 保留 ReversedBytes1 int // 保留
@@ -108,8 +106,8 @@ type SecurityQuote struct {
BVol int // 外盘 BVol int // 外盘
ReversedBytes2 int // 保留 ReversedBytes2 int // 保留
ReversedBytes3 int // 保留 ReversedBytes3 int // 保留
BidLevels []PriceLevel BidLevels [5]PriceLevel
AskLevels []PriceLevel AskLevels [5]PriceLevel
Bid1 float64 Bid1 float64
Ask1 float64 Ask1 float64
BidVol1 int BidVol1 int
@@ -135,6 +133,7 @@ type SecurityQuote struct {
ReversedBytes6 int // 保留 ReversedBytes6 int // 保留
ReversedBytes7 int // 保留 ReversedBytes7 int // 保留
ReversedBytes8 int // 保留 ReversedBytes8 int // 保留
ReversedBytes9 uint16 // 保留
Rate float64 // 涨速 Rate float64 // 涨速
Active2 uint16 // 活跃度 Active2 uint16 // 活跃度
} }
@@ -165,7 +164,9 @@ func (this securityQuote) Frame(m map[Exchange]string) (*Frame, error) {
Decode Decode
b1cb74000c02000000003e05af00af000136020000303030303031320bb2124c56105987e6d10cf212b78fa801ae01293dc54e8bd740acb8670086ca1e0001af36ba0c4102b467b6054203a68a0184094304891992114405862685108d0100000000e8ff320b0136303030303859098005464502468defd10cc005bed2668e05be15804d8ba12cb3b13a0083c3034100badc029d014201bc990384f70443029da503b7af074403a6e501b9db044504a6e2028dd5048d050000000000005909 b1cb74000c02000000003e05af00af000136020000303030303031320bb2124c56105987e6d10cf212b78fa801ae01293dc54e8bd740acb8670086ca1e0001af36ba0c4102b467b6054203a68a0184094304891992114405862685108d0100000000e8ff320b0136303030303859098005464502468defd10cc005bed2668e05be15804d8ba12cb3b13a0083c3034100badc029d014201bc990384f70443029da503b7af074403a6e501b9db044504a6e2028dd5048d050000000000005909
*/ */
func (this securityQuote) Decode(bs []byte) (*SecurityQuotesResp, error) { func (this securityQuote) Decode(bs []byte) SecurityQuotesResp {
resp := SecurityQuotesResp{}
//前2字节是什么? //前2字节是什么?
bs = bs[2:] bs = bs[2:]
@@ -174,14 +175,50 @@ func (this securityQuote) Decode(bs []byte) (*SecurityQuotesResp, error) {
bs = bs[2:] bs = bs[2:]
for i := uint16(0); i < number; i++ { for i := uint16(0); i < number; i++ {
sec := SecurityQuote{ sec := &SecurityQuote{
Market: bs[0], Market: bs[0],
Code: string(UTF8ToGBK(bytes2.Reverse(bs[1:7]))), Code: string(UTF8ToGBK(bytes2.Reverse(bs[1:7]))),
Active1: Uint16(bs[7:9]), Active1: Uint16(bs[7:9]),
} }
_ = sec bs, sec.K = DecodeK(bs[9:])
bs, sec.ReversedBytes0 = CutInt(bs)
sec.ServerTime = fmt.Sprintf("%d", sec.ReversedBytes0)
bs, sec.ReversedBytes1 = CutInt(bs)
bs, sec.Vol = CutInt(bs)
bs, sec.CurVol = CutInt(bs)
sec.Amount = getVolume(Uint32(bs[:4]))
bs, sec.SVol = CutInt(bs[4:])
bs, sec.BVol = CutInt(bs)
bs, sec.ReversedBytes2 = CutInt(bs)
bs, sec.ReversedBytes3 = CutInt(bs)
var p Price
for i := 0; i < 5; i++ {
bidele := PriceLevel{}
bs, p = GetPrice(bs)
bidele.Price = p + sec.K.Close
bs, bidele.Vol = CutInt(bs)
sec.BidLevels[i] = bidele
offerele := PriceLevel{}
bs, p = GetPrice(bs)
offerele.Price = p + sec.K.Close
bs, offerele.Vol = CutInt(bs)
sec.AskLevels[i] = offerele
} }
return nil, nil sec.ReversedBytes4 = Uint16(bs[:2])
bs, sec.ReversedBytes5 = CutInt(bs[2:])
bs, sec.ReversedBytes6 = CutInt(bs)
bs, sec.ReversedBytes7 = CutInt(bs)
bs, sec.ReversedBytes8 = CutInt(bs)
sec.ReversedBytes9 = Uint16(bs[:2])
sec.Rate = float64(sec.ReversedBytes9) / 100
sec.Active2 = Uint16(bs[2:4])
resp = append(resp, sec)
}
return resp
} }

195
protocol/model_k.go Normal file
View File

@@ -0,0 +1,195 @@
package protocol
import (
"fmt"
"math"
)
// Price 价格,单位分
type Price int32
func (this Price) Float64() float64 {
return float64(this) / 100
}
func (this Price) String() string {
return fmt.Sprintf("%.2f 元", this.Float64())
}
type PriceLevel struct {
Price Price
Vol int
}
// K k线图
type K struct {
Last Price //昨天收盘价
Open Price //今日开盘价
High Price //今日最高价
Low Price //今日最低价
Close Price //今日收盘价
}
func DecodeK(bs []byte) ([]byte, K) {
k := K{}
//当日收盘价
bs, k.Close = GetPrice(bs)
//前日收盘价
bs, k.Last = GetPrice(bs)
k.Last += k.Close
//当日开盘价
bs, k.Open = GetPrice(bs)
k.Open += k.Close
//当日最高价
bs, k.High = GetPrice(bs)
k.High += k.Close
//当日最低价
bs, k.Low = GetPrice(bs)
k.Low += k.Close
return bs, k
}
func GetPrice(bs []byte) ([]byte, Price) {
for i := range bs {
if bs[i]&0x80 == 0 {
return bs[i+1:], getPrice(bs[:i+1])
}
}
return bs, 0
}
/*
字节的第一位表示后续是否有数据(字节)
第一字节 的第二位表示正负 1负0正 有效数据为后6位
后续字节 的有效数据为后7位
最大长度未知
0x20说明有后续数据
*/
func getPrice(bs []byte) (data Price) {
for i := range bs {
switch i {
case 0:
//取后6位
data += Price(int32(bs[0] & 0x3F))
default:
//取后7位
data += Price(int32(bs[i]&0x7F) << uint8(6+(i-1)*7))
}
//判断是否有后续数据
if bs[i]&0x80 == 0 {
break
}
}
//第一字节的第二位为1表示为负数
if len(bs) > 0 && bs[0]&0x40 > 0 {
data = -data
}
return
}
func CutInt(bs []byte) ([]byte, int) {
for i := range bs {
if bs[i]&0x80 == 0 {
return bs[i+1:], getData(bs[:i+1])
}
}
return bs, 0
}
func getData(bs []byte) (data int) {
for i := range bs {
switch i {
case 0:
//取后6位
data += int(bs[0] & 0x3F)
default:
//取后7位
data += int(bs[i]&0x7F) << uint8(6+(i-1)*7)
}
//判断是否有后续数据
if bs[i]&0x80 == 0 {
break
}
}
//第一字节的第二位为1表示为负数
if len(bs) > 0 && bs[0]&0x40 > 0 {
data = -data
}
return
}
func getVolume(ivol uint32) (volume float64) {
logpoint := ivol >> (8 * 3)
//hheax := ivol >> (8 * 3) // [3]
hleax := (ivol >> (8 * 2)) & 0xff // [2]
lheax := (ivol >> 8) & 0xff //[1]
lleax := ivol & 0xff //[0]
//dbl_1 := 1.0
//dbl_2 := 2.0
//dbl_128 := 128.0
dwEcx := logpoint*2 - 0x7f
dwEdx := logpoint*2 - 0x86
dwEsi := logpoint*2 - 0x8e
dwEax := logpoint*2 - 0x96
tmpEax := dwEcx
if dwEcx < 0 {
tmpEax = -dwEcx
} else {
tmpEax = dwEcx
}
dbl_xmm6 := 0.0
dbl_xmm6 = math.Pow(2.0, float64(tmpEax))
if dwEcx < 0 {
dbl_xmm6 = 1.0 / dbl_xmm6
}
dbl_xmm4 := 0.0
dbl_xmm0 := 0.0
if hleax > 0x80 {
tmpdbl_xmm3 := 0.0
//tmpdbl_xmm1 := 0.0
dwtmpeax := dwEdx + 1
tmpdbl_xmm3 = math.Pow(2.0, float64(dwtmpeax))
dbl_xmm0 = math.Pow(2.0, float64(dwEdx)) * 128.0
dbl_xmm0 += float64(hleax&0x7f) * tmpdbl_xmm3
dbl_xmm4 = dbl_xmm0
} else {
if dwEdx >= 0 {
dbl_xmm0 = math.Pow(2.0, float64(dwEdx)) * float64(hleax)
} else {
dbl_xmm0 = (1 / math.Pow(2.0, float64(dwEdx))) * float64(hleax)
}
dbl_xmm4 = dbl_xmm0
}
dbl_xmm3 := math.Pow(2.0, float64(dwEsi)) * float64(lheax)
dbl_xmm1 := math.Pow(2.0, float64(dwEax)) * float64(lleax)
if (hleax & 0x80) > 0 {
dbl_xmm3 *= 2.0
dbl_xmm1 *= 2.0
}
volume = dbl_xmm6 + dbl_xmm4 + dbl_xmm3 + dbl_xmm1
return
}

14
protocol/model_k_test.go Normal file
View File

@@ -0,0 +1,14 @@
package protocol
import (
"math"
"testing"
)
func Test_getVolume(t *testing.T) {
f := float32(1.03)
n := math.Float32bits(f)
t.Log(n)
t.Log(getVolume(n))
}

View File

@@ -37,3 +37,8 @@ func Test_securityQuote_Decode(t *testing.T) {
//SecurityQuote.Decode(f.Data) //SecurityQuote.Decode(f.Data)
} }
func Test_getPrice(t *testing.T) {
t.Log(getPrice([]byte{0x7f, 0x3f, 0x40, 0x3f, 0x01})) //预期-63
t.Log(getPrice([]byte{0x2f, 0x3f, 0x40, 0x3f, 0x01})) //预期47
}