init
This commit is contained in:
33
org/digo/common.go
Normal file
33
org/digo/common.go
Normal 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
209
org/digo/v1.go
Normal 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
181
org/digo/v2.go
Normal 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
112
org/digo/v4.go
Normal 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)
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user