From 303acaccb22fea468689f5f83fbd3f44b0be5af2 Mon Sep 17 00:00:00 2001 From: yanweidong Date: Wed, 28 Jan 2026 23:09:16 +0800 Subject: [PATCH] deving --- cmd/cli/main.go | 8 ++++ go.mod | 1 + go.sum | 2 + internal/logic/strategy/data.go | 11 +++++ internal/logic/strategy/rule/roe.go | 21 ++++----- internal/logic/strategy/rule/rsi.go | 68 +++++++++++++++++++++++++++++ 6 files changed, 98 insertions(+), 13 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index d9874da..a1f6ea5 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -55,5 +55,13 @@ func RuleFilter(basic *models.StockBasic) (bool, string) { if re := rule.NewAmount().Run(basic.TsCode); re.Score <= 0 { return false, re.Desc } + + if re := rule.NewRoe().Run(basic.TsCode); re.Score <= 0 { + return false, re.Desc + } + + if re := rule.NewRsi(strategy.GetArgs(basic.TsCode)).Run(basic.TsCode); re.Score <= 0 { + return false, re.Desc + } return true, "" } diff --git a/go.mod b/go.mod index 36b30e7..2c869b5 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/markcheno/go-talib v0.0.0-20250114000313-ec55a20c902f github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index 965f7bc..112bf3e 100644 --- a/go.sum +++ b/go.sum @@ -98,6 +98,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/markcheno/go-talib v0.0.0-20250114000313-ec55a20c902f h1:iKq//xEUUaeRoXNcAshpK4W8eSm7HtgI0aNznWtX7lk= +github.com/markcheno/go-talib v0.0.0-20250114000313-ec55a20c902f/go.mod h1:3YUtoVrKWu2ql+iAeRyepSz3fy6a+19hJzGS88+u4u0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/internal/logic/strategy/data.go b/internal/logic/strategy/data.go index a372a7e..3bd72bb 100644 --- a/internal/logic/strategy/data.go +++ b/internal/logic/strategy/data.go @@ -31,6 +31,17 @@ func GetIndustry() (industry []string) { return } + +func GetArgs(code string) *models.StockArgs { + var args models.StockArgs + err := impl.DBService.Where("ts_code = ?", code).First(&args).Error + if err != nil { + return nil + } + + return &args +} + func GetBasic(code string) *models.StockBasic { var data StockData impl.DBService.Where("ts_code = ?", code).First(&data.Basic) diff --git a/internal/logic/strategy/rule/roe.go b/internal/logic/strategy/rule/roe.go index 55fa83a..0a237c4 100644 --- a/internal/logic/strategy/rule/roe.go +++ b/internal/logic/strategy/rule/roe.go @@ -9,7 +9,7 @@ import ( ) var ( - MinRoe = 0 + MinRoe float64 = 0 ) type Roe struct { @@ -26,20 +26,15 @@ func NewRoe() *Roe { func (r *Roe) Run(code string) *types.RuleResult { - var data []models.StockDaily - impl.DBService.Where("ts_code = ?", code).Order("trade_date desc").Limit(LastDay).Find(&data) - - check := true - for _, row := range data { - if row.Close < MinPrice { - check = false - break - } + var data models.StockFinaIndicator + err := impl.DBService.Where("ts_code = ?", code).Order("period desc").Limit(1).First(&data).Error + if err != nil { + return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: "最近无财报,无ROE值!"} } - if !check { - return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: fmt.Sprintf("最近%d天, 有价格低于%.2f", LastDay, MinPrice)} + if data.Roe < MinRoe { + return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: fmt.Sprintf("ROE=%.2f 低于%.2f", data.Roe, MinRoe)} } - return &types.RuleResult{Key: r.Key, Name: r.Name, Score: 1, Desc: fmt.Sprintf("最近%d天, 价格均高于%.2f", LastDay, MinPrice)} + return &types.RuleResult{Key: r.Key, Name: r.Name, Score: 1, Desc: fmt.Sprintf("ROE=%.2f 高于%.2f", data.Roe, MinRoe)} } diff --git a/internal/logic/strategy/rule/rsi.go b/internal/logic/strategy/rule/rsi.go index 63ed38e..d9e1bff 100644 --- a/internal/logic/strategy/rule/rsi.go +++ b/internal/logic/strategy/rule/rsi.go @@ -1 +1,69 @@ package rule + +import ( + "encoding/json" + "fmt" + + "git.apinb.com/quant/gostock/internal/impl" + "git.apinb.com/quant/gostock/internal/logic/types" + "git.apinb.com/quant/gostock/internal/models" + talib "github.com/markcheno/go-talib" +) + +type Rsi struct { + Key string + Name string + Conf *StockArgConf + Args *models.StockArgs +} + +type StockArgConf struct { + BestByDrawdown string `json:"best_by_drawdown"` + BestByProfit string `json:"best_by_profit"` + BestByReturn string `json:"best_by_return"` + BestBySharpe string `json:"best_by_sharpe"` + BestByWinRate string `json:"best_by_win_rate"` +} + +func NewRsi(args *models.StockArgs) *Rsi { + var conf StockArgConf + err := json.Unmarshal([]byte(args.Config), &conf) + if err != nil { + return &Rsi{ + Key: "Rsi", + Name: "RSI指标", + Conf: nil, + Args: args, + } + } + return &Rsi{ + Key: "Rsi", + Name: "RSI指标", + Conf: &conf, + Args: args, + } +} + +func (r *Rsi) Run(code string) *types.RuleResult { + if r.Conf == nil { + return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: "参数错误!"} + } + + if r.Conf.BestByProfit != "rsi" { + return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: "BestByProfit!=RSI,BestByProfit=" + r.Conf.BestByProfit} + } + + var close []float64 + impl.DBService.Model(models.StockDaily{}).Where("ts_code = ?", code).Order("trade_date desc").Limit(r.Args.RsiPeriod*4).Pluck("close", &close) + if len(close) < r.Args.RsiPeriod { + return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: "数据不足"} + } + + rsiResult := talib.Rsi(close, r.Args.RsiPeriod) + lastRsi := rsiResult[len(rsiResult)-1] + if lastRsi > float64(r.Args.RsiOversold) { + return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: fmt.Sprintf("RSI=%.2f 高于%d", lastRsi, r.Args.RsiOversold)} + } + + return &types.RuleResult{Key: r.Key, Name: r.Name, Score: 1, Desc: fmt.Sprintf("RSI=%.2f 低于%d", lastRsi, r.Args.RsiOversold)} +}