增加获取所有股票代码的接口

This commit is contained in:
injoyai
2024-10-28 14:15:37 +08:00
parent ec6e93d845
commit c5cfc2bc98
13 changed files with 84 additions and 81 deletions

View File

@@ -6,4 +6,6 @@
* 能通讯,有响应数据,还未解析响应数据 * 能通讯,有响应数据,还未解析响应数据
![](docs/plan.png) ![](docs/plan.png)
* 基本信息(5档报价) * 基本信息(5档报价)
![](docs/plan20241025.png) ![](docs/plan20241025.png)
* 基本信息(股票列表)
![](docs/plan20241028.png)

View File

@@ -19,20 +19,15 @@ func Dial(addr string, op ...client.Option) (cli *Client, err error) {
} }
cli.c, err = dial.TCP(addr, func(c *client.Client) { 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.AllReader = ios.NewAllReader(c.Reader.(io.Reader), protocol.ReadFrom) //分包
c.Event.OnReadFrom = protocol.ReadFrom //分包 c.Event.OnReadFrom = protocol.ReadFrom //分包
c.Event.OnDealMessage = cli.handlerDealMessage //处理分包数据 c.Event.OnDealMessage = cli.handlerDealMessage //处理分包数据
logs.Debug("option")
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
logs.Debug("run")
logs.Debugf("%#v\n", cli.c.Event.OnReadFrom)
go cli.c.Run() go cli.c.Run()
err = cli.connect() err = cli.connect()
@@ -62,10 +57,10 @@ func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
var resp any var resp any
switch f.Type { switch f.Type {
case protocol.TypeSecurityQuote: case protocol.TypeSecurityQuote:
resp = protocol.MSecurityQuote.Decode(f.Data) resp = protocol.MStockQuote.Decode(f.Data)
case protocol.TypeSecurityList: case protocol.TypeSecurityList:
resp, err = protocol.MSecurityList.Decode(f.Data) resp, err = protocol.MStockList.Decode(f.Data)
} }
@@ -74,7 +69,6 @@ func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
return return
} }
logs.Debug(resp)
this.w.Done(conv.String(f.MsgID), resp) this.w.Done(conv.String(f.MsgID), resp)
} }
@@ -112,19 +106,19 @@ func (this *Client) connect() error {
} }
// GetSecurityList 获取市场内指定范围内的所有证券代码 // GetSecurityList 获取市场内指定范围内的所有证券代码
func (this *Client) GetSecurityList(exchange protocol.Exchange, starts ...uint16) (*protocol.SecurityListResp, error) { func (this *Client) GetSecurityList(exchange protocol.Exchange, starts ...uint16) (*protocol.StockListResp, error) {
f := protocol.MSecurityList.Frame(exchange, starts...) f := protocol.MStockList.Frame(exchange, starts...)
result, err := this.SendFrame(f) result, err := this.SendFrame(f)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return result.(*protocol.SecurityListResp), nil return result.(*protocol.StockListResp), nil
} }
// GetSecurityQuotes 获取盘口五档报价 // GetSecurityQuotes 获取盘口五档报价
func (this *Client) GetSecurityQuotes(m map[protocol.Exchange]string) (protocol.SecurityQuotesResp, error) { func (this *Client) GetSecurityQuotes(m map[protocol.Exchange]string) (protocol.StockQuotesResp, error) {
f, err := protocol.MSecurityQuote.Frame(m) f, err := protocol.MStockQuote.Frame(m)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -132,5 +126,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.StockQuotesResp), nil
} }

BIN
docs/plan20241025.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
docs/plan20241028.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

View File

@@ -1,12 +1,6 @@
package main package main
import ( import (
"bytes"
"encoding/binary"
"github.com/injoyai/goutil/g"
"github.com/injoyai/ios/client"
"github.com/injoyai/ios/server"
"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" "github.com/injoyai/tdx/protocol"
@@ -40,20 +34,3 @@ func main() {
select {} select {}
} }
// 监听第三方包发送的数据,确定协议用
func _listen() {
listen.RunTCP(7709, func(s *server.Server) {
s.SetClientOption(func(c *client.Client) {
c.Logger.WithHEX()
})
})
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, g.Map{
"name": "名称",
"age": 17,
})
logs.PrintErr(err)
logs.Debug(buf.String())
}

View File

@@ -14,7 +14,7 @@ func main() {
logs.PrintErr(err) logs.PrintErr(err)
for _, v := range resp.List { for _, v := range resp.List {
logs.Debugf("%#v\n", v) logs.Debug(v)
} }
select {} select {}

2
go.mod
View File

@@ -6,7 +6,7 @@ require (
github.com/injoyai/base v1.0.18 github.com/injoyai/base v1.0.18
github.com/injoyai/conv v1.1.10 github.com/injoyai/conv v1.1.10
github.com/injoyai/goutil v0.0.0-20241009040015-3d20afe3efe6 github.com/injoyai/goutil v0.0.0-20241009040015-3d20afe3efe6
github.com/injoyai/ios v0.0.3 github.com/injoyai/ios v0.0.4
github.com/injoyai/logs v1.0.9 github.com/injoyai/logs v1.0.9
golang.org/x/text v0.16.0 golang.org/x/text v0.16.0
) )

4
go.sum
View File

@@ -1068,8 +1068,8 @@ github.com/injoyai/goutil v0.0.0-20241009040015-3d20afe3efe6 h1:ShXZNuV2tQOD9xlz
github.com/injoyai/goutil v0.0.0-20241009040015-3d20afe3efe6/go.mod h1:lXrP8bGSoueX7sBniT5pZctp1e2z3cVZYVeRrjQSKoc= github.com/injoyai/goutil v0.0.0-20241009040015-3d20afe3efe6/go.mod h1:lXrP8bGSoueX7sBniT5pZctp1e2z3cVZYVeRrjQSKoc=
github.com/injoyai/io v0.1.8 h1:C+pt2LLUNhCu80EVuzCs6+vetHf6Ny5ANAIOH8QUAco= github.com/injoyai/io v0.1.8 h1:C+pt2LLUNhCu80EVuzCs6+vetHf6Ny5ANAIOH8QUAco=
github.com/injoyai/io v0.1.8/go.mod h1:khts/5VvSJVm8Dl1fp0C+D8L0pmRrXtWsp+SJqlTgpw= github.com/injoyai/io v0.1.8/go.mod h1:khts/5VvSJVm8Dl1fp0C+D8L0pmRrXtWsp+SJqlTgpw=
github.com/injoyai/ios v0.0.3 h1:AAwcMbm9/eBNOT1rqdbvwMlZQFxNCyMUfDDQQYTK7m4= github.com/injoyai/ios v0.0.4 h1:yEZ6wN5uCSjAJB4qwpT6R77aMtjkblZo4giKwu9/s7Y=
github.com/injoyai/ios v0.0.3/go.mod h1:heABkaIUwoRRe424otl6mKgdU6LYt5gadav/V4gaojA= github.com/injoyai/ios v0.0.4/go.mod h1:heABkaIUwoRRe424otl6mKgdU6LYt5gadav/V4gaojA=
github.com/injoyai/logs v1.0.7/go.mod h1:CLchJCGhb39Obyrci816R+KMtbxZhgPs0FuikhyixK4= github.com/injoyai/logs v1.0.7/go.mod h1:CLchJCGhb39Obyrci816R+KMtbxZhgPs0FuikhyixK4=
github.com/injoyai/logs v1.0.9 h1:Wq7rCVIQKcPx+z+lzKQb2qyDK4TML/cgmaSZN9tx33c= github.com/injoyai/logs v1.0.9 h1:Wq7rCVIQKcPx+z+lzKQb2qyDK4TML/cgmaSZN9tx33c=
github.com/injoyai/logs v1.0.9/go.mod h1:CLchJCGhb39Obyrci816R+KMtbxZhgPs0FuikhyixK4= github.com/injoyai/logs v1.0.9/go.mod h1:CLchJCGhb39Obyrci816R+KMtbxZhgPs0FuikhyixK4=

View File

@@ -7,7 +7,6 @@ import (
"github.com/injoyai/base/bytes" "github.com/injoyai/base/bytes"
"github.com/injoyai/base/g" "github.com/injoyai/base/g"
"github.com/injoyai/conv" "github.com/injoyai/conv"
"github.com/injoyai/logs"
"io" "io"
) )
@@ -16,7 +15,7 @@ const (
Prefix = 0x0c Prefix = 0x0c
// PrefixResp 响应帧头 // PrefixResp 响应帧头
PrefixResp = 0xb1cb74 PrefixResp = 0xb1cb7400
) )
type Message interface { type Message interface {
@@ -109,7 +108,7 @@ func Decode(bs []byte) (*Response, error) {
// ReadFrom 这里的r推荐传入*bufio.Reader // ReadFrom 这里的r推荐传入*bufio.Reader
func ReadFrom(r io.Reader) (result []byte, err error) { func ReadFrom(r io.Reader) (result []byte, err error) {
logs.Debug("ReadFrom")
prefix := make([]byte, 4) prefix := make([]byte, 4)
for { for {
result = []byte(nil) result = []byte(nil)
@@ -133,8 +132,7 @@ func ReadFrom(r io.Reader) (result []byte, err error) {
result = append(result, buf...) result = append(result, buf...)
//获取后续字节长度 //获取后续字节长度
length := uint16(result[11])<<8 + uint16(result[10]) length := uint16(result[13])<<8 + uint16(result[12])
logs.Debug("长度:", length)
buf = make([]byte, length) buf = make([]byte, length)
_, err = io.ReadFull(r, buf) _, err = io.ReadFull(r, buf)
if err != nil { if err != nil {

View File

@@ -1,6 +1,7 @@
package protocol package protocol
import ( import (
"bytes"
"encoding/hex" "encoding/hex"
"testing" "testing"
) )
@@ -59,3 +60,25 @@ func TestDecode(t *testing.T) {
// //
//t.Log(result) //t.Log(result)
} }
func TestReadFrom(t *testing.T) {
s := "b1cb74001c00000000000d005100bd00789c6378c12ec325c7cb2061c5b4898987b9050ed1f90c2db74c1825bd18c1b42890fecff09c81819191f13fc3c9f3bb169f5e7dfefeb5ef57f7199a305009308208e5b32bb6bcbf701487120031c61e1e"
bs, err := hex.DecodeString(s)
if err != nil {
t.Error(err)
return
}
result, err := ReadFrom(bytes.NewReader(bs))
if err != nil {
t.Error(err)
return
}
t.Log(hex.EncodeToString(result))
resp, err := Decode(result)
if err != nil {
t.Error(err)
return
}
t.Log(hex.EncodeToString(resp.Data))
t.Log(string(resp.Data))
}

View File

@@ -8,9 +8,9 @@ import (
) )
var ( var (
MConnect = connect{} MConnect = connect{}
MSecurityQuote = securityQuote{} MStockQuote = stockQuote{}
MSecurityList = securityList{} MStockList = stockList{}
) )
type ConnectResp struct { type ConnectResp struct {
@@ -41,22 +41,26 @@ func (connect) Decode(bs []byte) (*ConnectResp, error) {
*/ */
type SecurityListResp struct { type StockListResp struct {
Count uint16 Count uint16
List []*Security List []*Stock
} }
type Security struct { type Stock struct {
Code string Name string //股票名称
VolUnit uint16 Code string //股票代码
DecimalPoint int8 VolUnit uint16 //未知
Name string DecimalPoint int8 //未知
PreClose float64 PreClose float64 //未知
} }
type securityList struct{} func (this *Stock) String() string {
return fmt.Sprintf("%s(%s)", this.Name, this.Code)
}
func (securityList) Frame(exchange Exchange, starts ...uint16) *Frame { type stockList struct{}
func (stockList) Frame(exchange Exchange, starts ...uint16) *Frame {
start := conv.DefaultUint16(0, starts...) start := conv.DefaultUint16(0, starts...)
return &Frame{ return &Frame{
Control: Control01, Control: Control01,
@@ -65,25 +69,26 @@ func (securityList) Frame(exchange Exchange, starts ...uint16) *Frame {
} }
} }
func (securityList) Decode(bs []byte) (*SecurityListResp, error) { func (stockList) Decode(bs []byte) (*StockListResp, error) {
if len(bs) < 2 { if len(bs) < 2 {
return nil, errors.New("数据长度不足") return nil, errors.New("数据长度不足")
} }
resp := &SecurityListResp{ resp := &StockListResp{
Count: Uint16(bs[:2]), Count: Uint16(bs[:2]),
} }
bs = bs[2:] bs = bs[2:]
for i := uint16(0); i < resp.Count; i++ { for i := uint16(0); i < resp.Count; i++ {
sec := &Security{ sec := &Stock{
Code: String(bs[:6]), Code: string(bs[:6]),
VolUnit: Uint16(bs[6:8]), VolUnit: Uint16(bs[6:8]),
Name: string(UTF8ToGBK(bs[8:16])), Name: string(UTF8ToGBK(bs[8:16])),
PreClose: getVolume(Uint32(bs[16:20])), DecimalPoint: int8(bs[20]),
PreClose: getVolume(Uint32(bs[21:25])),
} }
bs = bs[20:] bs = bs[29:]
resp.List = append(resp.List, sec) resp.List = append(resp.List, sec)
} }
@@ -97,9 +102,9 @@ func (securityList) Decode(bs []byte) (*SecurityListResp, error) {
*/ */
type SecurityQuotesResp []*SecurityQuote type StockQuotesResp []*StockQuote
func (this SecurityQuotesResp) String() string { func (this StockQuotesResp) String() string {
ls := []string(nil) ls := []string(nil)
for _, v := range this { for _, v := range this {
ls = append(ls, v.String()) ls = append(ls, v.String())
@@ -107,7 +112,7 @@ func (this SecurityQuotesResp) String() string {
return strings.Join(ls, "\n") return strings.Join(ls, "\n")
} }
type SecurityQuote struct { type StockQuote struct {
Exchange Exchange // 市场 Exchange Exchange // 市场
Code string // 股票代码 6个ascii字符串 Code string // 股票代码 6个ascii字符串
Active1 uint16 // 活跃度 Active1 uint16 // 活跃度
@@ -136,7 +141,7 @@ type SecurityQuote struct {
Active2 uint16 // 活跃度 Active2 uint16 // 活跃度
} }
func (this *SecurityQuote) String() string { func (this *StockQuote) String() string {
return fmt.Sprintf(`%s%s return fmt.Sprintf(`%s%s
%s %s
总量%s, 现量%s, 总金额%s, 内盘%s, 外盘%s 总量%s, 现量%s, 总金额%s, 内盘%s, 外盘%s
@@ -149,9 +154,9 @@ func (this *SecurityQuote) String() string {
) )
} }
type securityQuote struct{} type stockQuote struct{}
func (this securityQuote) Frame(m map[Exchange]string) (*Frame, error) { func (this stockQuote) Frame(m map[Exchange]string) (*Frame, error) {
f := &Frame{ f := &Frame{
Control: Control01, Control: Control01,
Type: TypeSecurityQuote, Type: TypeSecurityQuote,
@@ -196,11 +201,11 @@ b212 昨天收盘价1186
8defd10c 服务时间 8defd10c 服务时间
c005bed2668e05be15804d8ba12cb3b13a0083c3034100badc029d014201bc990384f70443029da503b7af074403a6e501b9db044504a6e2028dd5048d050000000000005909 c005bed2668e05be15804d8ba12cb3b13a0083c3034100badc029d014201bc990384f70443029da503b7af074403a6e501b9db044504a6e2028dd5048d050000000000005909
*/ */
func (this securityQuote) Decode(bs []byte) SecurityQuotesResp { func (this stockQuote) Decode(bs []byte) StockQuotesResp {
//logs.Debug(hex.EncodeToString(bs)) //logs.Debug(hex.EncodeToString(bs))
resp := SecurityQuotesResp{} resp := StockQuotesResp{}
//前2字节是什么? //前2字节是什么?
bs = bs[2:] bs = bs[2:]
@@ -209,7 +214,7 @@ func (this securityQuote) Decode(bs []byte) SecurityQuotesResp {
bs = bs[2:] bs = bs[2:]
for i := uint16(0); i < number; i++ { for i := uint16(0); i < number; i++ {
sec := &SecurityQuote{ sec := &StockQuote{
Exchange: Exchange(bs[0]), Exchange: Exchange(bs[0]),
Code: string(UTF8ToGBK(bs[1:7])), Code: string(UTF8ToGBK(bs[1:7])),
Active1: Uint16(bs[7:9]), Active1: Uint16(bs[7:9]),

View File

@@ -10,7 +10,7 @@ import (
0c02000000011a001a003e05050000000000000002000030303030303101363030303038 0c02000000011a001a003e05050000000000000002000030303030303101363030303038
*/ */
func TestNewSecurityQuotes(t *testing.T) { func TestNewSecurityQuotes(t *testing.T) {
f, err := MSecurityQuote.Frame(map[Exchange]string{ f, err := MStockQuote.Frame(map[Exchange]string{
ExchangeSH: "000001", ExchangeSH: "000001",
ExchangeSZ: "600008", ExchangeSZ: "600008",
}) })
@@ -48,6 +48,6 @@ func Test_getPrice(t *testing.T) {
0c020000000106000600500400000000 0c020000000106000600500400000000
*/ */
func Test_securityList_Frame(t *testing.T) { func Test_securityList_Frame(t *testing.T) {
f := MSecurityList.Frame(ExchangeSH, 0) f := MStockList.Frame(ExchangeSH, 0)
t.Log(f.Bytes().HEX()) t.Log(f.Bytes().HEX())
} }

View File

@@ -10,18 +10,22 @@ import (
"io" "io"
) )
// String 字节先转小端,再转字符
func String(bs []byte) string { func String(bs []byte) string {
return string(bytes2.Reverse(bs)) return string(bytes2.Reverse(bs))
} }
// Bytes 任意类型转小端字节
func Bytes(n any) []byte { func Bytes(n any) []byte {
return bytes2.Reverse(conv.Bytes(n)) return bytes2.Reverse(conv.Bytes(n))
} }
// Uint32 字节通过小端方式转为uint32
func Uint32(bs []byte) uint32 { func Uint32(bs []byte) uint32 {
return conv.Uint32(bytes2.Reverse(bs)) return conv.Uint32(bytes2.Reverse(bs))
} }
// Uint16 字节通过小端方式转为uint16
func Uint16(bs []byte) uint16 { func Uint16(bs []byte) uint16 {
return conv.Uint16(bytes2.Reverse(bs)) return conv.Uint16(bytes2.Reverse(bs))
} }