diff --git a/client.go b/client.go index 297cfb2..4e0c14e 100644 --- a/client.go +++ b/client.go @@ -1,6 +1,7 @@ package tdx import ( + "errors" "fmt" "github.com/injoyai/base/maps" "github.com/injoyai/base/maps/wait/v2" @@ -10,6 +11,7 @@ import ( "github.com/injoyai/ios/client/dial" "github.com/injoyai/logs" "github.com/injoyai/tdx/protocol" + "runtime/debug" "sync/atomic" "time" ) @@ -76,6 +78,7 @@ func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) { defer func() { if e := recover(); e != nil { logs.Err(e) + debug.PrintStack() } }() @@ -169,15 +172,15 @@ func (this *Client) GetStockList(exchange protocol.Exchange, start uint16) (*pro // GetStockAll 通过多次请求的方式获取全部证券代码 func (this *Client) GetStockAll(exchange protocol.Exchange) (*protocol.StockListResp, error) { resp := &protocol.StockListResp{} - maxSize := uint16(1000) - for start := uint16(0); ; start += maxSize { + size := uint16(1000) + for start := uint16(0); ; start += size { r, err := this.GetStockList(exchange, start) if err != nil { return nil, err } resp.Count += r.Count resp.List = append(resp.List, r.List...) - if r.Count < maxSize { + if r.Count < size { break } } @@ -212,6 +215,9 @@ func (this *Client) GetStockMinute(exchange protocol.Exchange, code string) (*pr // GetStockMinuteTrade 获取分时交易详情,服务器最多返回1800条,count-start<=1800 func (this *Client) GetStockMinuteTrade(exchange protocol.Exchange, code string, start, count uint16) (*protocol.StockMinuteTradeResp, error) { + if count > 1800 { + return nil, errors.New("数量不能超过1800") + } f, err := protocol.MStockMinuteTrade.Frame(exchange, code, start, count) if err != nil { return nil, err @@ -226,16 +232,16 @@ func (this *Client) GetStockMinuteTrade(exchange protocol.Exchange, code string, // 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) + size := uint16(1800) + for i := uint16(0); ; i += size { + r, err := this.GetStockMinuteTrade(exchange, code, i, i+size) if err != nil { return nil, err } resp.Count += r.Count resp.List = append(resp.List, r.List...) - if r.Count < maxSize { + if r.Count < size { break } } @@ -244,6 +250,9 @@ func (this *Client) GetStockMinuteTradeAll(exchange protocol.Exchange, code stri // GetStockHistoryMinuteTrade 获取历史分时交易,,只能获取昨天及之前的数据,服务器最多返回2000条,count-start<=2000 func (this *Client) GetStockHistoryMinuteTrade(t time.Time, exchange protocol.Exchange, code string, start, count uint16) (*protocol.StockHistoryMinuteTradeResp, error) { + if count > 2000 { + return nil, errors.New("数量不能超过2000") + } f, err := protocol.MStockHistoryMinuteTrade.Frame(t, exchange, code, start, count) if err != nil { return nil, err @@ -258,15 +267,15 @@ func (this *Client) GetStockHistoryMinuteTrade(t time.Time, exchange protocol.Ex // 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) + size := uint16(2000) + for i := uint16(0); ; i += size { + r, err := this.GetStockMinuteTrade(exchange, code, i, i+size) if err != nil { return nil, err } resp.Count += r.Count resp.List = append(resp.List, r.List...) - if r.Count < maxSize { + if r.Count < size { break } } @@ -286,52 +295,125 @@ func (this *Client) GetStockKline(Type protocol.TypeKline, req *protocol.StockKl return result.(*protocol.StockKlineResp), nil } +// GetStockKlineAll 获取全部k线数据 +func (this *Client) GetStockKlineAll(Type protocol.TypeKline, exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + resp := &protocol.StockKlineResp{} + size := uint16(800) + for i := uint16(0); ; i += size { + r, err := this.GetStockKline(Type, &protocol.StockKlineReq{ + Exchange: exchange, + Code: code, + Start: i, + Count: i + size, + }) + if err != nil { + return nil, err + } + resp.Count += r.Count + resp.List = append(resp.List, r.List...) + if r.Count < size { + break + } + } + return resp, nil +} + // GetStockKlineMinute 获取一分钟k线数据 func (this *Client) GetStockKlineMinute(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKlineMinute, req) } +// GetStockKlineMinuteAll 获取一分钟k线全部数据 +func (this *Client) GetStockKlineMinuteAll(exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + return this.GetStockKlineAll(protocol.TypeKlineMinute, exchange, code) +} + // GetStockKline5Minute 获取五分钟k线数据 func (this *Client) GetStockKline5Minute(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKline5Minute, req) } +// GetStockKline5MinuteAll 获取5分钟k线全部数据 +func (this *Client) GetStockKline5MinuteAll(exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + return this.GetStockKlineAll(protocol.TypeKline5Minute, exchange, code) +} + // GetStockKline15Minute 获取十五分钟k线数据 func (this *Client) GetStockKline15Minute(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKline15Minute, req) } +// GetStockKline15MinuteAll 获取十五分钟k线全部数据 +func (this *Client) GetStockKline15MinuteAll(exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + return this.GetStockKlineAll(protocol.TypeKline15Minute, exchange, code) +} + // GetStockKline30Minute 获取三十分钟k线数据 func (this *Client) GetStockKline30Minute(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKline30Minute, req) } +// GetStockKline30MinuteAll 获取三十分钟k线全部数据 +func (this *Client) GetStockKline30MinuteAll(exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + return this.GetStockKlineAll(protocol.TypeKline30Minute, exchange, code) +} + // GetStockKlineHour 获取小时k线数据 func (this *Client) GetStockKlineHour(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKlineHour, req) } +// GetStockKlineHourAll 获取小时k线全部数据 +func (this *Client) GetStockKlineHourAll(exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + return this.GetStockKlineAll(protocol.TypeKlineHour, exchange, code) +} + // GetStockKlineDay 获取日k线数据 func (this *Client) GetStockKlineDay(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKlineDay, req) } +// GetStockKlineDayAll 获取日k线全部数据 +func (this *Client) GetStockKlineDayAll(exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + return this.GetStockKlineAll(protocol.TypeKlineDay, exchange, code) +} + // GetStockKlineWeek 获取周k线数据 func (this *Client) GetStockKlineWeek(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKlineWeek, req) } +// GetStockKlineWeekAll 获取周k线全部数据 +func (this *Client) GetStockKlineWeekAll(exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + return this.GetStockKlineAll(protocol.TypeKlineWeek, exchange, code) +} + // GetStockKlineMonth 获取月k线数据 func (this *Client) GetStockKlineMonth(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKlineMonth, req) } +// GetStockKlineMonthAll 获取月k线全部数据 +func (this *Client) GetStockKlineMonthAll(exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + return this.GetStockKlineAll(protocol.TypeKlineMonth, exchange, code) +} + // GetStockKlineQuarter 获取季k线数据 func (this *Client) GetStockKlineQuarter(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKlineQuarter, req) } +// GetStockKlineQuarterAll 获取季k线全部数据 +func (this *Client) GetStockKlineQuarterAll(exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + return this.GetStockKlineAll(protocol.TypeKlineQuarter, exchange, code) +} + // GetStockKlineYear 获取年k线数据 func (this *Client) GetStockKlineYear(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKlineYear, req) } + +// GetStockKlineYearAll 获取年k线数据 +func (this *Client) GetStockKlineYearAll(exchange protocol.Exchange, code string) (*protocol.StockKlineResp, error) { + return this.GetStockKlineAll(protocol.TypeKlineYear, exchange, code) +} diff --git a/protocol/frame.go b/protocol/frame.go index defb8b4..4e9e305 100644 --- a/protocol/frame.go +++ b/protocol/frame.go @@ -54,9 +54,9 @@ func (this *Frame) Bytes() g.Bytes { type Response struct { Prefix uint32 //未知,猜测是帧头 - I2 uint8 //未知,猜测是响应的控制码 + Control uint8 //响应的控制码,目前发现0c是错误,1c是成功,猜测左数右第4位代表是否成功 MsgID uint32 //消息ID - Control uint8 //未知,猜测是响应的控制码 + Unknown uint8 //未知,猜测是响应的控制码 Type uint16 //响应类型,对应请求类型,如建立连接,请求分时数据等 ZipLength uint16 //数据长度 Length uint16 //未压缩长度 @@ -65,7 +65,7 @@ type Response struct { /* Decode -帧头 |未知 |消息ID |控制码 |数据类型 |未解压长度 |解压长度 |数据域 +帧头 |控制码 |消息ID |控制码 |数据类型 |未解压长度 |解压长度 |数据域 b1cb7400 |1c |00000000 |00 |0d00 |5100 |bd00 |789c6378c1cecb252ace6066c5b4898987b9050ed1f90cc5b74c18a5bc18c1b43490fecff09c81819191f13fc3c9f3bb169f5e7dfefeb5ef57f7199a305009308208e5b32bb6bcbf70148712002d7f1e13 */ func Decode(bs []byte) (*Response, error) { @@ -74,15 +74,19 @@ func Decode(bs []byte) (*Response, error) { } resp := &Response{ Prefix: Uint32(bs[:4]), - I2: bs[4], + Control: bs[4], MsgID: Uint32(bs[5:9]), - Control: bs[9], + Unknown: bs[9], Type: Uint16(bs[10:12]), ZipLength: Uint16(bs[12:14]), Length: Uint16(bs[14:16]), Data: bs[16:], } + if resp.Control&0x10 != 0x10 { + //return nil, fmt.Errorf("控制码不匹配,预期0x1c,得到0x%x", resp.Control) + } + if int(resp.ZipLength) != len(bs[16:]) { return nil, fmt.Errorf("压缩数据长度不匹配,预期%d,得到%d", resp.ZipLength+16, len(bs)) } diff --git a/protocol/model_stock_kline.go b/protocol/model_stock_kline.go index 729d355..32b0c38 100644 --- a/protocol/model_stock_kline.go +++ b/protocol/model_stock_kline.go @@ -15,6 +15,9 @@ type StockKlineReq struct { } func (this *StockKlineReq) Bytes(Type TypeKline) (g.Bytes, error) { + if this.Count > 800 { + return nil, errors.New("单次数量不能超过800") + } if len(this.Code) != 6 { return nil, errors.New("股票代码长度错误") } @@ -78,7 +81,6 @@ func (stockKline) Decode(bs []byte, Type TypeKline) (*StockKlineResp, error) { bs = bs[2:] var last Price - for i := uint16(0); i < resp.Count; i++ { k := &StockKline{ Time: GetTime([4]byte(bs[:4]), Type),