diff --git a/client.go b/client.go index 1fca3dd..39248f5 100644 --- a/client.go +++ b/client.go @@ -233,6 +233,40 @@ func (this *Client) GetCodeAll(exchange protocol.Exchange) (*protocol.CodeResp, return resp, nil } +// GetStockAll 获取所有股票代码 +func (this *Client) GetStockAll() ([]string, error) { + ls := []string(nil) + for _, ex := range []protocol.Exchange{protocol.ExchangeSH, protocol.ExchangeSZ} { + resp, err := this.GetCodeAll(ex) + if err != nil { + return nil, err + } + for _, v := range resp.List { + if protocol.IsStock(v.Code) { + ls = append(ls, v.Code) + } + } + } + return ls, nil +} + +// GetETFAll 获取所有ETF代码 +func (this *Client) GetETFAll() ([]string, error) { + ls := []string(nil) + for _, ex := range []protocol.Exchange{protocol.ExchangeSH, protocol.ExchangeSZ} { + resp, err := this.GetCodeAll(ex) + if err != nil { + return nil, err + } + for _, v := range resp.List { + if protocol.IsETF(v.Code) { + ls = append(ls, v.Code) + } + } + } + return ls, nil +} + // GetQuote 获取盘口五档报价 func (this *Client) GetQuote(codes ...string) (protocol.QuotesResp, error) { for i := range codes { @@ -360,14 +394,14 @@ func (this *Client) GetMinuteTradeAll(code string) (*protocol.TradeResp, error) return resp, nil } -func (this *Client) GetHistoryTrade(date, code string, start, count uint16) (*protocol.HistoryTradeResp, error) { +func (this *Client) GetHistoryTrade(date, code string, start, count uint16) (*protocol.TradeResp, error) { return this.GetHistoryMinuteTrade(date, code, start, count) } // GetHistoryMinuteTrade 获取历史分时交易 // 只能获取昨天及之前的数据,服务器最多返回2000条,count-start<=2000,如果日期输入错误,则返回0 // 历史数据sz000001在20241116只能查到21111112,13年差几天,3141天,或者其他规则 -func (this *Client) GetHistoryMinuteTrade(date, code string, start, count uint16) (*protocol.HistoryTradeResp, error) { +func (this *Client) GetHistoryMinuteTrade(date, code string, start, count uint16) (*protocol.TradeResp, error) { code = protocol.AddPrefix(code) f, err := protocol.MHistoryTrade.Frame(date, code, start, count) if err != nil { @@ -380,17 +414,17 @@ func (this *Client) GetHistoryMinuteTrade(date, code string, start, count uint16 if err != nil { return nil, err } - return result.(*protocol.HistoryTradeResp), nil + return result.(*protocol.TradeResp), nil } -func (this *Client) GetHistoryTradeAll(date, code string) (*protocol.HistoryTradeResp, error) { +func (this *Client) GetHistoryTradeAll(date, code string) (*protocol.TradeResp, error) { return this.GetHistoryMinuteTradeAll(date, code) } // GetHistoryMinuteTradeAll 获取历史分时全部交易,通过多次请求来拼接,只能获取昨天及之前的数据 // 历史数据sz000001在20241116只能查到21111112,13年差几天,3141天,或者其他规则 -func (this *Client) GetHistoryMinuteTradeAll(date, code string) (*protocol.HistoryTradeResp, error) { - resp := &protocol.HistoryTradeResp{} +func (this *Client) GetHistoryMinuteTradeAll(date, code string) (*protocol.TradeResp, error) { + resp := &protocol.TradeResp{} size := uint16(2000) for start := uint16(0); ; start += size { r, err := this.GetHistoryMinuteTrade(date, code, start, size) @@ -603,18 +637,32 @@ func (this *Client) GetKline30MinuteUntil(code string, f func(k *protocol.Kline) return this.GetKlineUntil(protocol.TypeKline30Minute, code, f) } +// GetKline60Minute 获取60分钟k线数据 +func (this *Client) GetKline60Minute(code string, start, count uint16) (*protocol.KlineResp, error) { + return this.GetKline(protocol.TypeKline60Minute, code, start, count) +} + // GetKlineHour 获取小时k线数据 func (this *Client) GetKlineHour(code string, start, count uint16) (*protocol.KlineResp, error) { - return this.GetKline(protocol.TypeKlineHour, code, start, count) + return this.GetKline(protocol.TypeKline60Minute, code, start, count) +} + +// GetKline60MinuteAll 获取60分钟k线全部数据 +func (this *Client) GetKline60MinuteAll(code string) (*protocol.KlineResp, error) { + return this.GetKlineAll(protocol.TypeKline60Minute, code) } // GetKlineHourAll 获取小时k线全部数据 func (this *Client) GetKlineHourAll(code string) (*protocol.KlineResp, error) { - return this.GetKlineAll(protocol.TypeKlineHour, code) + return this.GetKlineAll(protocol.TypeKline60Minute, code) +} + +func (this *Client) GetKline60MinuteUntil(code string, f func(k *protocol.Kline) bool) (*protocol.KlineResp, error) { + return this.GetKlineUntil(protocol.TypeKline60Minute, code, f) } func (this *Client) GetKlineHourUntil(code string, f func(k *protocol.Kline) bool) (*protocol.KlineResp, error) { - return this.GetKlineUntil(protocol.TypeKlineHour, code, f) + return this.GetKlineUntil(protocol.TypeKline60Minute, code, f) } // GetKlineDay 获取日k线数据 diff --git a/codes.go b/codes.go index a26516c..88b0c56 100644 --- a/codes.go +++ b/codes.go @@ -70,7 +70,7 @@ func NewCodes(c *Client, filenames ...string) (*Codes, error) { { //设置定时器,每天早上9点更新数据 task := cron.New(cron.WithSeconds()) - task.AddFunc("0 0 9 * * *", func() { + task.AddFunc("10 0 9 * * *", func() { for i := 0; i < 3; i++ { if err := cc.Update(); err == nil { return diff --git a/example/FastHosts/main.go b/example/FastHosts/main.go index f2a6f40..fa35717 100644 --- a/example/FastHosts/main.go +++ b/example/FastHosts/main.go @@ -8,7 +8,7 @@ import ( func main() { ls := tdx.FastHosts(tdx.Hosts...) for _, v := range ls { - logs.Debug(v) + logs.Debug(v.Host, v.Spend) } logs.Debug("总数量:", len(ls)) } diff --git a/example/GetTrade/main.go b/example/GetTrade/main.go new file mode 100644 index 0000000..e5f3d7d --- /dev/null +++ b/example/GetTrade/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "github.com/injoyai/logs" + "github.com/injoyai/tdx" + "github.com/injoyai/tdx/example/common" +) + +func main() { + common.Test(func(c *tdx.Client) { + resp, err := c.GetTrade("sz000001", 0, 20) + logs.PanicErr(err) + + for _, v := range resp.List { + logs.Debug(v) + } + + logs.Debug("总数:", resp.Count) + }) +} diff --git a/extend/pull-trade.go b/extend/pull-trade.go index 643425e..bf92087 100644 --- a/extend/pull-trade.go +++ b/extend/pull-trade.go @@ -54,7 +54,7 @@ func (this *PullTrade) PullYear(ctx context.Context, m *tdx.Manage, year int, co date := t.Format("20060102") - var resp *protocol.HistoryTradeResp + var resp *protocol.TradeResp err = m.Do(func(c *tdx.Client) error { resp, err = c.GetHistoryTradeAll(date, code) return err diff --git a/go.mod b/go.mod index 259520e..b527a5b 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.20 require ( github.com/glebarez/go-sqlite v1.22.0 github.com/go-sql-driver/mysql v1.7.0 - github.com/injoyai/base v1.2.7 - github.com/injoyai/conv v1.2.2 - github.com/injoyai/ios v0.0.7 + github.com/injoyai/base v1.2.8 + github.com/injoyai/conv v1.2.5 + github.com/injoyai/ios v0.0.10 github.com/injoyai/logs v1.0.9 github.com/robfig/cron/v3 v3.0.1 golang.org/x/text v0.16.0 diff --git a/go.sum b/go.sum index 454be08..44e6d1a 100644 --- a/go.sum +++ b/go.sum @@ -27,12 +27,12 @@ github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/injoyai/base v1.2.7 h1:uYZoUIPGidkTNMfQfObsyHulu5VvNCOzuCh1DGn+Hz0= -github.com/injoyai/base v1.2.7/go.mod h1:BsXiJ6/hXpswWxI4zLdKz+pW2Cwo+qhdfyaWDfR/vwg= -github.com/injoyai/conv v1.2.2 h1:nxFD3zCYq/ZvVE6xAExBR+agi6gB+vc9O0si67VAsPk= -github.com/injoyai/conv v1.2.2/go.mod h1:s05l3fQJQ4mT4VX+KIdbvCWQB0YzZHprmUfUu2uxd1k= -github.com/injoyai/ios v0.0.7 h1:7k/brTmpnoqE6ajodyilkr2EJmJmcvSkpUD+LptgUVU= -github.com/injoyai/ios v0.0.7/go.mod h1:9HemWSJTmhyJCnr+kH+lRfmvrEdABi1rkCBVsbqV5X8= +github.com/injoyai/base v1.2.8 h1:voEPqxE5U+wI1RvcQStVbjUZJDLENbl1SsoQQWMn4s0= +github.com/injoyai/base v1.2.8/go.mod h1:NfCQjml3z2pCvQ3J3YcOXtecqXD0xVPKjo4YTsMLhr8= +github.com/injoyai/conv v1.2.5 h1:G4OCyF0NTZul5W1u9IgXDOhW4/zmIigdPKXFHQGmv1M= +github.com/injoyai/conv v1.2.5/go.mod h1:s05l3fQJQ4mT4VX+KIdbvCWQB0YzZHprmUfUu2uxd1k= +github.com/injoyai/ios v0.0.10 h1:Zd37Rwp90PYV5eFhirR0LJ+ni/aYLSCAYgHltk/ZzJA= +github.com/injoyai/ios v0.0.10/go.mod h1:zLTmvQmIbdTf7zZxymOVxbxvvSeUMpcs4SCVPDT9BOc= 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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= diff --git a/hosts.go b/hosts.go index 018de93..3d4a058 100644 --- a/hosts.go +++ b/hosts.go @@ -1,10 +1,12 @@ package tdx import ( + "github.com/injoyai/base/types" "github.com/injoyai/logs" "net" "strings" "sync" + "time" ) var ( @@ -75,12 +77,12 @@ var ( } ) -// FastHosts 通过tcp(ping不可用)的方式筛选可用的地址,并排序(有点误差) -func FastHosts(hosts ...string) []string { +// FastHosts 通过tcp(ping不可用)连接速度的方式筛选排序可用的地址 +func FastHosts(hosts ...string) []DialResult { wg := sync.WaitGroup{} wg.Add(len(hosts)) mu := sync.Mutex{} - ls := []string(nil) + ls := types.List[DialResult](nil) for _, host := range hosts { go func(host string) { defer wg.Done() @@ -88,17 +90,30 @@ func FastHosts(hosts ...string) []string { if !strings.Contains(addr, ":") { addr += ":7709" } + now := time.Now() c, err := net.Dial("tcp", addr) if err != nil { logs.Err(err) return } - defer c.Close() + spend := time.Since(now) + c.Close() mu.Lock() - ls = append(ls, host) + ls = append(ls, DialResult{ + Host: host, + Spend: spend, + }) mu.Unlock() }(host) } wg.Wait() - return ls + return ls.Sort(func(a, b DialResult) bool { + return a.Spend < b.Spend + }) +} + +// DialResult 连接结果 +type DialResult struct { + Host string + Spend time.Duration } diff --git a/manage.go b/manage.go index 7798699..eb42458 100644 --- a/manage.go +++ b/manage.go @@ -25,13 +25,21 @@ func NewManage(cfg *ManageConfig, op ...client.Option) (*Manage, error) { cfg.Dial = DialDefault } - //代码 - codesClient, err := cfg.Dial(op...) + //通用客户端 + commonClient, err := cfg.Dial(op...) if err != nil { return nil, err } - codesClient.Wait.SetTimeout(time.Second * 5) - codes, err := NewCodes(codesClient, cfg.CodesFilename) + commonClient.Wait.SetTimeout(time.Second * 5) + + //代码管理 + codes, err := NewCodes(commonClient, cfg.CodesFilename) + if err != nil { + return nil, err + } + + //工作日管理 + workday, err := NewWorkday(commonClient, cfg.WorkdayFileName) if err != nil { return nil, err } @@ -44,17 +52,6 @@ func NewManage(cfg *ManageConfig, op ...client.Option) (*Manage, error) { return nil, err } - //工作日 - workdayClient, err := cfg.Dial(op...) - if err != nil { - return nil, err - } - workdayClient.Wait.SetTimeout(time.Second * 5) - workday, err := NewWorkday(workdayClient, cfg.WorkdayFileName) - if err != nil { - return nil, err - } - return &Manage{ Pool: p, Config: cfg, @@ -72,6 +69,20 @@ type Manage struct { Cron *cron.Cron } +// RangeStocks 遍历所有股票 +func (this *Manage) RangeStocks(f func(code string)) { + for _, v := range this.Codes.GetStocks() { + f(v) + } +} + +// RangeETFs 遍历所有ETF +func (this *Manage) RangeETFs(f func(code string)) { + for _, v := range this.Codes.GetETFs() { + f(v) + } +} + // AddWorkdayTask 添加工作日任务 func (this *Manage) AddWorkdayTask(spec string, f func(m *Manage)) { this.Cron.AddFunc(spec, func() { diff --git a/protocol/model_history_trade.go b/protocol/model_history_trade.go index d1cd3ad..bd17129 100644 --- a/protocol/model_history_trade.go +++ b/protocol/model_history_trade.go @@ -6,11 +6,8 @@ import ( "time" ) -// HistoryTradeResp 历史分时交易比实时少了单量 -type HistoryTradeResp struct { - Count uint16 - List Trades -} +// HistoryTradeResp 兼容之前的版本 +type HistoryTradeResp = TradeResp type historyTrade struct{} @@ -31,7 +28,7 @@ func (historyTrade) Frame(date, code string, start, count uint16) (*Frame, error }, nil } -func (historyTrade) Decode(bs []byte, c TradeCache) (*HistoryTradeResp, error) { +func (historyTrade) Decode(bs []byte, c TradeCache) (*TradeResp, error) { if len(bs) < 2 { return nil, errors.New("数据长度不足") } @@ -41,7 +38,7 @@ func (historyTrade) Decode(bs []byte, c TradeCache) (*HistoryTradeResp, error) { return nil, err } - resp := &HistoryTradeResp{ + resp := &TradeResp{ Count: Uint16(bs[:2]), } diff --git a/protocol/model_kline.go b/protocol/model_kline.go index b95dacd..6d581bd 100644 --- a/protocol/model_kline.go +++ b/protocol/model_kline.go @@ -161,7 +161,7 @@ func (kline) Decode(bs []byte, c KlineCache) (*KlineResp, error) { k.Volume = int64(getVolume(Uint32(bs[:4]))) bs = bs[4:] switch c.Type { - case TypeKlineMinute, TypeKline5Minute, TypeKlineMinute2, TypeKline15Minute, TypeKline30Minute, TypeKlineHour, TypeKlineDay2: + case TypeKlineMinute, TypeKline5Minute, TypeKlineMinute2, TypeKline15Minute, TypeKline30Minute, TypeKline60Minute, TypeKlineDay2: k.Volume /= 100 } k.Amount = Price(getVolume(Uint32(bs[:4])) * 1000) //从元转为厘,并去除多余的小数 diff --git a/protocol/types.go b/protocol/types.go index 444331f..294954a 100644 --- a/protocol/types.go +++ b/protocol/types.go @@ -50,6 +50,7 @@ const ( TypeKline5Minute uint8 = 0 // 5分钟K 线 TypeKline15Minute uint8 = 1 // 15分钟K 线 TypeKline30Minute uint8 = 2 // 30分钟K 线 + TypeKline60Minute uint8 = 3 // 60分钟K 线 TypeKlineHour uint8 = 3 // 1小时K 线 TypeKlineDay2 uint8 = 4 // 日K 线, 发现和Day的区别是这个要除以100,其他未知 TypeKlineWeek uint8 = 5 // 周K 线 diff --git a/protocol/unit.go b/protocol/unit.go index 284fd3c..797f475 100644 --- a/protocol/unit.go +++ b/protocol/unit.go @@ -102,7 +102,7 @@ func GetHourMinute(bs [2]byte) string { func GetTime(bs [4]byte, Type uint8) time.Time { switch Type { - case TypeKlineMinute, TypeKlineMinute2, TypeKline5Minute, TypeKline15Minute, TypeKline30Minute, TypeKlineHour: + case TypeKlineMinute, TypeKlineMinute2, TypeKline5Minute, TypeKline15Minute, TypeKline30Minute, TypeKline60Minute: yearMonthDay := Uint16(bs[:2]) hourMinute := Uint16(bs[2:4])