diff --git a/client.go b/client.go index 2d6ebde..41d4437 100644 --- a/client.go +++ b/client.go @@ -13,35 +13,41 @@ import ( ) // Dial 与服务器建立连接 -func Dial(addr string, op ...client.Option) (*Client, error) { - c, err := dial.TCP(addr, func(c *client.Client) { +func Dial(addr string, op ...client.Option) (cli *Client, err error) { + + cli = &Client{ + w: wait.New(time.Second * 2), + } + + cli.c, err = dial.TCP(addr, func(c *client.Client) { c.Logger.WithHEX() //以HEX显示 c.SetOption(op...) //自定义选项 //c.Event.OnReadFrom = protocol.ReadFrom //分包 - c.Event.OnDealMessage = handlerDealMessage //处理分包数据 + c.Event.OnDealMessage = cli.handlerDealMessage //处理分包数据 }) if err != nil { return nil, err } - go c.Run() - - cli := &Client{ - c: c, - w: wait.New(time.Second * 2), - } + go cli.c.Run() err = cli.connect() if err != nil { - c.Close() + cli.c.Close() return nil, err } return cli, err } +type Client struct { + c *client.Client + w *wait.Entity + msgID uint32 +} + // 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()) if err != nil { @@ -49,16 +55,20 @@ func handlerDealMessage(c *client.Client, msg ios.Acker) { return } + 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 + + } + _ = f } -type Client struct { - c *client.Client - w *wait.Entity - msgID uint32 -} - func (this *Client) SendFrame(f *protocol.Frame) (any, error) { this.msgID++ f.MsgID = this.msgID @@ -111,7 +121,7 @@ func (this *Client) GetSecurityList() (*protocol.SecurityListResp, error) { } // 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) if err != nil { return nil, err @@ -120,5 +130,5 @@ func (this *Client) GetSecurityQuotes(m map[protocol.Exchange]string) (*protocol if err != nil { return nil, err } - return result.(*protocol.SecurityQuotesResp), nil + return result.(protocol.SecurityQuotesResp), nil } diff --git a/example/test/main.go b/example/test/main.go index dbcc554..7e4857d 100644 --- a/example/test/main.go +++ b/example/test/main.go @@ -9,6 +9,7 @@ import ( "github.com/injoyai/ios/server/listen" "github.com/injoyai/logs" "github.com/injoyai/tdx" + "github.com/injoyai/tdx/protocol" ) func main() { @@ -26,8 +27,15 @@ func main() { b1cb74001c00000000000d005100bd00789c6378c1cecb252ace6066c5b4898987b9050ed1f90cc5b74c18a5bc18c1b43490fecff09c81819191f13fc3c9f3bb169f5e7dfefeb5ef57f7199a305009308208e5b32bb6bcbf70148712002d7f1e13 b1cb74000c02000000003e05ac00ac000102020000303030303031601294121a1c2d4eadabcf0ed412aae5fc01afb0024561124fbcc08301afa47900b2e3174100bf68871a4201b741b6144302bb09af334403972e96354504ac09b619560e00000000f8ff601201363030303038b60fba04060607429788a70efa04ada37ab2531c12974d91e7449dbc354184b6010001844bad324102b5679ea1014203a65abd8d0143048a6ba4dd01440587e101b3d2029613000000000000b60f */ - _, err = c.GetSecurityList() - logs.PanicErr(err) + resp, err := c.GetSecurityQuotes(map[protocol.Exchange]string{ + protocol.ExchangeSH: "000001", + protocol.ExchangeSZ: "600008", + }) + logs.PrintErr(err) + + for _, v := range resp { + logs.Debug(*v) + } select {} diff --git a/protocol/model.go b/protocol/model.go index b4b78c4..6969c1a 100644 --- a/protocol/model.go +++ b/protocol/model.go @@ -2,6 +2,7 @@ package protocol import ( "errors" + "fmt" bytes2 "github.com/injoyai/base/bytes" ) @@ -84,20 +85,17 @@ func (securityList) Decode(bs []byte) (*SecurityListResp, error) { type SecurityQuotesResp []*SecurityQuote -type PriceLevel struct { - Price float64 - Vol int -} - type SecurityQuote struct { - Market uint8 // 市场 - Code string // 代码 - Active1 uint16 // 活跃度 - Price float64 // 现价 - LastClose float64 // 昨收 - Open float64 // 开盘 - High float64 // 最高 - Low float64 // 最低 + Market uint8 // 市场 + Code string // 代码 + Active1 uint16 // 活跃度 + //Price float64 // 现价 + //Close float64 // 昨收 + //Open float64 // 开盘 + //High float64 // 最高 + //Low float64 // 最低 + K K //k线 + ServerTime string // 时间 ReversedBytes0 int // 保留(时间 ServerTime) ReversedBytes1 int // 保留 @@ -108,8 +106,8 @@ type SecurityQuote struct { BVol int // 外盘 ReversedBytes2 int // 保留 ReversedBytes3 int // 保留 - BidLevels []PriceLevel - AskLevels []PriceLevel + BidLevels [5]PriceLevel + AskLevels [5]PriceLevel Bid1 float64 Ask1 float64 BidVol1 int @@ -135,6 +133,7 @@ type SecurityQuote struct { ReversedBytes6 int // 保留 ReversedBytes7 int // 保留 ReversedBytes8 int // 保留 + ReversedBytes9 uint16 // 保留 Rate float64 // 涨速 Active2 uint16 // 活跃度 } @@ -165,7 +164,9 @@ func (this securityQuote) Frame(m map[Exchange]string) (*Frame, error) { Decode b1cb74000c02000000003e05af00af000136020000303030303031320bb2124c56105987e6d10cf212b78fa801ae01293dc54e8bd740acb8670086ca1e0001af36ba0c4102b467b6054203a68a0184094304891992114405862685108d0100000000e8ff320b0136303030303859098005464502468defd10cc005bed2668e05be15804d8ba12cb3b13a0083c3034100badc029d014201bc990384f70443029da503b7af074403a6e501b9db044504a6e2028dd5048d050000000000005909 */ -func (this securityQuote) Decode(bs []byte) (*SecurityQuotesResp, error) { +func (this securityQuote) Decode(bs []byte) SecurityQuotesResp { + + resp := SecurityQuotesResp{} //前2字节是什么? bs = bs[2:] @@ -174,14 +175,50 @@ func (this securityQuote) Decode(bs []byte) (*SecurityQuotesResp, error) { bs = bs[2:] for i := uint16(0); i < number; i++ { - sec := SecurityQuote{ + sec := &SecurityQuote{ Market: bs[0], Code: string(UTF8ToGBK(bytes2.Reverse(bs[1:7]))), 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 + } + + 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 nil, nil + return resp } diff --git a/protocol/model_k.go b/protocol/model_k.go new file mode 100644 index 0000000..be847a4 --- /dev/null +++ b/protocol/model_k.go @@ -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 +} diff --git a/protocol/model_k_test.go b/protocol/model_k_test.go new file mode 100644 index 0000000..c309d9f --- /dev/null +++ b/protocol/model_k_test.go @@ -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)) +} diff --git a/protocol/model_test.go b/protocol/model_test.go index 1a9474f..5fe015a 100644 --- a/protocol/model_test.go +++ b/protocol/model_test.go @@ -37,3 +37,8 @@ func Test_securityQuote_Decode(t *testing.T) { //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 +}