增加了历史分时成交

This commit is contained in:
injoyai
2024-10-29 14:54:58 +08:00
parent 9eac8cbc1a
commit 64d993a366
13 changed files with 251 additions and 33 deletions

View File

@@ -1,6 +1,7 @@
package tdx
import (
"github.com/injoyai/base/maps"
"github.com/injoyai/base/maps/wait/v2"
"github.com/injoyai/conv"
"github.com/injoyai/ios"
@@ -16,6 +17,7 @@ func Dial(addr string, op ...client.Option) (cli *Client, err error) {
cli = &Client{
w: wait.New(time.Second * 2),
m: maps.NewSafe(),
}
cli.c, err = dial.TCP(addr, func(c *client.Client) {
@@ -48,6 +50,7 @@ func Dial(addr string, op ...client.Option) (cli *Client, err error) {
type Client struct {
c *client.Client
w *wait.Entity
m *maps.Safe
msgID uint32
}
@@ -65,6 +68,9 @@ func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
return
}
val, _ := this.m.GetAndDel(conv.String(f.MsgID))
code := conv.String(val)
var resp any
switch f.Type {
@@ -83,7 +89,10 @@ func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
resp, err = protocol.MStockMinute.Decode(f.Data)
case protocol.TypeStockMinuteTrade:
resp, err = protocol.MStockMinuteTrade.Decode(f.Data, "") //todo
resp, err = protocol.MStockMinuteTrade.Decode(f.Data, code) //todo
case protocol.TypeStockHistoryMinuteTrade:
resp, err = protocol.MStockHistoryMinuteTrade.Decode(f.Data, code)
}
@@ -130,9 +139,10 @@ func (this *Client) GetStockCount(exchange protocol.Exchange) (*protocol.StockCo
return result.(*protocol.StockCountResp), nil
}
// GetStockList 获取市场内指定范围内的所有证券代码,todo 这个start好像没用
func (this *Client) GetStockList(exchange protocol.Exchange, starts ...uint16) (*protocol.StockListResp, error) {
f := protocol.MStockList.Frame(exchange, starts...)
// GetStockList 获取市场内指定范围内的所有证券代码,一次固定返回1000只,上证股票有效范围370-1480
// 上证前370只是395/399开头的(中证500/总交易等辅助类),在后面的话是一些100开头的国债
func (this *Client) GetStockList(exchange protocol.Exchange, start uint16) (*protocol.StockListResp, error) {
f := protocol.MStockList.Frame(exchange, start)
result, err := this.SendFrame(f)
if err != nil {
return nil, err
@@ -166,7 +176,7 @@ func (this *Client) GetStockMinute(exchange protocol.Exchange, code string) (*pr
return result.(*protocol.StockMinuteResp), nil
}
// GetStockMinuteTrade 获取分时交易详情
// GetStockMinuteTrade 获取分时交易详情,服务器最多返回1800条,count-start<=1800
func (this *Client) GetStockMinuteTrade(exchange protocol.Exchange, code string, start, count uint16) (*protocol.StockMinuteTradeResp, error) {
f, err := protocol.MStockMinuteTrade.Frame(exchange, code, start, count)
if err != nil {
@@ -178,3 +188,53 @@ func (this *Client) GetStockMinuteTrade(exchange protocol.Exchange, code string,
}
return result.(*protocol.StockMinuteTradeResp), nil
}
// GetStockMinuteTradeAll 获取分时全部交易详情,todo 只做参考 因为交易实时在进行,然后又是分页读取的,所以会出现读取间隔内产生的交易会丢失
func (this *Client) GetStockMinuteTradeAll(exchange protocol.Exchange, code string) (*protocol.StockMinuteTradeResp, error) {
resp := &protocol.StockMinuteTradeResp{}
maxSize := uint16(1800)
for i := uint16(0); ; i += maxSize {
r, err := this.GetStockMinuteTrade(exchange, code, i, i+maxSize)
if err != nil {
return nil, err
}
resp.Count += r.Count
resp.List = append(resp.List, r.List...)
if r.Count < maxSize {
break
}
}
return resp, nil
}
// GetStockHistoryMinuteTrade 获取历史分时交易,,只能获取昨天及之前的数据,服务器最多返回2000条,count-start<=2000
func (this *Client) GetStockHistoryMinuteTrade(t time.Time, exchange protocol.Exchange, code string, start, count uint16) (*protocol.StockHistoryMinuteTradeResp, error) {
f, err := protocol.MStockHistoryMinuteTrade.Frame(t, exchange, code, start, count)
if err != nil {
return nil, err
}
result, err := this.SendFrame(f)
if err != nil {
return nil, err
}
return result.(*protocol.StockHistoryMinuteTradeResp), nil
}
// GetStockHistoryMinuteTradeAll 获取历史分时全部交易,通过多次请求来拼接,只能获取昨天及之前的数据
func (this *Client) GetStockHistoryMinuteTradeAll(exchange protocol.Exchange, code string) (*protocol.StockMinuteTradeResp, error) {
resp := &protocol.StockMinuteTradeResp{}
maxSize := uint16(2000)
for i := uint16(0); ; i += maxSize {
r, err := this.GetStockMinuteTrade(exchange, code, i, i+maxSize)
if err != nil {
return nil, err
}
resp.Count += r.Count
resp.List = append(resp.List, r.List...)
if r.Count < maxSize {
break
}
}
return resp, nil
}

39
client_test.go Normal file
View File

@@ -0,0 +1,39 @@
package tdx
import (
"github.com/injoyai/logs"
"github.com/injoyai/tdx/protocol"
"testing"
"time"
)
var (
c *Client
do func(f func(c *Client))
)
func init() {
var err error
c, err = Dial("124.71.187.122:7709")
logs.PanicErr(err)
do = func(f func(c *Client)) {
f(c)
<-c.Done()
}
}
func TestClient_GetStockHistoryMinuteTrade(t *testing.T) {
ti := time.Date(2024, 10, 28, 0, 0, 0, 0, time.Local)
do(func(c *Client) {
resp, err := c.GetStockHistoryMinuteTrade(ti, protocol.ExchangeSH, "000001", 0, 100)
if err != nil {
t.Error(err)
return
}
for _, v := range resp.List {
t.Log(v)
}
t.Log("总数:", resp.Count)
})
}

View File

@@ -0,0 +1,23 @@
package main
import (
"github.com/injoyai/logs"
"github.com/injoyai/tdx"
"github.com/injoyai/tdx/example/common"
"github.com/injoyai/tdx/protocol"
"time"
)
func main() {
common.Test(func(c *tdx.Client) {
t := time.Date(2024, 10, 29, 0, 0, 0, 0, time.Local)
resp, err := c.GetStockHistoryMinuteTrade(t, protocol.ExchangeSH, "000001", 0, 2000)
logs.PanicErr(err)
for _, v := range resp.List {
logs.Debug(v)
}
logs.Debug("总数:", resp.Count)
})
}

View File

@@ -10,11 +10,11 @@ func main() {
c, err := tdx.Dial("124.71.187.122:7709")
logs.PanicErr(err)
resp, err := c.GetStockList(protocol.ExchangeSH, 255)
resp, err := c.GetStockList(protocol.ExchangeSH, 369)
logs.PanicErr(err)
for _, v := range resp.List {
logs.Debug(v)
for i, v := range resp.List {
logs.Debug(i, v)
}
logs.Debug("总数:", resp.Count)

View File

@@ -10,7 +10,7 @@ func main() {
c, err := tdx.Dial("124.71.187.122:7709")
logs.PanicErr(err)
resp, err := c.GetStockMinute(protocol.ExchangeSZ, "000001")
resp, err := c.GetStockMinute(protocol.ExchangeSH, "000001")
logs.PanicErr(err)
for _, v := range resp.List {

View File

@@ -10,7 +10,7 @@ import (
func main() {
common.Test(func(c *tdx.Client) {
resp, err := c.GetStockMinuteTrade(protocol.ExchangeSH, "000001", 0, 100)
resp, err := c.GetStockMinuteTrade(protocol.ExchangeSH, "000001", 0, 1900)
logs.PanicErr(err)
for _, v := range resp.List {

View File

@@ -1,13 +1,14 @@
package protocol
const (
TypeConnect = 0x000D //建立连接
TypeHeart = 0x0004 //心跳
TypeStockCount = 0x044E //获取股票数量
TypeStockList = 0x0450 //获取股票代码
TypeStockQuote = 0x053E //行情信息
TypeStockMinute = 0x051D //分时数据
TypeStockMinuteTrade = 0x0FC5 //分时交易
TypeConnect = 0x000D //建立连接
TypeHeart = 0x0004 //心跳
TypeStockCount = 0x044E //获取股票数量
TypeStockList = 0x0450 //获取股票代码
TypeStockQuote = 0x053E //行情信息
TypeStockMinute = 0x051D //分时数据
TypeStockMinuteTrade = 0x0FC5 //分时交易
TypeStockHistoryMinuteTrade = 0x0FB5 //历史分时交易
)
/*

View File

@@ -5,13 +5,14 @@ import (
)
var (
MConnect = connect{}
MHeart = heart{}
MStockCount = stockCount{}
MStockQuote = stockQuote{}
MStockList = stockList{}
MStockMinute = stockMinute{}
MStockMinuteTrade = stockMinuteTrade{}
MConnect = connect{}
MHeart = heart{}
MStockCount = stockCount{}
MStockQuote = stockQuote{}
MStockList = stockList{}
MStockMinute = stockMinute{}
MStockMinuteTrade = stockMinuteTrade{}
MStockHistoryMinuteTrade = stockHistoryMinuteTrade{}
)
type ConnectResp struct {

View File

@@ -0,0 +1,66 @@
package protocol
import (
"errors"
"github.com/injoyai/conv"
"time"
)
// StockHistoryMinuteTradeResp 历史分时交易比实时少了单量
type StockHistoryMinuteTradeResp struct {
Count uint16
List []*StockMinuteTrade
}
type StockHistoryMinuteTrade struct {
Time string //时间
Price Price //价格
Volume int //成交量
Status int //0是买1是卖2无效汇总出现
}
type stockHistoryMinuteTrade struct{}
func (stockHistoryMinuteTrade) Frame(t time.Time, exchange Exchange, code string, start, count uint16) (*Frame, error) {
date := conv.Uint32(t.Format("20060102"))
dataBs := Bytes(date)
dataBs = append(dataBs, exchange.Uint8(), 0x0)
dataBs = append(dataBs, []byte(code)...)
dataBs = append(dataBs, Bytes(start)...)
dataBs = append(dataBs, Bytes(count)...)
return &Frame{
Control: Control01,
Type: TypeStockHistoryMinuteTrade,
Data: dataBs,
}, nil
}
func (stockHistoryMinuteTrade) Decode(bs []byte, code string) (*StockHistoryMinuteTradeResp, error) {
if len(bs) < 2 {
return nil, errors.New("数据长度不足")
}
resp := &StockHistoryMinuteTradeResp{
Count: Uint16(bs[:2]),
}
//第2-6字节不知道是啥
bs = bs[2+4:]
lastPrice := Price(0)
for i := uint16(0); i < resp.Count; i++ {
mt := &StockMinuteTrade{
Time: GetTime([2]byte(bs[:2])),
}
var sub Price
bs, sub = GetPrice(bs[2:])
lastPrice += sub
mt.Price = lastPrice / basePrice(code)
bs, mt.Volume = CutInt(bs)
bs, mt.Status = CutInt(bs)
bs, _ = CutInt(bs) //这个得到的是0不知道是啥
resp.List = append(resp.List, mt)
}
return resp, nil
}

View File

@@ -0,0 +1,18 @@
package protocol
import (
"testing"
"time"
)
func Test_stockHistoryMinuteTrade_Frame(t *testing.T) {
// 预期 0c 02000000 00 1200 1200 b50f 84da3401 0000 30303030303100006400
// 0c000000000112001200b50f84da3401000030303030303100006400
ti := time.Date(2024, 10, 28, 0, 0, 0, 0, time.Local)
f, err := MStockHistoryMinuteTrade.Frame(ti, ExchangeSH, "000001", 0, 100)
if err != nil {
t.Error(err)
return
}
t.Log(f.Bytes().HEX())
}

View File

@@ -3,7 +3,6 @@ package protocol
import (
"errors"
"fmt"
"github.com/injoyai/conv"
)
type StockListResp struct {
@@ -25,8 +24,7 @@ func (this *Stock) String() string {
type stockList struct{}
func (stockList) Frame(exchange Exchange, starts ...uint16) *Frame {
start := conv.DefaultUint16(0, starts...)
func (stockList) Frame(exchange Exchange, start uint16) *Frame {
return &Frame{
Control: Control01,
Type: TypeStockList,
@@ -53,6 +51,7 @@ func (stockList) Decode(bs []byte) (*StockListResp, error) {
DecimalPoint: int8(bs[20]),
PreClose: getVolume(Uint32(bs[21:25])),
}
//logs.Debug(bs[25:29]) //26和28字节 好像是枚举(基本是44,45和34,35)
bs = bs[29:]
resp.List = append(resp.List, sec)
}

View File

@@ -6,7 +6,12 @@ import (
type StockMinuteResp struct {
Count uint16
List []PriceLevel
List []PriceNumber
}
type PriceNumber struct {
Price Price
Number int
}
type stockMinute struct{}
@@ -39,13 +44,10 @@ func (this *stockMinute) Decode(bs []byte) (*StockMinuteResp, error) {
for i := uint16(0); i < resp.Count; i++ {
bs, price = GetPrice(bs)
var unknown Price
bs, unknown = GetPrice(bs) //这个是什么
_ = unknown
//logs.Debug(price, unknown)
bs, _ = CutInt(bs) //这个是什么
var number int
bs, number = CutInt(bs)
resp.List = append(resp.List, PriceLevel{
resp.List = append(resp.List, PriceNumber{
Price: price,
Number: number,
})

View File

@@ -20,6 +20,15 @@ func Bytes(n any) []byte {
return bytes2.Reverse(conv.Bytes(n))
}
// Reverse 字节倒序
func Reverse(bs []byte) []byte {
x := make([]byte, len(bs))
for i, v := range bs {
x[len(bs)-i-1] = v
}
return x
}
// Uint32 字节通过小端方式转为uint32
func Uint32(bs []byte) uint32 {
return conv.Uint32(bytes2.Reverse(bs))