This commit is contained in:
2026-08-24 20:53:21 +08:00
commit 6e78bba16a
287 changed files with 32443 additions and 0 deletions

117
old/CorrMeanRev.go Normal file
View File

@@ -0,0 +1,117 @@
package old
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
"github.com/banbox/banbot/utils"
"github.com/banbox/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"gonum.org/v1/gonum/floats"
"strconv"
)
type CoMeSta struct {
corr float64
atrBs float64
}
func CorrMeanRev(pol *config.RunPolicyConfig) *strat.TradeStrat {
corrHisLen := 70
avgCorr := float64(0)
rsiUpp := 70.0
rsiLow := 30.0
overBuy, overSell := 0.0, 0.0
avgMaOff := 0.0
lenAtr := int(pol.Def("atr", 20, core.PNorm(7, 40)))
baseAtrLen := int(float64(lenAtr) * 4.3)
return &strat.TradeStrat{
WarmupNum: 100,
BatchInOut: true,
OnStartUp: func(s *strat.StratJob) {
s.More = &CoMeSta{}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
m, _ := s.More.(*CoMeSta)
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
m.atrBs = atrBase
//ma := ta.SMA(e.Close, 10).Get(0)
//maOff := (e.Close.Get(0) - ma) / m.atrBs
rsi := ta.RSI(e.Close, 14).Get(0)
if m.corr >= 0.9 && avgCorr > 0.7 && s.OrderNum == 0 {
// 全品种趋同70%以上,当前品种趋同90%
dirt := 0
if rsi > 70 {
dirt = 1
} else if rsi < 30 {
dirt = -1
}
//if avgMaOff > 0.2 && maOff > 0 {
// dirt = 1
//} else if avgMaOff < -0.2 && maOff < 0 {
// dirt = -1
//}
if dirt != 0 {
s.OpenOrder(&strat.EnterReq{
Tag: strconv.Itoa(dirt),
Short: dirt < 0,
StopLossVal: atr.Get(0) * 2,
TakeProfitVal: atr.Get(0) * 5,
})
}
} else if overBuy > 0.7 && rsi > 85 {
s.CloseOrders(&strat.ExitReq{Tag: "closeL", Dirt: core.OdDirtLong})
} else if overSell > 0.7 && rsi < 15 {
s.CloseOrders(&strat.ExitReq{Tag: "closeS", Dirt: core.OdDirtShort})
}
},
OnBatchJobs: func(jobs []*strat.StratJob) {
envs := make([]*ta.BarEnv, 0, len(jobs))
dataLen, barNum := 0, 0
for _, j := range jobs {
dataLen = j.Env.Close.Len()
barNum = j.Env.BarNum
break
}
if dataLen < corrHisLen+3 {
return
}
batchNumFlt := float64(len(jobs))
overBuy, overSell = 0.0, 0.0
avgMaOff = 0
for _, j := range jobs {
e := j.Env
m, _ := j.More.(*CoMeSta)
envs = append(envs, e)
rsi := ta.RSI(e.Close, 14).Get(0)
if rsi > rsiUpp {
overBuy += 1
} else if rsi < rsiLow {
overSell += 1
}
ma := ta.SMA(e.Close, 10).Get(0)
avgMaOff += (e.Close.Get(0) - ma) / m.atrBs
}
overBuy /= batchNumFlt
overSell /= batchNumFlt
avgMaOff /= batchNumFlt
if barNum%10 > 0 {
// 每10个bar计算一次相关性
return
}
_, corrs, err := utils.CalcEnvsCorr(envs, corrHisLen)
if err != nil {
log.Warn("CalcEnvsCorr fail", zap.String("e", err.Error()))
return
}
avgCorr = floats.Sum(corrs) / float64(len(corrs))
for i, j := range jobs {
m, _ := j.More.(*CoMeSta)
m.corr = corrs[i]
}
},
}
}

44
old/boll_back_com.go Normal file
View File

@@ -0,0 +1,44 @@
package old
import (
ta "github.com/banbox/banta"
"math"
"slices"
)
type MoreType struct {
lastCalc int
BollSlops []float64
MaSlops []float64
BollUpSlop float64
BollDownSlop float64
MaUpSlop float64
MaDownSlop float64
}
func (m *MoreType) UpdateSlops(e *ta.BarEnv, bollSlop, maSlop float64, sampleNum int) {
m.BollSlops = append(m.BollSlops, math.Abs(bollSlop))
m.MaSlops = append(m.MaSlops, math.Abs(maSlop))
if len(m.BollSlops) < sampleNum || e.BarNum-m.lastCalc < sampleNum/2 {
return
}
if len(m.BollSlops) > sampleNum {
m.BollSlops = m.BollSlops[len(m.BollSlops)-sampleNum:]
m.MaSlops = m.MaSlops[len(m.MaSlops)-sampleNum:]
}
thres := CutScore(m.BollSlops, 0.25)
m.BollUpSlop = thres
m.BollDownSlop = -thres
thres = CutScore(m.MaSlops, 0.25)
m.MaUpSlop = thres
m.MaDownSlop = -thres
m.lastCalc = e.BarNum
}
func CutScore(vals []float64, rate float64) float64 {
arr := make([]float64, len(vals))
copy(arr, vals)
slices.Sort(arr)
idx := int(float64(len(vals)) * rate)
return arr[idx]
}

78
old/boll_back_v1.go Normal file
View File

@@ -0,0 +1,78 @@
package old
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"math"
)
const (
v1SlopLen = 2
v1BollLen = 26
v1MaLen = 50
v1AtrLen = 14
v1SampleNum = 300
)
func BollBackV1(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
Version: 1,
WarmupNum: 1000,
MinTfScore: 0.75,
DrawDownExit: true,
OnStartUp: func(s *strat.StratJob) {
s.More = &MoreType{}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
_, bollMid, _ := ta.BBANDS(e.Close, v1BollLen, 2, 2)
ma := ta.SMA(e.Close, v1MaLen)
atr := ta.ATR(e.High, e.Low, e.Close, v1AtrLen)
//baseAtrLen := int(math.Round(float64(v1AtrLen) * 4.3))
//atrBase := ta.Lowest(ta.Highest(atr, v1AtrLen), baseAtrLen).Get(0)
bollSub := bollMid.Get(0) - bollMid.Get(v1SlopLen)
maSub := ma.Get(0) - ma.Get(v1SlopLen)
if math.IsNaN(maSub) || math.IsNaN(bollSub) {
return
}
bollSlop := bollSub / bollMid.Get(0)
maSlop := maSub / ma.Get(0)
more, _ := s.More.(*MoreType)
if e.BarNum%v1SlopLen == 0 {
more.UpdateSlops(e, bollSlop, maSlop, v1SampleNum)
}
if s.OrderNum > 0 || bollSlop*maSlop < 0 || more.BollUpSlop == 0 {
// 已开单,或者布林中轨和均线方向不一致,或者上下限尚未确定,跳过
return
}
if bollSlop >= more.BollDownSlop && bollSlop <= more.BollUpSlop || maSlop >= more.MaDownSlop && maSlop <= more.MaUpSlop {
// 布林中轨或者均线未突破斜率阈值
return
}
dirt := 1
if bollSlop < 0 {
dirt *= -1
}
atrVal := atr.Get(0) //*0.5 + atrBase*0.5
stopLoss := e.Close.Get(0) - atrVal*float64(dirt)
err := s.OpenOrder(&strat.EnterReq{
Tag: "enter",
StopLoss: stopLoss,
Short: dirt < 0,
})
if err != nil {
log.Error("open fail", zap.Error(err))
}
},
}
}

75
old/boll_back_v2.go Normal file
View File

@@ -0,0 +1,75 @@
package old
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"math"
)
const (
v2SlopLen = 5
v2BollLen = 26
v2AtrLen = 14
v2SampleNum = 300
v2RsiLen = 15
)
func BollBackV2(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
Version: 1,
WarmupNum: 300,
MinTfScore: 0.75,
DrawDownExit: true,
OnStartUp: func(s *strat.StratJob) {
s.More = &MoreType{}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
rsi := ta.RSI(e.Close, v2RsiLen).Get(0) - 50
_, bollMid, _ := ta.BBANDS(e.Close, v2BollLen, 2, 2)
atr := ta.ATR(e.High, e.Low, e.Close, v2AtrLen)
//baseAtrLen := int(math.Round(float64(v2AtrLen) * 4.3))
//atrBase := ta.Lowest(ta.Highest(atr, v2AtrLen), baseAtrLen).Get(0)
bollSlop := bollMid.Get(0)/bollMid.Get(v2SlopLen) - 1
if math.IsNaN(bollSlop) {
return
}
more, _ := s.More.(*MoreType)
if e.BarNum%v2SlopLen == 0 {
more.UpdateSlops(e, bollSlop, 0, v2SampleNum)
}
if s.OrderNum > 0 || bollSlop*rsi > 0 || more.BollUpSlop == 0 || math.Abs(rsi) <= 20 {
// 已开单,或者布林中轨和均线方向不一致,或者上下限尚未确定,跳过
return
}
if bollSlop >= more.BollDownSlop && bollSlop <= more.BollUpSlop {
// 布林中轨或者均线未突破斜率阈值
return
}
dirt := 1
if rsi < 0 {
dirt = -1
}
atrVal := atr.Get(0) * 3 //*0.5 + atrBase*0.5
stopLoss := e.Close.Get(0) - atrVal*float64(dirt)
err := s.OpenOrder(&strat.EnterReq{
Tag: "enter",
StopLoss: stopLoss,
Short: dirt < 0,
})
if err != nil {
log.Error("open fail", zap.Error(err))
}
},
}
}

75
old/breakout_v1.go Normal file
View File

@@ -0,0 +1,75 @@
package old
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
)
/*
适合4h,收益很不稳定
*/
func BreakOutV1(pol *config.RunPolicyConfig) *strat.TradeStrat {
periodBig := 200
periodSml := 30
return &strat.TradeStrat{
Version: 1,
WarmupNum: periodBig,
MinTfScore: 0.75,
//DrawDownExit: true,
OnBar: func(s *strat.StratJob) {
e := s.Env
hhBig := ta.Highest(e.Close, periodBig).Get(1)
//llSml := ta.Lowest(e.Close, periodSml).Get(1)
llBig := ta.Lowest(e.Close, periodBig).Get(1)
//hhSml := ta.Highest(e.Close, periodSml).Get(1)
maSml := ta.SMA(e.Close, periodSml)
maBig := ta.SMA(e.Close, periodBig)
maX := maSml.Cross(maBig)
c := e.Close.Get(0)
openTag := ""
closeTag := ""
if len(s.LongOrders) == 0 && c >= hhBig {
openTag = "long"
} else if len(s.ShortOrders) == 0 && c <= llBig {
openTag = "short"
}
if maX == 1 {
closeTag = "xshort"
} else if maX == -1 {
closeTag = "xlong"
}
//if len(s.LongOrders) > 0 && c <= llSml {
// closeTag = "xlong"
//} else if len(s.ShortOrders) > 0 && c >= hhSml {
// closeTag = "xshort"
//}
if openTag != "" {
err := s.OpenOrder(&strat.EnterReq{
Tag: openTag,
Short: openTag == "short",
})
if err != nil {
log.Error("open fail", zap.Error(err))
}
}
if closeTag != "" {
dirt := core.OdDirtLong
if closeTag == "xshort" {
dirt = core.OdDirtShort
}
err := s.CloseOrders(&strat.ExitReq{
Tag: closeTag,
Dirt: dirt,
})
if err != nil {
log.Error("close fail", zap.Error(err))
}
}
},
}
}

26
old/common.go Normal file
View File

@@ -0,0 +1,26 @@
package old
import (
"github.com/banbox/banbot/strat"
"github.com/banbox/banstagy/old/rev"
"github.com/banbox/banstagy/old/rsi_ptn_rev"
"github.com/banbox/banstagy/old/s01"
"github.com/banbox/banstagy/old/t01"
)
func init() {
strat.AddStratGroup("old", map[string]strat.FuncMakeStrat{
"boll_back0": BollBackV1,
"boll_back": BollBackV2,
"breakout": BreakOutV1,
"ped": Ped,
"corrMean": CorrMeanRev,
"macd": MACD,
"rev": rev.S01V1,
"rsi_ptn_rev": rsi_ptn_rev.MainV1,
"s01v1": s01.MainV1,
"s01v2": s01.MainV2, // 建议版本
"xem4o": xem40,
"t01": t01.Main4HV1,
})
}

165
old/eat.go Normal file
View File

@@ -0,0 +1,165 @@
package old
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"math"
"sort"
)
var (
lenAtr = 9
rangeRate = 0.6 // 预计bar利润区间比率:60%
ShakeTop = 0.2 // 只对分数前20%的标的进行震荡交易
minProfitPct = 0.01 // 最小目标盈利百分比
)
type TMore struct {
symbol string
shakeScore float64 // 震荡分数:(1-平均实体比率)*平均bar波动比率
btmPrice float64 // 平均向下突破比率
topPrice float64 // 平均向上突破比率
shakeOK bool // 是否允许震荡中开单
shakeHit float64 // 震荡首先触发位置:-1触发顶部,1触发底部
}
func Eat(pol *config.RunPolicyConfig) *strat.TradeStrat {
baseAtrLen := int(math.Round(float64(lenAtr) * 5))
return &strat.TradeStrat{
Version: 1,
WarmupNum: max(30, baseAtrLen),
MinTfScore: 0.75,
BatchInfo: true,
OnStartUp: func(s *strat.StratJob) {
s.More = &TMore{
symbol: s.Symbol.Symbol,
}
},
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
return []*strat.PairSub{
{Pair: "_cur_", TimeFrame: "8h", WarmupNum: 30},
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
h, l, c := e.High.Get(0), e.Low.Get(0), e.Close.Get(0)
m, _ := s.More.(*TMore)
if m.shakeOK {
// 允许震荡开单
if m.shakeHit == 0 {
if h >= m.topPrice {
m.shakeHit = -1
} else if l <= m.btmPrice {
m.shakeHit = 1
}
}
if s.OrderNum == 0 && m.shakeHit != 0 {
slRange := (m.topPrice - m.btmPrice)
tag := "long"
trigPrice := m.btmPrice
takeProfit := m.topPrice
if m.shakeHit < 0 {
tag = "short"
trigPrice = m.topPrice
takeProfit = m.btmPrice
}
stopLoss := trigPrice - slRange*m.shakeHit
slDst := (c - stopLoss) * m.shakeHit / slRange
tpDst := (takeProfit - c) * m.shakeHit / slRange
if slDst > 0.5 && tpDst > 0.7 {
// 到止损的距离不低于50%
err := s.OpenOrder(&strat.EnterReq{
Tag: tag,
Short: m.shakeHit < 0,
StopLoss: stopLoss,
TakeProfit: takeProfit,
})
//date := btime.ToDateStr(btime.TimeMS(), "")
//log.Info(fmt.Sprintf("%s %s open %s c:%.3f sl:%.3f, tp:%.3f", s.Symbols.Symbols,
// date, tag, c, stopLoss, takeProfit))
if err != nil {
log.Error("open fail", zap.Error(err))
}
}
}
}
},
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
// ma := ta.EMA(e.Close, 5)
rsi := ta.RSI(e.Close, 9)
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
atrRate := atr.Get(0) / atrBase
// 计算平均实体比率
o, h, l, c := e.Open.Get(0), e.High.Get(0), e.Low.Get(0), e.Close.Get(0)
ph, pl, pc := e.High.Get(1), e.Low.Get(1), e.Close.Get(1)
solidTop, solidBtm := max(o, c), min(o, c)
tRange, pRange := h-l, ph-pl
//tr := e.GetSeries("tRange")
//tr.Set(math.Sqrt(tRange * 100 / c))
//maTRate := ta.EMA(tr, 3)
solRate := e.Close.To("solRate", 0)
solRate.Set(math.Abs(o-c) / tRange)
maSRT := ta.EMA(solRate, 3).Get(0)
// 计算上下行比率
dnRate := e.Close.To("dnRate", 0)
dnRate.Set((l - pc) / pRange)
maDnRate := ta.EMA(dnRate, 5)
upRate := e.Close.To("upRate", 0)
upRate.Set((h - pc) / pRange)
maUpRate := ta.EMA(upRate, 5)
m, _ := s.More.(*TMore)
m.shakeScore = (1 - maSRT) // * maTRate.Get(0)
top := c + maUpRate.Get(0)*tRange
btm := c + maDnRate.Get(0)*tRange
edgeOff := (top - btm) * (1 - rangeRate) * 0.5
m.btmPrice = btm - edgeOff
m.topPrice = top + edgeOff
//m.maChg = (ma.Get(0) - ma.Get(1)) / pRange
topLineRate := (h - solidTop) / tRange
btmLineRate := (solidBtm - l) / tRange
lineDiff := math.Abs(topLineRate-btmLineRate) / max(topLineRate, btmLineRate)
rsiVal, pRsi := rsi.Get(0)-50, rsi.Get(1)-50
m.shakeOK = (top-btm)*rangeRate/c >= minProfitPct && lineDiff < 0.35 && maSRT < 0.3
m.shakeHit = 0
//log.Info(fmt.Sprintf("h: %.3f l:%.3f c:%.3f maSRT: %.3f upRate:%.3f dnRate:%.3f %v", h, l, c,
// maSRT.Get(0), maUpRate.Get(0), maDnRate.Get(0), m.shakeOK))
if math.Abs(rsiVal) > 20 || math.Abs(pRsi) > 20 {
// (ATR较大/反向影线占比大)和RSI都极端时,容易反转,禁止震荡开单
revLineRate := btmLineRate
if rsiVal > 0 {
revLineRate = topLineRate
}
if atrRate > 2.5 || atrRate > 1.3 && revLineRate > 0.75 {
m.shakeOK = false
}
}
},
OnBatchInfos: func(tf string, jobs map[string]*strat.JobEnv) {
// 筛选出所有币中shakeScore降序的前n个,然后在子周期中低买高卖
var mores = make([]*TMore, 0, len(jobs))
for _, job := range jobs {
m, _ := job.Job.More.(*TMore)
mores = append(mores, m)
}
var shakeTopN = int(math.Ceil(float64(len(jobs)) * ShakeTop))
sort.Slice(mores, func(i, j int) bool {
return mores[i].shakeScore > mores[j].shakeScore
})
for _, m := range mores {
if shakeTopN > 0 && m.shakeOK {
shakeTopN -= 1
} else {
m.shakeOK = false
}
}
},
}
}

127
old/macd_v1.go Normal file
View File

@@ -0,0 +1,127 @@
package old
import (
"fmt"
"github.com/banbox/banbot/btime"
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
"github.com/banbox/banstagy/com"
ta "github.com/banbox/banta"
"go.uber.org/zap"
)
func MACD(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
Version: 1,
WarmupNum: 300,
MinTfScore: 0.75,
DrawDownExit: true,
OnStartUp: onStartUp,
OnBar: onBar,
}
}
type MoreMACD struct {
*com.ExmTrace
max1 *com.ExmItem
max2 *com.ExmItem
slope float64 // 斜率
delta float64 // 直线偏移值
}
/*
val: 当前指标值
pNum: 前一个索引
ptime:前一个时间戳
*/
func (m *MoreMACD) update(val float64, pNum int, ptime int64) bool {
if m.ExmTrace.Update(val, pNum, ptime) {
var exm *com.ExmItem
last := m.Hist[len(m.Hist)-1]
if last.Valid && m.ExmTrace.Expect < 0 {
exm = last
} else if len(m.Hist) > 1 && m.ExmTrace.Expect > 0 {
exm = m.Hist[len(m.Hist)-2]
} else {
return false
}
// 更新最高点和次高点
if m.max1 == nil || exm.Value >= m.max1.Value {
m.max1 = exm
m.max2 = nil
} else if m.max1 != nil && pNum-exm.BarNum > 500 {
// 最高点超过500个bar过期
if m.max2 != nil && m.max2.Value > exm.Value {
exm = m.max2
}
m.max1 = m.max2
m.max2 = nil
} else if m.max2 == nil || exm.Value > m.max2.Value {
m.max2 = exm
return true
}
}
return false
}
func onStartUp(s *strat.StratJob) {
s.More = &MoreMACD{
ExmTrace: &com.ExmTrace{},
}
}
func onBar(s *strat.StratJob) {
e := s.Env
macd, _ := ta.MACD(e.Close, 12, 26, 9)
atr := ta.ATR(e.High, e.Low, e.Close, 14).Get(0)
m, _ := s.More.(*MoreMACD)
c := e.Close.Get(0)
if m.slope > 0 && m.max1 != nil && m.max2 != nil {
point := m.slope*float64(e.BarNum) + m.delta
tag := ""
stopLoss := float64(0)
if c-atr*2 > point && len(s.LongOrders) == 0 {
// 突破压力线,做多
tag = "long"
stopLoss = point - atr
} else if c+atr*0.5 > point && c <= point+atr && len(s.ShortOrders) == 0 {
// 触发压力线,开空
tag = "short"
stopLoss = max(c, point) + atr*2
}
if tag != "" && !s.IsWarmUp {
price1 := e.Close.Get(e.BarNum - m.max1.BarNum)
price2 := e.Close.Get(e.BarNum - m.max2.BarNum)
date := btime.ToDateStr(e.TimeStop, "")
date1 := btime.ToDateStr(m.max1.TimeMS, "")
date2 := btime.ToDateStr(m.max2.TimeMS, "")
log.Info(fmt.Sprintf("%s open %s, %s %s %.5f - %s %.5f", date, tag, s.Symbol.Symbol,
date1, price1, date2, price2))
err := s.OpenOrder(&strat.EnterReq{
Tag: tag,
StopLoss: stopLoss,
Short: tag == "short",
})
if err != nil {
log.Error("open fail", zap.Error(err))
}
}
}
if m.update(macd.Get(0), e.BarNum-1, e.TimeStart-e.TFMSecs) {
price1 := e.Close.Get(e.BarNum - m.max1.BarNum)
price2 := e.Close.Get(e.BarNum - m.max2.BarNum)
// 更新斜率和偏移值
m.slope = (price2 - price1) / float64(m.max2.BarNum-m.max1.BarNum)
m.delta = price1 - m.slope*float64(m.max1.BarNum)
//date1 := btime.ToDateStr(m.max1.TimeMS, "")
//date2 := btime.ToDateStr(m.max2.TimeMS, "")
//log.Info(fmt.Sprintf("update pt2, %s %s %.5f - %s %.5f", s.Symbols.Symbols,
// date1, price1, date2, price2))
}
}

71
old/ped.go Normal file
View File

@@ -0,0 +1,71 @@
package old
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
type pedSta struct {
bigDirt int
midRsi float64
}
func Ped(pol *config.RunPolicyConfig) *strat.TradeStrat {
lenAtr := int(pol.Def("atr", 20, core.PNorm(7, 40)))
baseAtrLen := int(float64(lenAtr) * 4.3)
return &strat.TradeStrat{
WarmupNum: 100,
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
return []*strat.PairSub{
{"_cur_", "15m", 100},
{"_cur_", "1h", 205},
}
},
OnStartUp: func(s *strat.StratJob) {
s.More = &pedSta{}
},
OnBar: func(s *strat.StratJob) {
m, _ := s.More.(*pedSta)
e := s.Env
c := e.Close.Get(0)
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
maBig := ta.SMA(e.Close, 10).Get(0)
rsi := ta.RSI(e.Close, 14).Get(0)
if s.OrderNum > 0 {
return
}
if m.bigDirt > 0 && m.midRsi < 70 && c > maBig && rsi > 70 {
s.OpenOrder(&strat.EnterReq{
Tag: "long",
StopLossVal: atrBase,
TakeProfitVal: atrBase * 7,
})
} else if m.bigDirt < 0 && m.midRsi > 30 && c < maBig && rsi < 30 {
s.OpenOrder(&strat.EnterReq{
Tag: "short",
Short: true,
StopLossVal: atrBase,
TakeProfitVal: atrBase * 7,
})
}
},
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
m, _ := s.More.(*pedSta)
if tf == "1h" {
maSml := ta.SMA(e.Close, 20).Get(0)
maBig := ta.SMA(e.Close, 100).Get(0)
if maSml > maBig {
m.bigDirt = 1
} else {
m.bigDirt = -1
}
} else {
// 15m
m.midRsi = ta.RSI(e.Close, 14).Get(0)
}
},
}
}

119
old/rev/01.go Normal file
View File

@@ -0,0 +1,119 @@
package rev
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/orm/ormo"
"github.com/banbox/banbot/strat"
"github.com/banbox/banstagy/com"
ta "github.com/banbox/banta"
"math"
)
type MoreRev struct {
thres float64 // 阈值
dirt float64 // 1rsi高于0,-1rsi低于0
hitAt int // 进入极值的BarNum
exmVal float64 // 最大极值
bigRsi float64 // 大周期rsi
}
func (m *MoreRev) update(rsi50 float64, barNum int) {
rsiAbs := math.Abs(rsi50)
if rsiAbs < m.thres {
m.dirt = 0
m.hitAt = 0
m.exmVal = 0
return
}
if m.dirt == 0 {
if rsi50 > 0 {
m.dirt = 1
} else {
m.dirt = -1
}
m.hitAt = barNum
m.exmVal = rsiAbs
} else if m.exmVal < rsiAbs {
m.exmVal = rsiAbs
}
}
func S01V1(pol *config.RunPolicyConfig) *strat.TradeStrat {
var lenRsi = 22
var lenRsi2 = 140
var thresRsi = 20.0
var hitRsi = 22.0
var minKeep = 3
lenAtr := 15
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
return &strat.TradeStrat{
Version: 1,
WarmupNum: lenRsi * 2,
MinTfScore: 0.75,
DrawDownExit: true,
OnStartUp: func(s *strat.StratJob) {
s.More = &MoreRev{thres: thresRsi}
},
OnPairInfos: com.MakePairInfos(lenRsi2),
OnBar: func(s *strat.StratJob) {
e := s.Env
m, _ := s.More.(*MoreRev)
rsi := ta.RSI(e.Close, lenRsi)
rsiMa := ta.SMA(rsi, 3)
rsiVal := rsi.Get(0)
rsiX := rsi.Cross(rsiMa)
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
atrRate := math.Sqrt(atr.Get(0) / atrBase)
c := e.Close.Get(0)
bigRsiFit := math.Abs(m.bigRsi) > 5 && m.bigRsi*m.dirt > 0
rsiFit := m.dirt != 0 && m.exmVal >= hitRsi && e.BarNum-m.hitAt >= minKeep
if s.OrderNum == 0 && bigRsiFit && rsiFit && (rsiX == 1 || rsiX == -1) {
// rsi处于极值范围,且触发最大极值,且满足最少极值数量,且和rsi均值交叉
slVal := atrBase * atrRate * 5
slLong, slShort := c-slVal, c+slVal
//entPrice := c - slVal*0.3
//if m.dirt > 0 {
// entPrice = c - slVal*0.3
//}
com.FastOpen(s, -m.dirt, slLong, slShort, 0)
} else if rsiFit && s.EnteredNum > 0 && m.bigRsi*m.dirt > 0 {
if m.dirt == 1 && len(s.LongOrders) > 0 && m.bigRsi > 2 {
// 平多
_ = s.CloseOrders(&strat.ExitReq{
Tag: "exitL",
Dirt: core.OdDirtLong,
})
} else if m.dirt == -1 && len(s.ShortOrders) > 0 && m.bigRsi < -2 {
_ = s.CloseOrders(&strat.ExitReq{
Tag: "exitS",
Dirt: core.OdDirtShort,
})
}
}
m.update(rsiVal-50, e.BarNum)
},
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
m, _ := s.More.(*MoreRev)
m.bigRsi = ta.RSI50(e.Close, lenRsi2).Get(0)
},
GetDrawDownExitRate: func(s *strat.StratJob, od *ormo.InOutOrder, maxChg float64) float64 {
sl := od.GetStopLoss()
if sl == nil || sl.Range == 0 {
return 0
}
bestRate := maxChg * od.InitPrice / sl.Range
if bestRate > 2 {
return 0.3
} else if bestRate > 1 {
return 0.5
}
return 0
},
}
}

57
old/rsi_ptn_rev/v1.go Normal file
View File

@@ -0,0 +1,57 @@
package rsi_ptn_rev
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
"github.com/banbox/banstagy/ptn"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"math"
)
const (
v1RsiLen = 9
v1RsiThres = 25
v1AtrLen = 14
)
func MainV1(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
Version: 1,
WarmupNum: 30,
MinTfScore: 0.75,
DrawDownExit: true,
OnBar: func(s *strat.StratJob) {
e := s.Env
rsi := ta.RSI(e.Close, v1RsiLen).Get(0) - 50
atr := ta.ATR(e.High, e.Low, e.Close, v1AtrLen)
baseAtrLen := int(math.Round(float64(v1AtrLen) * 4.3))
atrBase := ta.Lowest(ta.Highest(atr, v1AtrLen), baseAtrLen).Get(0)
atrVal := atr.Get(0)
if math.IsNaN(atrBase) || math.Abs(rsi) <= v1RsiThres || s.OrderNum > 0 || atrVal < atrBase {
return
}
ptn := ptn.Recognize(e)
if ptn == nil {
// 未识别到蜡烛形态,或者形态与RSI相反,忽略
return
}
ptnDirt := float64(ptn.Dirt)
if rsi*ptnDirt > 0 {
return
}
stopLoss := e.Close.Get(0) - atrVal*ptnDirt
err := s.OpenOrder(&strat.EnterReq{
Tag: "enter",
StopLoss: stopLoss,
Short: ptnDirt < 0,
})
if err != nil {
log.Error("open fail", zap.Error(err))
}
},
}
}

163
old/s01/main.go Normal file
View File

@@ -0,0 +1,163 @@
package s01
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"math"
)
/*
此策略对应python原始策略名为PivotRev
【s01_base】
是最初版本的策略,所有标的使用固定的RSI差阈值:300周期9, 50周期20
【s01_adp.50】~【s01_adp.100】
增加了标的自适应的前20个高于固定阈值50%~100%的所有RSI值的均值,作为实际阈值。
开单数量倍增,曲线稍微平滑一些,收益更高。
测试最佳值是54%
【s01_adpb.55】~【s01_adp.70】
将bigTmp和smlTmp同时满足时才记录,前面是各自达到阈值就记录。
改了后订单减少40%左右,收益减少30%左右。
不采用
后续考虑记录RSI300和RSI50的历史峰值,然后峰值排序,取Top50%的均值作为阈值。
*/
var (
RsiBig = 300
RsiSml = 50
ThsBig = float64(9)
ThsSml = float64(20)
AtrLen = 14
bigMin = float64(ThsBig) * 0.55
smlMin = float64(ThsSml) * 0.55
AvgLastNum = 20
)
type MoreType struct {
hit bool
bigThsHis []float64
smlThsHis []float64
bigThs float64
smlThs float64
bigTmp float64
smlTmp float64
}
func (m *MoreType) close() {
if m.bigTmp > bigMin {
m.bigThsHis = append(m.bigThsHis, m.bigTmp)
if len(m.bigThsHis) > AvgLastNum {
m.bigThsHis = m.bigThsHis[len(m.bigThsHis)-AvgLastNum:]
}
sumVal := float64(0)
for _, val := range m.bigThsHis {
sumVal += val
}
m.bigThs = sumVal / float64(len(m.bigThsHis))
}
if m.smlTmp > smlMin {
m.smlThsHis = append(m.smlThsHis, m.smlTmp)
if len(m.smlThsHis) > AvgLastNum {
m.smlThsHis = m.smlThsHis[len(m.smlThsHis)-AvgLastNum:]
}
sumSml := float64(0)
for _, val := range m.smlThsHis {
sumSml += val
}
m.smlThs = sumSml / float64(len(m.smlThsHis))
}
m.bigTmp = -1
m.smlTmp = -1
m.hit = false
}
func (m *MoreType) check(big, sml float64) {
if m.bigTmp < 0 {
m.bigTmp = math.Abs(big)
} else {
m.bigTmp = max(m.bigTmp, math.Abs(big))
}
if m.smlTmp < 0 {
m.smlTmp = math.Abs(sml)
} else {
m.smlTmp = max(m.smlTmp, math.Abs(sml))
}
}
func MainV0(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
Version: 1,
WarmupNum: RsiBig * 10,
MinTfScore: 0.75,
DrawDownExit: true,
OnStartUp: func(s *strat.StratJob) {
s.More = &MoreType{
bigThs: ThsBig,
smlThs: ThsSml,
bigTmp: -1,
smlTmp: -1,
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
ma5 := ta.EMA(e.Close, 5)
ma20 := ta.EMA(e.Close, 20)
maX := ma5.Cross(ma20)
rsiB := ta.RSI(e.Close, RsiBig).Get(0) - 50
rsiS := ta.RSI(e.Close, RsiSml).Get(0) - 50
atr := ta.ATR(e.High, e.Low, e.Close, AtrLen)
baseAtrLen := int(math.Round(float64(AtrLen) * 4.3))
atrB := ta.Lowest(ta.Highest(atr, AtrLen), baseAtrLen).Get(0)
if math.IsNaN(rsiB) || math.IsInf(rsiB, 0) {
// 过滤无效值
return
}
m, _ := s.More.(*MoreType)
if math.Abs(float64(maX)) == 1 {
// 均线交叉,顶底切换,允许开单
m.close()
}
m.check(rsiB, rsiS)
if m.hit {
// 已开单,禁止重复开单
return
}
if math.Abs(rsiB) <= m.bigThs || math.Abs(rsiS) <= m.smlThs {
// 未触发阈值,不开单
return
}
m.hit = true // 禁止同方向再次开单
isShort := rsiB > 0
odDirt := float64(1)
if isShort {
odDirt = -1
}
stopLoss := e.Close.Get(0) - max(atrB*3, atr.Get(0)*1.5)*odDirt
err := s.OpenOrder(&strat.EnterReq{
Tag: "enter",
Short: isShort,
StopLoss: stopLoss,
})
if err != nil {
log.Error("open order fail", zap.String("pair", s.Symbol.Symbol),
zap.String("stagy", s.Strat.Name), zap.Error(err))
}
},
}
}

71
old/s01/v1.go Normal file
View File

@@ -0,0 +1,71 @@
package s01
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"math"
)
/*
此策略对应python原始策略名为PivotRev
*/
func MainV1(pol *config.RunPolicyConfig) *strat.TradeStrat {
RsiBig = 60
RsiSml = 14
ThsBig = 20
ThsSml = 30
AtrLen = 14
return &strat.TradeStrat{
Version: 1,
WarmupNum: RsiBig,
MinTfScore: 0.75,
DrawDownExit: true,
OnBar: func(s *strat.StratJob) {
e := s.Env
ma5 := ta.EMA(e.Close, 5)
ma20 := ta.EMA(e.Close, 20)
maX := ma5.Cross(ma20)
rsiB := ta.RSI(e.Close, RsiBig).Get(0) - 50
rsiS := ta.RSI(e.Close, RsiSml).Get(0) - 50
atr := ta.ATR(e.High, e.Low, e.Close, AtrLen)
basev1AtrLen := int(math.Round(float64(AtrLen) * 4.3))
atrB := ta.Lowest(ta.Highest(atr, AtrLen), basev1AtrLen).Get(0)
if math.Abs(float64(maX)) == 1 {
// 均线交叉,顶底切换,允许开单
s.More = nil
} else if s.More != nil {
// 禁止开单
return
}
if math.Abs(rsiB) <= ThsBig || math.Abs(rsiS) <= ThsSml {
// 未触发阈值,不开单
return
}
s.More = true // 禁止同方向再次开单
isShort := rsiB > 0
odDirt := float64(1)
if isShort {
odDirt = -1
}
stopLoss := e.Close.Get(0) - max(atrB*3, atr.Get(0)*1.5)*odDirt
err := s.OpenOrder(&strat.EnterReq{
Tag: "enter",
Short: isShort,
StopLoss: stopLoss,
})
if err != nil {
log.Error("open order fail", zap.String("pair", s.Symbol.Symbol),
zap.String("stagy", s.Strat.Name), zap.Error(err))
}
},
}
}

120
old/s01/v2.go Normal file
View File

@@ -0,0 +1,120 @@
package s01
import (
"fmt"
"github.com/banbox/banbot/btime"
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/orm/ormo"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"math"
)
type S1More struct {
JumpRates map[int64]float64
HitNum int // 记录当前是否已开过单。对于单边行情,为避免重复开单,只有当rsi转折时才开单
}
func MainV2(pol *config.RunPolicyConfig) *strat.TradeStrat {
RsiSml = 14
ThsSml = 40
AtrLen = 14
return &strat.TradeStrat{
Version: 1,
WarmupNum: RsiBig,
MinTfScore: 0.1,
DrawDownExit: true,
BatchInOut: true,
OnStartUp: func(s *strat.StratJob) {
s.More = &S1More{
JumpRates: make(map[int64]float64),
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
rsi := ta.RSI(e.Close, RsiSml)
rsiS := rsi.Get(0) - 50
atr := ta.ATR(e.High, e.Low, e.Close, 14).Get(0)
m, _ := s.More.(*S1More)
// 检查是否RSI多次触发,且首次转折
openOK := m.HitNum > 1 && math.Abs(rsiS) < math.Abs(rsi.Get(1)-50)
if math.Abs(rsiS) < ThsSml && !openOK {
// rsi低于阈值,且不属于多次触发后转折
m.HitNum = 0
return
}
if rsiS > 0 && len(s.ShortOrders) > 0 || rsiS < 0 && len(s.LongOrders) > 0 {
// 已有入场订单,不重复开单
return
}
m.HitNum += 1
if m.HitNum > 1 && !openOK {
// 重复触发,只允许rsi调头时第二次入场
return
}
c := e.Close.Get(0)
stopLoss := c - atr*2
tag := "long"
if rsiS > 0 {
stopLoss = c + atr*2
tag = "short"
}
err := s.OpenOrder(&strat.EnterReq{
Tag: tag,
Short: rsiS > 0,
StopLoss: stopLoss,
})
if err != nil {
log.Error("open order fail", zap.String("pair", s.Symbol.Symbol),
zap.String("stagy", s.Strat.Name), zap.Error(err))
}
},
OnBatchJobs: func(jobs []*strat.StratJob) {
openNum := float64(0)
for _, j := range jobs {
if len(j.Entrys) > 0 {
openNum += 1
}
}
openRate := openNum / float64(len(jobs))
// 这里无需限制openRate的上限,前面已禁止单个趋势重复多次开单
if openRate < 0.2 {
for _, j := range jobs {
j.Entrys = nil
}
return
}
log.Info(fmt.Sprintf("%s open orders, num: %v, total: %v, rate: %.2f",
btime.ToDateStr(btime.TimeMS(), ""), openNum, len(jobs), openRate))
},
GetDrawDownExitRate: func(s *strat.StratJob, od *ormo.InOutOrder, maxChg float64) float64 {
more, _ := s.More.(*S1More)
jumpRate, ok := more.JumpRates[od.ID]
if !ok {
e := s.Env
jumpRate = (e.Close.Get(RsiSml)/e.Close.Get(0) - 1) * 0.3
more.JumpRates[od.ID] = jumpRate
}
var rate float64
switch {
case maxChg > jumpRate*15:
rate = 0.15
case maxChg > jumpRate*10:
rate = 0.17
case maxChg > jumpRate*3:
rate = 0.25
case maxChg > jumpRate*1:
rate = 0.37
case maxChg > jumpRate*0.5:
rate = 0.5
default:
rate = 0
}
return rate
},
}
}

64
old/s01/v3.go Normal file
View File

@@ -0,0 +1,64 @@
package s01
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
"math"
"slices"
)
func MainV3(pol *config.RunPolicyConfig) *strat.TradeStrat {
RsiSml = 100
watchBig := 5
watchSml := 3
threshold := float64(17)
return &strat.TradeStrat{
Version: 1,
WarmupNum: RsiBig,
MinTfScore: 0.75,
//DrawDownExit: true,
OnBar: func(s *strat.StratJob) {
e := s.Env
rsi := ta.RSI(e.Close, RsiSml)
sml := ta.SMA(rsi, watchSml)
big := ta.SMA(rsi, watchBig)
atr := ta.ATR(e.High, e.Low, e.Close, 14).Get(0)
crx := sml.Cross(big)
if crx != 1 && crx != -1 {
return
}
rsiVal := float64(0)
if rsi.Get(watchBig) > 50 {
rsiVal = slices.Max(rsi.Range(0, watchBig*2))
} else {
rsiVal = slices.Min(rsi.Range(0, watchBig*2))
}
if math.Abs(rsiVal-50) < threshold {
return
}
c := e.Close.Get(0)
if crx == -1 {
_ = s.CloseOrders(&strat.ExitReq{
Tag: "x_short",
Dirt: core.OdDirtShort,
})
_ = s.OpenOrder(&strat.EnterReq{
Tag: "long",
StopLoss: c - atr*10,
})
} else if crx == 1 {
_ = s.CloseOrders(&strat.ExitReq{
Tag: "x_long",
Dirt: core.OdDirtLong,
})
_ = s.OpenOrder(&strat.EnterReq{
Tag: "short",
Short: true,
StopLoss: c + atr*10,
})
}
},
}
}

23
old/t01/1h.go Normal file
View File

@@ -0,0 +1,23 @@
package t01
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
)
func Main1HV1(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 最适合1h的参数,测试20240101-20240324,50币,无需大周期趋势
maSml = 10
openOffRate = -1.5
badRate = 1.5
goodRate = 1
return &strat.TradeStrat{
Version: 1,
WarmupNum: 300,
MinTfScore: 0.75,
//OnPairInfos: onPairInfos,
OnStartUp: onStartUp,
//OnInfoBar: onInfoBar,
OnBar: onBar,
}
}

BIN
old/t01/20240322_24_1h.xlsx Normal file

Binary file not shown.

BIN
old/t01/20240322_24_4h.xlsx Normal file

Binary file not shown.

BIN
old/t01/20240322_24_5m.xlsx Normal file

Binary file not shown.

23
old/t01/4h.go Normal file
View File

@@ -0,0 +1,23 @@
package t01
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
)
func Main4HV1(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 最适合4h的参数,测试20240101-20240324,50币,无需大周期趋势
maSml = 10
openOffRate = -1
badRate = 1.5
goodRate = 1
return &strat.TradeStrat{
Version: 1,
WarmupNum: 300,
MinTfScore: 0.75,
//OnPairInfos: onPairInfos,
OnStartUp: onStartUp,
//OnInfoBar: onInfoBar,
OnBar: onBar,
}
}

138
old/t01/common.go Normal file
View File

@@ -0,0 +1,138 @@
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)
}
}
}

27
old/t01/v1.go Normal file
View File

@@ -0,0 +1,27 @@
package t01
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banstagy/com"
)
func MainV1(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 最适合5m的参数,测试20240322-20240324,50币,无需大周期趋势
maSml = 10
maBig = 50
maParent = 30
openOffRate = 0.5
badRate = 3
goodRate = 3
return &strat.TradeStrat{
Version: 1,
WarmupNum: 300,
MinTfScore: 0.75,
//DrawDownExit: true,
OnStartUp: onStartUp,
OnBar: onBar,
OnPairInfos: com.MakePairInfos(sampleNum*sampleStep + maParent),
OnInfoBar: onInfoBar,
}
}

89
old/xem4o.go Normal file
View File

@@ -0,0 +1,89 @@
package old
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/exg"
"github.com/banbox/banbot/orm/ormo"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
"math"
"strconv"
)
/*
这是针对XEM/USDT:USDT的策略,其最小价格变动0.6%,挂单等待成交盈利很稳定。
但实盘时实际成交量很小,挂单十几分钟才吃进去。
*/
func xem40(p *config.RunPolicyConfig) *strat.TradeStrat {
lenAtr := 14
return &strat.TradeStrat{
WarmupNum: 300,
MinTfScore: 0.3,
EachMaxLong: 1,
EachMaxShort: 1,
OnStartUp: func(s *strat.StratJob) {
pip, err := exg.Default.PriceOnePip(s.Symbol.Symbol)
if err != nil {
panic(err)
}
s.More = pip
},
OnBar: func(s *strat.StratJob) {
e := s.Env
c := e.Close.Get(0)
pip := s.More.(float64)
if pip/c < 0.002 {
// 仅针对XEM,最小价格变动是0.6%
return
}
volMa := ta.SMA(e.Volume, 14).Get(0)
maSml := ta.SMA(e.Close, 50).Get(0)
maBig := ta.SMA(e.Close, 300).Get(0)
rsi := ta.RSI50(e.Close, 50).Get(0)
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr).Get(0)
if s.OrderNum > 0 {
var od *ormo.InOutOrder
if len(s.LongOrders) > 0 {
od = s.LongOrders[0]
} else {
od = s.ShortOrders[0]
}
if od.Status == ormo.InOutStatusPartEnter && (e.TimeStop-od.RealEnterMS())/e.TFMSecs > 3 {
s.CloseOrders(&strat.ExitReq{Tag: "cancel", UnFillOnly: true})
}
return
}
sub1, sub2 := c-maSml, maSml-maBig
dirt := 0
if sub1*sub2 <= 0 && math.Abs(sub2) < atr {
// 震荡行情
if rsi > 3 {
dirt = -1
} else if rsi < -3 {
dirt = 1
}
}
if dirt == 0 && math.Abs(sub2) < pip {
// 波动很小,直接开单
dirt = 1
if c > maSml {
dirt = -1
}
}
if dirt != 0 {
amt := min(volMa*0.1, config.StakeAmount/c)
s.OpenOrder(&strat.EnterReq{
Tag: strconv.Itoa(dirt),
Short: dirt < 0,
Limit: c,
Amount: amt,
StopLossVal: pip * 2,
TakeProfitVal: pip,
StopBars: 20,
})
}
},
}
}