diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 7454078..6698181 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -26,12 +26,14 @@ func main() { strategy.InitCacheByCode(code) model := models.NewStratModel("selector", code) stratRule := rule.NewRule(model) - stratRule.RunUpDate(strategy.Cache[code].Basic.ListDate) - stratRule.RunST(strategy.Cache[code].Basic.Name) - stratRule.RunIndustry(strategy.Cache[code].Basic.Industry) - stratRule.RunPrice(code) - stratRule.RunAmount(code) - stratRule.RunRoe(code) + { + stratRule.RunUpDate(strategy.Cache[code].Basic.ListDate) + stratRule.RunST(strategy.Cache[code].Basic.Name) + stratRule.RunIndustry(strategy.Cache[code].Basic.Industry) + stratRule.RunPrice(code) + stratRule.RunAmount(code) + stratRule.RunRoe(code) + } model.Save() } diff --git a/internal/logic/strategy/filter.go b/internal/logic/strategy/filter.go index f918d3d..9807b4f 100644 --- a/internal/logic/strategy/filter.go +++ b/internal/logic/strategy/filter.go @@ -3,7 +3,6 @@ package strategy import ( "strings" - "git.apinb.com/quant/gostock/internal/logic/strategy/rule" "git.apinb.com/quant/gostock/internal/logic/types" "git.apinb.com/quant/gostock/internal/models" ) @@ -33,11 +32,6 @@ func MustFilter(basic *models.StockBasic) (bool, *types.ResultData) { } func ClacFilter(allow []*types.ResultData) []*types.ResultData { - for idx, item := range allow { - re := rule.NewRsi(GetArgs(item.Code)).Run(item.Code) - allow[idx].Score = re.Score - allow[idx].RSI = re.Result.(float64) - } return allow } diff --git a/internal/logic/strategy/rule/price.go b/internal/logic/strategy/rule/price.go index 52d3606..22c35e5 100644 --- a/internal/logic/strategy/rule/price.go +++ b/internal/logic/strategy/rule/price.go @@ -28,10 +28,10 @@ func (r *RuleFactory) RunPrice(code string) { } if !check { - r.Model.GtAmount = -1 + r.Model.GtPrice = -1 r.Model.AddDesc(fmt.Sprintf("最近%d天, 有价格低于%.2f", LastDay, MinPrice)) return } - r.Model.GtAmount = 1 + r.Model.GtPrice = 1 } diff --git a/internal/logic/strategy/rule/roe.go b/internal/logic/strategy/rule/roe.go index 68ed557..e31e793 100644 --- a/internal/logic/strategy/rule/roe.go +++ b/internal/logic/strategy/rule/roe.go @@ -19,20 +19,22 @@ func (r *RuleFactory) RunRoe(code string) { var data models.StockFinaIndicator err := impl.DBService.Where("ts_code = ?", code).Order("period desc").Limit(1).First(&data).Error if err != nil { - r.Model.GtAmount = -1 + r.Model.GtRoe = -1 + r.Model.ValRoe = -1 r.Model.AddDesc("最近无财报,无ROE值!") return } data.Roe = utils.FloatRound(data.Roe, 2) + r.Model.ValRoe = data.Roe if data.Roe < MinRoe { - r.Model.GtAmount = -1 + r.Model.GtRoe = -1 r.Model.AddDesc(fmt.Sprintf("ROE=%.2f 低于%.2f", data.Roe, MinRoe)) return } - r.Model.GtAmount = 1 + r.Model.GtRoe = 1 r.Model.AddDesc(fmt.Sprintf("ROE=%.2f 高于%.2f", data.Roe, MinRoe)) return } diff --git a/internal/logic/strategy/rule/rsi.go b/internal/logic/strategy/rule/rsi.go index a527659..684bf66 100644 --- a/internal/logic/strategy/rule/rsi.go +++ b/internal/logic/strategy/rule/rsi.go @@ -6,18 +6,10 @@ import ( "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"` @@ -26,67 +18,83 @@ type StockArgConf struct { 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 +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 } var conf StockArgConf - err := json.Unmarshal([]byte(args.Config), &conf) + err = json.Unmarshal([]byte(args.Config), &conf) if err != nil { - return rsi + return nil, nil, err } - rsi.Conf = &conf - rsi.Args = args - return rsi + return &args, &conf, nil } -func (r *Rsi) Run(code string) *types.RuleResult { - if r.Conf == nil { - return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: "参数错误!"} +func (r *RuleFactory) RunRsi(code string) { + args, conf, err := GetArgConfig(code) + if err != nil { + r.Model.ScoreRsi = -1 + r.Model.AddDesc("RSI参数错误!") + return } - if r.Conf.BestByProfit != "rsi" { - return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: "BestByProfit!=RSI,BestByProfit=" + r.Conf.BestByProfit} + if conf.BestByProfit != "rsi" { + r.Model.ScoreRsi = -1 + r.Model.AddDesc("BestByProfit不是RSI") + return } 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: "数据不足"} + 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 } 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) + 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 prveRsiInt := int(prveRsi) lastRsiInt := int(lastRsi) // 跌破RSI下轨 - if lastRsiInt > r.Args.RsiOversold { + if lastRsiInt > 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)} + r.Model.ScoreRsi = 1 + r.Model.AddDesc(fmt.Sprintf("RSI=%d 跌破下轨,14日最低", lastRsiInt)) + return } 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)} + r.Model.ScoreRsi = 1 + r.Model.AddDesc(fmt.Sprintf("RSI=%d 跌破下轨,20日最低", lastRsiInt)) + return } - return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: fmt.Sprintf("RSI=%d 高于%d", lastRsiInt, r.Args.RsiOversold)} + r.Model.ScoreRsi = -1 + r.Model.AddDesc(fmt.Sprintf("RSI=%d 高于Oversold%d", lastRsiInt, args.RsiOversold)) + return } // RSI跌破下轨后呈上涨趋势 if lastRsiInt < prveRsiInt { - return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: fmt.Sprintf("Rsi=%d prveRsi=%d,跌破下轨,持续下跌", lastRsiInt, prveRsiInt)} + 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 } - return &types.RuleResult{Key: r.Key, Name: r.Name, Score: 1, Result: lastRsi, Desc: fmt.Sprintf("RSI=%d prveRsi=%d,跌破下轨后呈上涨趋势", lastRsiInt, prveRsiInt)} + r.Model.ScoreRsi = 2 + r.Model.AddDesc(fmt.Sprintf("Rsi=%d prveRsi=%d,突破下轨后呈上涨趋势", lastRsiInt, prveRsiInt)) + return } diff --git a/internal/models/strat_desc.go b/internal/models/strat_desc.go new file mode 100644 index 0000000..784ca76 --- /dev/null +++ b/internal/models/strat_desc.go @@ -0,0 +1,24 @@ +package models + +import ( + "time" + + "git.apinb.com/bsm-sdk/core/database" +) + +type StratDesc struct { + ID uint `gorm:"primarykey"` + CreatedAt time.Time + StratModelID uint `gorm:"column:strat_model_id"` + Hash string `gorm:"uniqueIndex"` + Desc string +} + +func init() { + database.AppendMigrate(&StratDesc{}) +} + +// TableName 设置表名 +func (StratDesc) TableName() string { + return "strat_desc" +} diff --git a/internal/models/strat_model.go b/internal/models/strat_model.go index e829d2b..7031cfe 100644 --- a/internal/models/strat_model.go +++ b/internal/models/strat_model.go @@ -1,6 +1,7 @@ package models import ( + "fmt" "time" "git.apinb.com/bsm-sdk/core/database" @@ -21,7 +22,14 @@ type StratModel struct { GtAmount int // 每日交易额大于设定值 GtPrice int // 最近20日交易日价格大于设定值 GtRoe int // ROE 是否大于设定值 - Desc string + ScoreRsi int + + // 值 + ValRoe float64 + ValRsiOversold int + ValRsiPrve float64 + ValRsiLast float64 + Desc []StratDesc `gorm:"foreignKey:StratModelID"` } func init() { @@ -43,7 +51,17 @@ func NewStratModel(key, code string) *StratModel { } func (s *StratModel) AddDesc(d string) { - s.Desc = s.Desc + "||" + d + hash := utils.Md5(fmt.Sprintf("%s-%d-%s:%s", s.StratKey, s.Ymd, s.Code, d)) + + var cnt int64 + impl.DBService.Model(&StratDesc{}).Where("hash=?", hash).Count(&cnt) + if cnt > 0 { + return + } + s.Desc = append(s.Desc, StratDesc{ + Hash: hash, + Desc: d, + }) } func (s *StratModel) Save() error {