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

33
org/digo/common.go Normal file
View File

@@ -0,0 +1,33 @@
package digo
import (
"github.com/banbox/banbot/strat"
"math"
)
func init() {
strat.AddStratGroup("digo", map[string]strat.FuncMakeStrat{
"v2": v2,
"v4": v4,
})
}
var (
trendFilter = true // 是否启用趋势过滤(逆趋势开单降低权重)
goodWeight = float64(1) // 顺趋势开单权重,默认1
badWeight = float64(0) // 逆趋势开单权重,默认0不开单
badMaxRate = -0.05 // 开单后允许趋势相反最大价格比率
goodMaxRate = 0.005
stopLossRate = 0.02
)
func calcFactor(pFac float64) float64 {
odNum := math.Log2(pFac) + 1
return math.Pow(2, odNum)
}
type MoreType struct {
bigDirt int // 1牛市,0不确定,1熊市
maBig float64 // 大周期MA,用于判断趋势
signal float64 // 最新bar是否有信号,1开多,-1开空
}

209
org/digo/v1.go Normal file
View File

@@ -0,0 +1,209 @@
package digo
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/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"math"
"slices"
)
func openOrder(s *strat.StratJob, m *MoreType, signal float64) {
odWeight := goodWeight
if trendFilter && m.bigDirt != int(signal) {
// 启用趋势过滤,且是逆趋势
odWeight = badWeight
}
if odWeight == 0 {
return
}
allOrders := s.GetOrders(signal)
orders := make([]*ormo.InOutOrder, 0, len(allOrders))
for _, od := range allOrders {
if od.EnterTag != core.EnterTagThird {
orders = append(orders, od)
}
}
enteredAmt := float64(0)
for _, od := range orders {
enteredAmt += od.Enter.Filled
}
c := s.Env.Close.Get(0)
basePrice := max(c, m.maBig)
if signal > 0 {
basePrice = min(c, m.maBig)
}
stopPrice := basePrice * (1 - signal*stopLossRate)
if enteredAmt == 0 {
// 没有打开的订单,直接下单
err := s.OpenOrder(&strat.EnterReq{
Tag: "enter",
Short: signal < 0,
CostRate: odWeight,
StopLoss: stopPrice,
})
if err != nil {
log.Error("open order fail", zap.Error(err))
}
return
}
slices.SortFunc(orders, func(a, b *ormo.InOutOrder) int {
return int(a.RealEnterMS() - b.RealEnterMS())
})
prevOd := orders[len(orders)-1]
// 检查价格是否背离订单5%
chgRate := c/prevOd.InitPrice - 1
if signal*chgRate >= badMaxRate {
// 价格背离未超过5%,不加仓
return
}
// 以上一个订单的两倍开单
baseAmt := s.Strat.GetStakeAmount(s)
prevFac := prevOd.GetInfoFloat64(ormo.OdInfoLegalCost) / baseAmt
costRate := calcFactor(prevFac)
err := s.OpenOrder(&strat.EnterReq{
Tag: "enter",
Short: signal < 0,
CostRate: odWeight * costRate,
StopLoss: stopPrice,
})
if err != nil {
log.Error("open order fail", zap.Error(err))
}
}
func closeOrders(s *strat.StratJob, signal float64) {
allOrders := s.GetOrders(signal)
orders := make([]*ormo.InOutOrder, 0, len(allOrders))
totalProfit := float64(0)
for _, od := range allOrders {
if od.EnterTag != core.EnterTagThird {
orders = append(orders, od)
totalProfit += od.Profit
}
}
if len(orders) < 3 {
return
}
if totalProfit > 0 {
err := s.CloseOrders(&strat.ExitReq{
Tag: "exit",
Dirt: int(signal),
})
if err != nil {
log.Error("close orders fail", zap.Error(err))
}
}
}
func MainV1(pol *config.RunPolicyConfig) *strat.TradeStrat {
trendFilter = true
goodWeight = 1
badWeight = 0
stopLossRate = 0.02
badMaxRate = -0.05
goodMaxRate = 0.005
return &strat.TradeStrat{
Version: 1,
WarmupNum: 361,
MinTfScore: 0.75,
DrawDownExit: true,
StopEnterBars: 10,
//OnPairInfos: func(s *strategy.StratJob) []*strategy.PairSub {
// return []*strategy.PairSub{
// {
// Key: "_cur_",
// TimeFrame: "1d",
// WarmupNum: 60,
// },
// }
//},
OnStartUp: func(s *strat.StratJob) {
s.More = &MoreType{}
},
//OnInfoBar: func(s *strategy.StratJob, pair, tf string) {
// m, _ := s.More.(*MoreType)
// m.maBig = ta.SMA(s.Env.Close, 60).Get(0)
//},
OnBar: func(s *strat.StratJob) {
e := s.Env
_, d, _ := ta.KDJBy(e.High, e.Low, e.Close, 13, 3, 3, "sma")
ma3_d := ta.SMA(d, 3)
//fmt.Printf("%s: %.3f %.3f %.3f\n", btime.ToDateStr(e.TimeStart, ""),
// k.Get(0), d.Get(0), ma3_d.Get(0))
c := e.Close.Get(0)
m, _ := s.More.(*MoreType)
m.maBig = ta.EMA(e.Close, 360).Get(0)
if math.IsNaN(m.maBig) {
m.bigDirt = 0
} else {
m.bigDirt = -1
if c >= m.maBig {
// K线高于大周期均线,牛市
m.bigDirt = 1
}
}
zhicheng := ma3_d.Cross(20) == -1
yali := ma3_d.Cross(80.5) == 1
m.signal = float64(0)
if yali {
m.signal = -1
} else if zhicheng {
m.signal = 1
} else {
return
}
if m.bigDirt != 0 {
openOrder(s, m, m.signal)
}
closeOrders(s, m.signal)
},
OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq {
odDirt := float64(1)
if od.Short {
odDirt = -1
}
m, _ := s.More.(*MoreType)
c := s.Env.Close.Get(0)
basePrice := max(od.InitPrice, m.maBig)
if odDirt > 0 {
basePrice = min(od.InitPrice, m.maBig)
}
stopPrice := basePrice * (1 - odDirt*stopLossRate)
od.SetStopLoss(&ormo.ExitTrigger{Price: stopPrice})
priceBk := c - stopPrice
if odDirt*priceBk < 0 {
// 触发止损,立刻卖出
return &strat.ExitReq{
Tag: core.ExitTagStopLoss,
OrderID: od.ID,
}
}
if m.signal == 0 || m.signal == odDirt {
// 没有信号,或顺趋势信号,退出
return nil
}
// 出现逆趋势信号
entPrice := od.InitPrice
if od.Enter.Price != 0 {
entPrice = od.Enter.Price
}
chgRate := c/entPrice - 1
if odDirt*chgRate >= goodMaxRate {
// 价格盈利超过阈值,卖出止盈
return &strat.ExitReq{
Tag: "stop_win",
OrderID: od.ID,
}
}
return nil
},
}
}

181
org/digo/v2.go Normal file
View File

@@ -0,0 +1,181 @@
package digo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/orm/ormo"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
type MoreV2 struct {
bigEma199 float64
}
/*
# 趋势判断
趋势多头 = EMA360 > 日线EMA199 且 当前价格 > EMA360
趋势空头 = EMA360 < 日线EMA199 且 当前价格 < EMA360
# 震荡判断
震荡偏多 = min(EMA360, 当前价格) > 日线EMA199 且 当前价格 < EMA360
震荡偏空 = max(EMA360, 当前价格) < 日线EMA199 且 当前价格 > EMA360
如果是震荡偏多市场:
在支撑位:开多单(1倍杠杆),平掉空单
在压力位:开小仓位空单(0.5倍杠杆),平掉多单
如果是震荡偏空市场:
在支撑位:开小仓位多单(0.5倍杠杆),平掉空单
在压力位:开空单(1倍杠杆),平掉多单
止损逐bar更新:
震荡偏空/震荡偏多:
多单止损:min(ema360, bigEma199)下方2个点
空单止损:max(ema360, bigEma199)上方2个点
*/
func v2(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 定义策略参数
smoothK := int(pol.Def("smoothK", 13, core.PUniform(8, 20)))
smoothD := int(pol.Def("smoothD", 3, core.PUniform(2, 5)))
//bSmaLen := int(pol.Def("bSmaLen", 9, core.PUniform(5, 15)))
//bSmaDev := 2.0
slRate := 0.02
return &strat.TradeStrat{
WarmupNum: 600, // 需要足够的预热周期来计算EMA360
EachMaxLong: 1, // 最多同时持有1笔多单
EachMaxShort: 1,
OnStartUp: func(s *strat.StratJob) {
s.More = &MoreV2{}
},
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
return []*strat.PairSub{
{"_cur_", "1d", 360},
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
m, _ := s.More.(*MoreV2)
c := e.Close.Get(0)
ema360 := ta.EMA(e.Close, 360).Get(0)
var trend = 0 // 2 多头, 1 震荡偏多, 0 不确定, -1 震荡偏空 -2 空头
if c > ema360 && ema360 > m.bigEma199 {
// 多头排列
trend = 2
} else if c < ema360 && ema360 < m.bigEma199 {
// 空头排列
trend = -2
} else if min(c, ema360) > m.bigEma199 && c < ema360 {
// 震荡偏多,
trend = 1
} else if max(c, ema360) < m.bigEma199 && c > ema360 {
// 震荡偏空
trend = -1
}
_, d, _ := ta.KDJBy(e.High, e.Low, e.Close, smoothK, 3, smoothD, "sma")
dSMA := ta.SMA(d, 3)
// 判断支撑压力
isUpp := dSMA.Cross(20) == -1 // crossunder
isDww := dSMA.Cross(80.5) == 1 // crossover
// 计算布林带
//bbCols := ta.BBANDS(e.Close, bSmaLen, bSmaDev, bSmaDev).Cols
//bbUpper, bbLower := bbCols[0].Get(0), bbCols[2].Get(0)
//h := e.High.Get(0)
isYali := isDww // && h > bbUpper*1.08
isZhicheng := isUpp // && c < bbLower*1.08
// 计算止损
slLong, slShort := 0.0, 0.0
if trend == 1 || trend == -1 {
slLong = min(ema360, m.bigEma199) * (1 - slRate)
slShort = max(ema360, m.bigEma199) * (1 + slRate)
for _, od := range s.LongOrders {
od.SetStopLoss(&ormo.ExitTrigger{
Price: slLong,
})
}
for _, od := range s.ShortOrders {
od.SetStopLoss(&ormo.ExitTrigger{
Price: slShort,
})
}
}
// 开仓和平仓逻辑
longRate, shortRate := 1.0, 1.0
if trend == 1 {
// 震荡偏多
longRate, shortRate = 1, 0.5
} else if trend == -1 {
// 震荡偏空
longRate, shortRate = 0.5, 1
}
if trend == 1 || trend == -1 {
if isZhicheng {
// 支撑位开多
s.OpenOrder(&strat.EnterReq{
Tag: "longZC1",
CostRate: longRate,
StopLoss: slLong,
})
s.CloseOrders(&strat.ExitReq{
Tag: "zc1",
Dirt: core.OdDirtShort,
})
} else if isYali {
//_ = shortRate
// 压力位半仓开空
s.OpenOrder(&strat.EnterReq{
Tag: "shortYL1",
CostRate: shortRate,
Short: true,
StopLoss: slShort,
})
s.CloseOrders(&strat.ExitReq{
Tag: "yl1",
Dirt: core.OdDirtLong,
})
}
}
//if isZhicheng && trend == 2 && s.OrderNum == 0 {
// // 支撑位开多
// s.OpenOrder(&strat.EnterReq{
// Tag: "support_long",
// Short: false,
// })
//} else if isYali && trend == 2 && s.OrderNum > 0 {
// // 压力位平多
// s.CloseOrders(&strat.ExitReq{
// Tag: "resistance_close",
// EnterTag: "support_long",
// Dirt: core.OdDirtLong,
// })
//}
//if trend == -2 && isYali && s.OrderNum == 0 {
// // 顺势空,压力位开空
// s.OpenOrder(&strat.EnterReq{
// Tag: "resi_short",
// Short: true,
// })
//} else if trend == -2 && isZhicheng && s.OrderNum > 0 {
// s.CloseOrders(&strat.ExitReq{
// Tag: "supp_close",
// EnterTag: "resi_short",
// Dirt: core.OdDirtShort,
// })
//}
},
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
m, _ := s.More.(*MoreV2)
m.bigEma199 = ta.EMA(e.Close, 199).Get(0)
},
}
}

112
org/digo/v4.go Normal file
View File

@@ -0,0 +1,112 @@
package digo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/orm/ormo"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
"math"
)
/*
# 趋势判断
趋势多头 = EMA360 > 日线EMA199 且 当前价格 > EMA360
趋势空头 = EMA360 < 日线EMA199 且 当前价格 < EMA360
# 震荡判断
震荡偏多 = min(EMA360, 当前价格) > 日线EMA199 且 当前价格 < EMA360
震荡偏空 = max(EMA360, 当前价格) < 日线EMA199 且 当前价格 > EMA360
如果是震荡偏多市场:
在支撑位:开多单(1倍杠杆),平掉空单
在压力位:开小仓位空单(0.5倍杠杆),平掉多单
如果是震荡偏空市场:
在支撑位:开小仓位多单(0.5倍杠杆),平掉空单
在压力位:开空单(1倍杠杆),平掉多单
止损逐bar更新:
震荡偏空/震荡偏多:
多单止损:min(ema360, bigEma199)下方2个点
空单止损:max(ema360, bigEma199)上方2个点
*/
func v4(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 定义策略参数
smoothK := int(pol.Def("smoothK", 13, core.PUniform(8, 20)))
smoothD := int(pol.Def("smoothD", 3, core.PUniform(2, 5)))
//bSmaLen := int(pol.Def("bSmaLen", 9, core.PUniform(5, 15)))
//bSmaDev := 2.0
slRate := 0.05
tpRate := 0.05
return &strat.TradeStrat{
WarmupNum: 600, // 需要足够的预热周期来计算EMA360
EachMaxLong: 1, // 最多同时持有1笔多单
EachMaxShort: 1,
OnStartUp: func(s *strat.StratJob) {
s.More = &MoreV2{}
},
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
return []*strat.PairSub{
{"_cur_", "1d", 360},
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
m, _ := s.More.(*MoreV2)
c := e.Close.Get(0)
ema360 := ta.EMA(e.Close, 360).Get(0)
ema169 := ta.EMA(e.Close, 169).Get(0)
var isLong = false
if ema169 > m.bigEma199 && c > ema360 {
//顺势偏多
isLong = true
}
// 收盘价偏离ema169的百分比
devRangePct := math.Abs(c-ema169) * 100 / ema169
_, d, _ := ta.KDJBy(e.High, e.Low, e.Close, smoothK, 3, smoothD, "sma")
dSMA := ta.SMA(d, 3)
// 判断支撑压力
isUpp := dSMA.Cross(20) == -1 // crossunder
isDww := dSMA.Cross(80.5) == 1 // crossover
isYali := isDww // && h > bbUpper*1.08
isZhicheng := isUpp // && c < bbLower*1.08
// 计算止损
slLong := ema169 * (1 - slRate)
slShort := ema169 * (1 + slRate)
for _, od := range s.LongOrders {
od.SetStopLoss(&ormo.ExitTrigger{
Price: slLong,
})
}
for _, od := range s.ShortOrders {
od.SetStopLoss(&ormo.ExitTrigger{
Price: slShort,
})
}
_ = isYali
if isLong && isZhicheng && devRangePct < 5 {
s.OpenOrder(&strat.EnterReq{
Tag: "longZC1",
StopLoss: slLong,
TakeProfit: c * (1 + tpRate),
})
}
},
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
m, _ := s.More.(*MoreV2)
m.bigEma199 = ta.EMA(e.Close, 199).Get(0)
},
}
}