mirror of
https://github.com/injoyai/tdx.git
synced 2025-11-26 21:25:35 +08:00
解析了5档价格,还未和实际校对
This commit is contained in:
46
client.go
46
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,14 +55,18 @@ func handlerDealMessage(c *client.Client, msg ios.Acker) {
|
||||
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 {
|
||||
c *client.Client
|
||||
w *wait.Entity
|
||||
msgID uint32
|
||||
_ = f
|
||||
|
||||
}
|
||||
|
||||
func (this *Client) SendFrame(f *protocol.Frame) (any, error) {
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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 // 最低
|
||||
//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
|
||||
}
|
||||
|
||||
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
195
protocol/model_k.go
Normal 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
14
protocol/model_k_test.go
Normal 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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user