This commit is contained in:
yanweidong
2026-02-01 14:57:20 +08:00
parent ec0fb57d1f
commit d89d7967b8
7 changed files with 108 additions and 60 deletions

View File

@@ -26,12 +26,14 @@ func main() {
strategy.InitCacheByCode(code) strategy.InitCacheByCode(code)
model := models.NewStratModel("selector", code) model := models.NewStratModel("selector", code)
stratRule := rule.NewRule(model) stratRule := rule.NewRule(model)
{
stratRule.RunUpDate(strategy.Cache[code].Basic.ListDate) stratRule.RunUpDate(strategy.Cache[code].Basic.ListDate)
stratRule.RunST(strategy.Cache[code].Basic.Name) stratRule.RunST(strategy.Cache[code].Basic.Name)
stratRule.RunIndustry(strategy.Cache[code].Basic.Industry) stratRule.RunIndustry(strategy.Cache[code].Basic.Industry)
stratRule.RunPrice(code) stratRule.RunPrice(code)
stratRule.RunAmount(code) stratRule.RunAmount(code)
stratRule.RunRoe(code) stratRule.RunRoe(code)
}
model.Save() model.Save()
} }

View File

@@ -3,7 +3,6 @@ package strategy
import ( import (
"strings" "strings"
"git.apinb.com/quant/gostock/internal/logic/strategy/rule"
"git.apinb.com/quant/gostock/internal/logic/types" "git.apinb.com/quant/gostock/internal/logic/types"
"git.apinb.com/quant/gostock/internal/models" "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 { 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 return allow
} }

View File

@@ -28,10 +28,10 @@ func (r *RuleFactory) RunPrice(code string) {
} }
if !check { if !check {
r.Model.GtAmount = -1 r.Model.GtPrice = -1
r.Model.AddDesc(fmt.Sprintf("最近%d天, 有价格低于%.2f", LastDay, MinPrice)) r.Model.AddDesc(fmt.Sprintf("最近%d天, 有价格低于%.2f", LastDay, MinPrice))
return return
} }
r.Model.GtAmount = 1 r.Model.GtPrice = 1
} }

View File

@@ -19,20 +19,22 @@ func (r *RuleFactory) RunRoe(code string) {
var data models.StockFinaIndicator var data models.StockFinaIndicator
err := impl.DBService.Where("ts_code = ?", code).Order("period desc").Limit(1).First(&data).Error err := impl.DBService.Where("ts_code = ?", code).Order("period desc").Limit(1).First(&data).Error
if err != nil { if err != nil {
r.Model.GtAmount = -1 r.Model.GtRoe = -1
r.Model.ValRoe = -1
r.Model.AddDesc("最近无财报无ROE值") r.Model.AddDesc("最近无财报无ROE值")
return return
} }
data.Roe = utils.FloatRound(data.Roe, 2) data.Roe = utils.FloatRound(data.Roe, 2)
r.Model.ValRoe = data.Roe
if data.Roe < MinRoe { if data.Roe < MinRoe {
r.Model.GtAmount = -1 r.Model.GtRoe = -1
r.Model.AddDesc(fmt.Sprintf("ROE=%.2f 低于%.2f", data.Roe, MinRoe)) r.Model.AddDesc(fmt.Sprintf("ROE=%.2f 低于%.2f", data.Roe, MinRoe))
return return
} }
r.Model.GtAmount = 1 r.Model.GtRoe = 1
r.Model.AddDesc(fmt.Sprintf("ROE=%.2f 高于%.2f", data.Roe, MinRoe)) r.Model.AddDesc(fmt.Sprintf("ROE=%.2f 高于%.2f", data.Roe, MinRoe))
return return
} }

View File

@@ -6,18 +6,10 @@ import (
"git.apinb.com/bsm-sdk/core/utils" "git.apinb.com/bsm-sdk/core/utils"
"git.apinb.com/quant/gostock/internal/impl" "git.apinb.com/quant/gostock/internal/impl"
"git.apinb.com/quant/gostock/internal/logic/types"
"git.apinb.com/quant/gostock/internal/models" "git.apinb.com/quant/gostock/internal/models"
talib "github.com/markcheno/go-talib" talib "github.com/markcheno/go-talib"
) )
type Rsi struct {
Key string
Name string
Conf *StockArgConf
Args *models.StockArgs
}
type StockArgConf struct { type StockArgConf struct {
BestByDrawdown string `json:"best_by_drawdown"` BestByDrawdown string `json:"best_by_drawdown"`
BestByProfit string `json:"best_by_profit"` BestByProfit string `json:"best_by_profit"`
@@ -26,67 +18,83 @@ type StockArgConf struct {
BestByWinRate string `json:"best_by_win_rate"` BestByWinRate string `json:"best_by_win_rate"`
} }
func NewRsi(args *models.StockArgs) *Rsi { func GetArgConfig(code string) (*models.StockArgs, *StockArgConf, error) {
rsi := &Rsi{ var args models.StockArgs
Key: "Rsi", err := impl.DBService.Where("ts_code = ?", code).First(&args).Error
Name: "RSI指标", if err != nil {
Conf: nil, return nil, nil, err
Args: nil,
}
if args == nil {
return rsi
} }
var conf StockArgConf var conf StockArgConf
err := json.Unmarshal([]byte(args.Config), &conf) err = json.Unmarshal([]byte(args.Config), &conf)
if err != nil { if err != nil {
return rsi return nil, nil, err
} }
rsi.Conf = &conf return &args, &conf, nil
rsi.Args = args
return rsi
} }
func (r *Rsi) Run(code string) *types.RuleResult { func (r *RuleFactory) RunRsi(code string) {
if r.Conf == nil { args, conf, err := GetArgConfig(code)
return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: "参数错误!"} if err != nil {
r.Model.ScoreRsi = -1
r.Model.AddDesc("RSI参数错误")
return
} }
if r.Conf.BestByProfit != "rsi" { if conf.BestByProfit != "rsi" {
return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: "BestByProfit!=RSI,BestByProfit=" + r.Conf.BestByProfit} r.Model.ScoreRsi = -1
r.Model.AddDesc("BestByProfit不是RSI")
return
} }
var close []float64 var close []float64
impl.DBService.Model(models.StockDaily{}).Where("ts_code = ?", code).Order("trade_date desc").Limit(r.Args.RsiPeriod*4).Pluck("close", &close) impl.DBService.Model(models.StockDaily{}).Where("ts_code = ?", code).Order("trade_date desc").Limit(args.RsiPeriod*4).Pluck("close", &close)
if len(close) < r.Args.RsiPeriod { if len(close) < args.RsiPeriod {
return &types.RuleResult{Key: r.Key, Name: r.Name, Score: -1, Desc: "数据不足"} r.Model.ScoreRsi = -1
r.Model.AddDesc("数据不足")
return
} }
newCloses := reverseSlice(close) newCloses := reverseSlice(close)
rsiResult := talib.Rsi(newCloses, r.Args.RsiPeriod) rsiResult := talib.Rsi(newCloses, args.RsiPeriod)
prveRsi := rsiResult[len(rsiResult)-2] prveRsi := utils.FloatRound(rsiResult[len(rsiResult)-2], 2)
lastRsi := rsiResult[len(rsiResult)-1] lastRsi := utils.FloatRound(rsiResult[len(rsiResult)-1], 2)
lastRsi = utils.FloatRound(lastRsi, 2) r.Model.ValRsiLast = lastRsi
r.Model.ValRsiPrve = prveRsi
r.Model.ValRsiOversold = args.RsiOversold
prveRsiInt := int(prveRsi) prveRsiInt := int(prveRsi)
lastRsiInt := int(lastRsi) lastRsiInt := int(lastRsi)
// 跌破RSI下轨 // 跌破RSI下轨
if lastRsiInt > r.Args.RsiOversold { if lastRsiInt > args.RsiOversold {
if CheckLowest(close, lastRsiInt, 14) { 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) { 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跌破下轨后呈上涨趋势 // RSI跌破下轨后呈上涨趋势
if lastRsiInt < prveRsiInt { 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
} }

View File

@@ -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"
}

View File

@@ -1,6 +1,7 @@
package models package models
import ( import (
"fmt"
"time" "time"
"git.apinb.com/bsm-sdk/core/database" "git.apinb.com/bsm-sdk/core/database"
@@ -21,7 +22,14 @@ type StratModel struct {
GtAmount int // 每日交易额大于设定值 GtAmount int // 每日交易额大于设定值
GtPrice int // 最近20日交易日价格大于设定值 GtPrice int // 最近20日交易日价格大于设定值
GtRoe int // ROE 是否大于设定值 GtRoe int // ROE 是否大于设定值
Desc string ScoreRsi int
// 值
ValRoe float64
ValRsiOversold int
ValRsiPrve float64
ValRsiLast float64
Desc []StratDesc `gorm:"foreignKey:StratModelID"`
} }
func init() { func init() {
@@ -43,7 +51,17 @@ func NewStratModel(key, code string) *StratModel {
} }
func (s *StratModel) AddDesc(d string) { 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 { func (s *StratModel) Save() error {