From 060cbbf301e5973c71f47d60455e66ca60912afd Mon Sep 17 00:00:00 2001 From: injoyai <1113655791@qq.com> Date: Wed, 30 Oct 2024 10:14:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A5=E7=BA=BF=E6=95=B0=E6=8D=AE=E5=AF=B9?= =?UTF-8?q?=E4=B8=8A=E4=BA=86,=E5=85=B6=E5=AE=83k=E7=BA=BF=E5=BE=85?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 +++++++- client.go | 37 ++++++++++++++---- protocol/model_stock_kline.go | 6 +-- protocol/model_stock_kline_test.go | 2 +- protocol/types.go | 4 +- protocol/types_price.go | 59 ---------------------------- protocol/types_price_test.go | 14 ------- protocol/unit.go | 62 +++++++++++++++++++++++++++++- protocol/unit_test.go | 5 +++ 9 files changed, 116 insertions(+), 90 deletions(-) delete mode 100644 protocol/types_price_test.go diff --git a/README.md b/README.md index 62bd54c..0124438 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,19 @@ * 分时成交 ![](docs/plan20241028-2.png) * K线 -![](docs/plan20241029.png) \ No newline at end of file +![](docs/plan20241029.png) + +### 其它信息 + +* 中国的股市开盘时间为:每周一至周五的上午9:30——11:30, 下午13:00——15:00。中国股市收盘时间为:每周一至周五的下午3点。 + +* 600开头的股票是上证A股,属于大盘股,其中6006开头的股票是最早上市的股票, 6016开头的股票为大盘蓝筹股;900开头的股票是上证B股; 000开头的股票是深证A股,001、002开头的股票也都属于深证A股, 其中002开头的股票是深证A股中小企业股票;200开头的股票是深证B股; 300开头的股票是创业板股票;400开头的股票是三板市场股票。 + + +### 数据接口 + +* 实时行情数据 http://vip.stock.finance.sina.com.cn/mkt/#hs_a + 描述: A 股数据是从新浪财经获取的数据, 重复运行本函数会被新浪暂时封 IP, 建议增加时间间隔 限量: 单次返回所有 A 股上市公司的实时行情数据 + +* 历史行情数据 https://finance.sina.com.cn/realstock/company/sh600006/nc.shtml + 描述: A 股数据是从新浪财经获取的数据, 历史数据按日频率更新; 注意其中的 sh689009 为 CDR, 请 通过 stock_zh_a_cdr_daily 接口获取 限量: 单次返回指定 A 股上市公司指定日期间的历史行情日频率数据 \ No newline at end of file diff --git a/client.go b/client.go index 351d900..d87eeb5 100644 --- a/client.go +++ b/client.go @@ -10,6 +10,7 @@ import ( "github.com/injoyai/ios/client/dial" "github.com/injoyai/logs" "github.com/injoyai/tdx/protocol" + "sync/atomic" "time" ) @@ -128,21 +129,15 @@ func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) { } +// SendFrame 发送数据,并等待响应 func (this *Client) SendFrame(f *protocol.Frame) (any, error) { - this.msgID++ - f.MsgID = this.msgID + f.MsgID = atomic.AddUint32(&this.msgID, 1) if _, err := this.Client.Write(f.Bytes()); err != nil { return nil, err } return this.Wait.Wait(conv.String(this.msgID)) } -func (this *Client) connect() error { - f := protocol.MConnect.Frame() - _, err := this.Write(f.Bytes()) - return err -} - // GetStockCount 获取市场内的股票数量 func (this *Client) GetStockCount(exchange protocol.Exchange) (*protocol.StockCountResp, error) { f := protocol.MStockCount.Frame(exchange) @@ -155,6 +150,9 @@ func (this *Client) GetStockCount(exchange protocol.Exchange) (*protocol.StockCo // GetStockList 获取市场内指定范围内的所有证券代码,一次固定返回1000只,上证股票有效范围370-1480 // 上证前370只是395/399开头的(中证500/总交易等辅助类),在后面的话是一些100开头的国债 +// 600开头的股票是上证A股,属于大盘股,其中6006开头的股票是最早上市的股票, 6016开头的股票为大盘蓝筹股;900开头的股票是上证B股; +// 000开头的股票是深证A股,001、002开头的股票也都属于深证A股, 其中002开头的股票是深证A股中小企业股票;200开头的股票是深证B股; +// 300开头的股票是创业板股票;400开头的股票是三板市场股票。 func (this *Client) GetStockList(exchange protocol.Exchange, start uint16) (*protocol.StockListResp, error) { f := protocol.MStockList.Frame(exchange, start) result, err := this.SendFrame(f) @@ -164,6 +162,24 @@ func (this *Client) GetStockList(exchange protocol.Exchange, start uint16) (*pro return result.(*protocol.StockListResp), nil } +// GetStockAll 通过多次请求的方式获取全部证券代码 +func (this *Client) GetStockAll(exchange protocol.Exchange) (*protocol.StockListResp, error) { + resp := &protocol.StockListResp{} + maxSize := uint16(1000) + for start := uint16(0); ; start += maxSize { + 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 { + break + } + } + return resp, nil +} + // GetStockQuotes 获取盘口五档报价 func (this *Client) GetStockQuotes(m map[protocol.Exchange]string) (protocol.StockQuotesResp, error) { f, err := protocol.MStockQuote.Frame(m) @@ -286,6 +302,11 @@ func (this *Client) GetStockKline30Minute(req *protocol.StockKlineReq) (*protoco return this.GetStockKline(protocol.TypeKline30Minute, req) } +// GetStockKlineHour 获取小时k线数据 +func (this *Client) GetStockKlineHour(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { + return this.GetStockKline(protocol.TypeKlineHour, req) +} + // GetStockKlineDay 获取日k线数据 func (this *Client) GetStockKlineDay(req *protocol.StockKlineReq) (*protocol.StockKlineResp, error) { return this.GetStockKline(protocol.TypeKlineDay, req) diff --git a/protocol/model_stock_kline.go b/protocol/model_stock_kline.go index adbbae2..729d355 100644 --- a/protocol/model_stock_kline.go +++ b/protocol/model_stock_kline.go @@ -37,7 +37,7 @@ type StockKline struct { Open Price //开盘价 High Price //最高价 Low Price //最低价 - Close Price //收盘价 + Close Price //收盘价,如果是当天,则是最新价/实时价 Volume float64 //成交量 Amount float64 //成交额 Time time.Time //时间 @@ -100,9 +100,7 @@ func (stockKline) Decode(bs []byte, Type TypeKline) (*StockKlineResp, error) { last = last + open + _close - //logs.Debug(Reverse(bs[:4]), getVolume(Uint32(bs[:4]))) - //logs.Debug(Reverse(bs[4:8]), getVolume(Uint32(bs[4:8]))) - k.Volume = getVolume(Uint32(bs[:4])) / 100 + k.Volume = getVolume(Uint32(bs[:4])) k.Amount = getVolume(Uint32(bs[4:8])) bs = bs[8:] diff --git a/protocol/model_stock_kline_test.go b/protocol/model_stock_kline_test.go index 5dca6ac..cc024ca 100644 --- a/protocol/model_stock_kline_test.go +++ b/protocol/model_stock_kline_test.go @@ -8,7 +8,7 @@ import ( func Test_stockKline_Frame(t *testing.T) { //预期0c02000000001c001c002d050000303030303031 0900 0100 0000 0a00 00000000000000000000 // 0c00000000011c001c002d050000313030303030 0900 0000 0000 0a00 00000000000000000000 - f, _ := MStockKline.Frame(TypeKlineDay2, &StockKlineReq{ + f, _ := MStockKline.Frame(TypeKlineDay, &StockKlineReq{ Exchange: ExchangeSH, Code: "000001", Start: 0, diff --git a/protocol/types.go b/protocol/types.go index de0f696..c8590f1 100644 --- a/protocol/types.go +++ b/protocol/types.go @@ -55,12 +55,12 @@ const ( TypeKline15Minute TypeKline = 1 // 15分钟K 线 TypeKline30Minute TypeKline = 2 // 30分钟K 线 TypeKlineHour TypeKline = 3 // 1小时K 线 - TypeKlineDay TypeKline = 4 // 日K 线 + TypeKlineDay2 TypeKline = 4 // 日K 线 TypeKlineWeek TypeKline = 5 // 周K 线 TypeKlineMonth TypeKline = 6 // 月K 线 TypeKlineMinute TypeKline = 7 // 1分钟 TypeKlineMinute2 TypeKline = 8 // 1分钟K 线 - TypeKlineDay2 TypeKline = 9 // 日K 线 + TypeKlineDay TypeKline = 9 // 日K 线 TypeKlineQuarter TypeKline = 10 // 季K 线 TypeKlineYear TypeKline = 11 // 年K 线 ) diff --git a/protocol/types_price.go b/protocol/types_price.go index a197a86..b6da99c 100644 --- a/protocol/types_price.go +++ b/protocol/types_price.go @@ -2,7 +2,6 @@ package protocol import ( "fmt" - "math" ) // Price 价格,单位分 @@ -158,61 +157,3 @@ func getData(bs []byte) (data int) { 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 -} diff --git a/protocol/types_price_test.go b/protocol/types_price_test.go deleted file mode 100644 index c309d9f..0000000 --- a/protocol/types_price_test.go +++ /dev/null @@ -1,14 +0,0 @@ -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)) -} diff --git a/protocol/unit.go b/protocol/unit.go index c050aae..00c1cb6 100644 --- a/protocol/unit.go +++ b/protocol/unit.go @@ -8,6 +8,7 @@ import ( "golang.org/x/text/encoding/simplifiedchinese" "golang.org/x/text/transform" "io" + "math" "time" ) @@ -82,7 +83,7 @@ func GetDate(bs [2]byte) string { func GetTime(bs [4]byte, Type TypeKline) time.Time { switch Type { - case TypeKlineDay, TypeKlineMinute, TypeKlineMinute2: + case TypeKlineDay2, TypeKlineMinute, TypeKlineMinute2: yearMonthDay := Uint16(bs[:2]) hourMinute := Uint16(bs[:2]) @@ -115,3 +116,62 @@ func basePrice(code string) Price { return 10 } } + +func getVolume(val uint32) (volume float64) { + ivol := int32(val) + 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 +} diff --git a/protocol/unit_test.go b/protocol/unit_test.go index 402dbf1..8157556 100644 --- a/protocol/unit_test.go +++ b/protocol/unit_test.go @@ -17,3 +17,8 @@ func TestUTF8ToGBK(t *testing.T) { bs = UTF8ToGBK(bs) t.Log(string(bs)) } + +func Test_getVolume(t *testing.T) { + t.Log(getVolume(1237966432)) + t.Log(getVolume(1237966432)) +}