Files
banstagy/old/t01/common.go
2026-08-24 20:53:21 +08:00

139 lines
3.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package t01
import (
"github.com/banbox/banbot/orm/ormo"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
"github.com/banbox/banstagy/com"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"math"
)
var (
// 这里是默认参数,请尽量在MainVX()中修改,不要在这里修改
maBig = 30
maSml = 30
maParent = 50
sampleNum = 500 // 斜率采样数量
sampleStep = 2 // 斜率计算间隔
atrLen = 14
openOffRate = float64(0) // 开单时,从均线偏移基准ATR的倍数
goodRate = float64(10) // 收益超过此倍数的ATR时,将止损更新为均线
badRate = float64(5) // 损失超过此倍数的ATR时,止损退出
)
type MoreType struct {
bigSlop *com.Slop
hitGoods map[int64]bool // 触发goodRate的订单ID,允许每个bar更新止损
}
func openOrder(s *strat.StratJob, closePrice, dirt, price, stopLoss float64) {
if (stopLoss-closePrice)*dirt >= 0 {
// 当前止损立刻满足,跳过
return
}
tag := "long"
if dirt < 0 {
tag = "short"
}
err := s.OpenOrder(&strat.EnterReq{
Tag: tag,
Limit: price,
StopLoss: stopLoss,
Short: dirt < 0,
})
if err != nil {
log.Error("open order fail", zap.Error(err))
}
}
func onStartUp(s *strat.StratJob) {
s.More = &MoreType{
bigSlop: com.NewSlop(sampleNum, 0.25),
hitGoods: make(map[int64]bool),
}
}
func onInfoBar(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
sma := ta.SMA(e.Close, maParent)
prevSma := sma.Get(sampleStep)
if e.BarNum%sampleStep > 0 || math.IsNaN(prevSma) {
return
}
more, _ := s.More.(*MoreType)
curSma := sma.Get(0)
slop := (curSma - prevSma) / curSma
more.bigSlop.Update(e.BarNum, slop)
}
func onBar(s *strat.StratJob) {
e := s.Env
atr := ta.ATR(e.High, e.Low, e.Close, atrLen)
baseAtrLen := int(math.Round(float64(atrLen) * 4.3))
atrBase := ta.Lowest(ta.Highest(atr, atrLen), baseAtrLen).Get(0)
c := e.Close.Get(0)
smaBig := ta.SMA(e.Close, maBig)
smaVal := smaBig.Get(0)
// smaSml := ta.SMA(e.Close, maSml)
// maX := smaSml.Cross(smaBig)
// rsiVal := ta.RSI(e.Close, 20).Get(0)
more, _ := s.More.(*MoreType)
prevSma := smaBig.Get(sampleStep)
if e.BarNum%sampleStep == 0 && !math.IsNaN(prevSma) {
slop := (smaVal - prevSma) / smaVal
more.bigSlop.Update(e.BarNum, slop)
}
var longOd, shortOd *ormo.InOutOrder
if len(s.LongOrders) > 0 {
longOd = s.LongOrders[0]
}
if len(s.ShortOrders) > 0 {
shortOd = s.ShortOrders[0]
}
atrVal := atr.Get(0)
slRange := max(atrBase*badRate, atrVal*1.5)
// 检查是否触发盈利阈值,若触发更新止损
var orders = []*ormo.InOutOrder{longOd, shortOd}
for _, od := range orders {
if od != nil {
odDirt := 1.0
if od.Short {
odDirt = -1
}
isGood, ok := more.hitGoods[od.ID]
if !ok && (c-od.Enter.Average)*odDirt >= atrBase*goodRate {
more.hitGoods[od.ID] = true
isGood = true
}
if isGood {
// 已触发盈利阈值,每个bar更新止损
sl := od.GetStopLoss()
if sl == nil || (smaVal-sl.Price)*odDirt > 0 {
od.SetStopLoss(&ormo.ExitTrigger{Price: smaVal})
}
} else if od.Status == ormo.InOutStatusInit {
// 尚未入场,更新入场价格,更新止损
_ = od.SetEnterLimit(smaVal)
od.SetStopLoss(&ormo.ExitTrigger{Price: smaVal - odDirt*slRange})
}
}
}
if atrVal < atrBase {
// && more.bigSlop.Dirt > 0
if longOd == nil && more.bigSlop.Dirt >= 0 {
// 开多单 && c-smaVal+slRange > atrBase*0.5
openOrder(s, c, 1, 0, c-slRange)
}
// && more.bigSlop.Dirt < 0
if shortOd == nil && more.bigSlop.Dirt <= 0 {
// 开空单 && smaVal+slRange-c > atrBase*0.5
openOrder(s, c, -1, 0, c+slRange)
}
}
}