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 { 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: "数据不足"} } rsiResult := talib.Rsi(close, r.Args.RsiPeriod) prveRsi := rsiResult[len(rsiResult)-2] lastRsi := rsiResult[len(rsiResult)-1] 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, Desc: fmt.Sprintf("RSI=%d 跌破下轨,14日最低", lastRsiInt)} } if CheckLowest(close, lastRsiInt, 20) { return &types.RuleResult{Key: r.Key, Name: r.Name, Score: 1, 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, Rsi: lastRsi, Desc: fmt.Sprintf("RSI=%d prveRsi=%d,跌破下轨后呈上涨趋势", lastRsiInt, prveRsiInt)} } // 检查值是否为数组最后N个元素中的最小值 func CheckLowest(prices []float64, target int, n int) bool { if len(prices) == 0 || n <= 0 { return false } // 如果n大于数组长度,使用整个数组 if n > len(prices) { n = len(prices) } // 获取最后n个元素 slice := prices[len(prices)-n:] // 查找最小值 minVal := slice[0] for _, val := range slice[1:] { if val < minVal { minVal = val } } return target == int(minVal) }