mirror of
https://github.com/injoyai/tdx.git
synced 2025-11-26 21:25:35 +08:00
增加了历史分时成交
This commit is contained in:
70
client.go
70
client.go
@@ -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
39
client_test.go
Normal 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)
|
||||
})
|
||||
|
||||
}
|
||||
23
example/GetStockHistoryMinuteTrade/main.go
Normal file
23
example/GetStockHistoryMinuteTrade/main.go
Normal 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)
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 //历史分时交易
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
@@ -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 {
|
||||
|
||||
66
protocol/model_stock_history_minute_trade.go
Normal file
66
protocol/model_stock_history_minute_trade.go
Normal 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
|
||||
}
|
||||
18
protocol/model_stock_history_minute_trade_test.go
Normal file
18
protocol/model_stock_history_minute_trade_test.go
Normal 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())
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user