Files
gostock/internal/logic/strategy/rule/rsi.go
yanweidong e38e584535 deving
2026-01-30 21:02:02 +08:00

93 lines
2.8 KiB
Go

package rule
import (
"encoding/json"
"fmt"
"git.apinb.com/bsm-sdk/core/utils"
"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 {
rsi := &Rsi{
Key: "Rsi",
Name: "RSI指标",
Conf: nil,
Args: nil,
}
if args == nil {
return rsi
}
var conf StockArgConf
err := json.Unmarshal([]byte(args.Config), &conf)
if err != nil {
return rsi
}
rsi.Conf = &conf
rsi.Args = args
return rsi
}
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: "数据不足"}
}
newCloses := reverseSlice(close)
rsiResult := talib.Rsi(newCloses, r.Args.RsiPeriod)
prveRsi := rsiResult[len(rsiResult)-2]
lastRsi := rsiResult[len(rsiResult)-1]
lastRsi = utils.FloatRound(lastRsi, 2)
prveRsiInt := int(prveRsi)
lastRsiInt := int(lastRsi)
// 跌破RSI下轨
if lastRsiInt > r.Args.RsiOversold {
if CheckLowest(close, lastRsiInt, 14) {
return &types.RuleResult{Key: r.Key, Name: r.Name, Score: 1, Result: lastRsi, Desc: fmt.Sprintf("RSI=%d 跌破下轨,14日最低", lastRsiInt)}
}
if CheckLowest(close, lastRsiInt, 20) {
return &types.RuleResult{Key: r.Key, Name: r.Name, Score: 1, Result: lastRsi, Desc: fmt.Sprintf("RSI=%d 跌破下轨,20日最低", lastRsiInt)}
}
return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: fmt.Sprintf("RSI=%d 高于%d", lastRsiInt, r.Args.RsiOversold)}
}
// RSI跌破下轨后呈上涨趋势
if lastRsiInt < prveRsiInt {
return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: fmt.Sprintf("Rsi=%d prveRsi=%d,跌破下轨,持续下跌", lastRsiInt, prveRsiInt)}
}
return &types.RuleResult{Key: r.Key, Name: r.Name, Score: 1, Result: lastRsi, Desc: fmt.Sprintf("RSI=%d prveRsi=%d,跌破下轨后呈上涨趋势", lastRsiInt, prveRsiInt)}
}