2026-01-27 19:40:28 +08:00
|
|
|
|
package rule
|
2026-01-28 23:09:16 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"encoding/json"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
2026-01-29 03:22:42 +08:00
|
|
|
|
"git.apinb.com/bsm-sdk/core/utils"
|
2026-01-28 23:09:16 +08:00
|
|
|
|
"git.apinb.com/quant/gostock/internal/impl"
|
|
|
|
|
|
"git.apinb.com/quant/gostock/internal/models"
|
|
|
|
|
|
talib "github.com/markcheno/go-talib"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-01 14:57:20 +08:00
|
|
|
|
func GetArgConfig(code string) (*models.StockArgs, *StockArgConf, error) {
|
|
|
|
|
|
var args models.StockArgs
|
|
|
|
|
|
err := impl.DBService.Where("ts_code = ?", code).First(&args).Error
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, nil, err
|
2026-01-28 23:27:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-28 23:09:16 +08:00
|
|
|
|
var conf StockArgConf
|
2026-02-01 14:57:20 +08:00
|
|
|
|
err = json.Unmarshal([]byte(args.Config), &conf)
|
2026-01-28 23:09:16 +08:00
|
|
|
|
if err != nil {
|
2026-02-01 14:57:20 +08:00
|
|
|
|
return nil, nil, err
|
2026-01-28 23:09:16 +08:00
|
|
|
|
}
|
2026-01-28 23:27:21 +08:00
|
|
|
|
|
2026-02-01 14:57:20 +08:00
|
|
|
|
return &args, &conf, nil
|
2026-01-28 23:09:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-01 14:57:20 +08:00
|
|
|
|
func (r *RuleFactory) RunRsi(code string) {
|
|
|
|
|
|
args, conf, err := GetArgConfig(code)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
r.Model.ScoreRsi = -1
|
|
|
|
|
|
r.Model.AddDesc("RSI参数错误!")
|
|
|
|
|
|
return
|
2026-01-28 23:09:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-01 14:57:20 +08:00
|
|
|
|
if conf.BestByProfit != "rsi" {
|
|
|
|
|
|
r.Model.ScoreRsi = -1
|
|
|
|
|
|
r.Model.AddDesc("BestByProfit不是RSI")
|
|
|
|
|
|
return
|
2026-01-28 23:09:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var close []float64
|
2026-02-01 14:57:20 +08:00
|
|
|
|
impl.DBService.Model(models.StockDaily{}).Where("ts_code = ?", code).Order("trade_date desc").Limit(args.RsiPeriod*4).Pluck("close", &close)
|
|
|
|
|
|
if len(close) < args.RsiPeriod {
|
|
|
|
|
|
r.Model.ScoreRsi = -1
|
|
|
|
|
|
r.Model.AddDesc("数据不足")
|
|
|
|
|
|
return
|
2026-01-28 23:09:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-30 01:12:21 +08:00
|
|
|
|
newCloses := reverseSlice(close)
|
|
|
|
|
|
|
2026-02-01 14:57:20 +08:00
|
|
|
|
rsiResult := talib.Rsi(newCloses, args.RsiPeriod)
|
|
|
|
|
|
prveRsi := utils.FloatRound(rsiResult[len(rsiResult)-2], 2)
|
|
|
|
|
|
lastRsi := utils.FloatRound(rsiResult[len(rsiResult)-1], 2)
|
|
|
|
|
|
r.Model.ValRsiLast = lastRsi
|
|
|
|
|
|
r.Model.ValRsiPrve = prveRsi
|
|
|
|
|
|
r.Model.ValRsiOversold = args.RsiOversold
|
2026-01-29 02:57:10 +08:00
|
|
|
|
prveRsiInt := int(prveRsi)
|
|
|
|
|
|
lastRsiInt := int(lastRsi)
|
|
|
|
|
|
|
|
|
|
|
|
// 跌破RSI下轨
|
2026-02-01 14:57:20 +08:00
|
|
|
|
if lastRsiInt > args.RsiOversold {
|
2026-01-29 02:57:10 +08:00
|
|
|
|
if CheckLowest(close, lastRsiInt, 14) {
|
2026-02-01 14:57:20 +08:00
|
|
|
|
r.Model.ScoreRsi = 1
|
|
|
|
|
|
r.Model.AddDesc(fmt.Sprintf("RSI=%d 跌破下轨,14日最低", lastRsiInt))
|
|
|
|
|
|
return
|
2026-01-29 02:57:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
if CheckLowest(close, lastRsiInt, 20) {
|
2026-02-01 14:57:20 +08:00
|
|
|
|
r.Model.ScoreRsi = 1
|
|
|
|
|
|
r.Model.AddDesc(fmt.Sprintf("RSI=%d 跌破下轨,20日最低", lastRsiInt))
|
|
|
|
|
|
return
|
2026-01-29 02:57:10 +08:00
|
|
|
|
}
|
2026-02-01 14:57:20 +08:00
|
|
|
|
r.Model.ScoreRsi = -1
|
|
|
|
|
|
r.Model.AddDesc(fmt.Sprintf("RSI=%d 高于Oversold%d", lastRsiInt, args.RsiOversold))
|
|
|
|
|
|
return
|
2026-01-29 02:57:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// RSI跌破下轨后呈上涨趋势
|
2026-01-29 03:16:11 +08:00
|
|
|
|
if lastRsiInt < prveRsiInt {
|
2026-02-01 14:57:20 +08:00
|
|
|
|
r.Model.ScoreRsi = -1
|
|
|
|
|
|
r.Model.AddDesc(fmt.Sprintf("Rsi=%d prveRsi=%d,突破下轨,持续下跌", lastRsiInt, prveRsiInt))
|
|
|
|
|
|
return
|
|
|
|
|
|
} else if lastRsiInt == prveRsiInt {
|
|
|
|
|
|
r.Model.ScoreRsi = 1
|
|
|
|
|
|
r.Model.AddDesc(fmt.Sprintf("Rsi=%d prveRsi=%d,突破下轨,与前一交易日无太大波动", lastRsiInt, prveRsiInt))
|
|
|
|
|
|
return
|
2026-01-29 02:57:10 +08:00
|
|
|
|
}
|
2026-02-01 14:57:20 +08:00
|
|
|
|
r.Model.ScoreRsi = 2
|
|
|
|
|
|
r.Model.AddDesc(fmt.Sprintf("Rsi=%d prveRsi=%d,突破下轨后呈上涨趋势", lastRsiInt, prveRsiInt))
|
|
|
|
|
|
return
|
2026-01-29 02:57:10 +08:00
|
|
|
|
}
|