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

79
demo/bar_agg.go Normal file
View File

@@ -0,0 +1,79 @@
package demo
import (
"fmt"
"github.com/banbox/banbot/btime"
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banbot/utils"
"github.com/banbox/banexg"
"github.com/banbox/banexg/errs"
"github.com/banbox/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"path/filepath"
"strings"
)
type AggMore struct {
bars []*banexg.Kline
barsBig []*banexg.Kline
}
func getBar(e *ta.BarEnv) *banexg.Kline {
return &banexg.Kline{
Time: e.TimeStart,
Open: e.Open.Get(0),
High: e.High.Get(0),
Low: e.Low.Get(0),
Close: e.Close.Get(0),
Volume: e.Volume.Get(0),
}
}
func dumpKline(bars []*banexg.Kline, name string) *errs.Error {
if len(bars) == 0 {
return nil
}
outDir := config.GetDataDir()
rows := utils.KlineToStr(bars, btime.UTCLocale)
path := filepath.Join(outDir, "tmp", fmt.Sprintf("%s.csv", name))
return utils.WriteCsvFile(path, rows, false)
}
func BarAgg(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
WarmupNum: 1,
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
return []*strat.PairSub{
{"_cur_", "1h", 1},
}
},
OnStartUp: func(s *strat.StratJob) {
s.More = &AggMore{}
},
OnBar: func(s *strat.StratJob) {
b := getBar(s.Env)
m, _ := s.More.(*AggMore)
m.bars = append(m.bars, b)
},
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
b := getBar(e)
m, _ := s.More.(*AggMore)
m.barsBig = append(m.barsBig, b)
},
OnShutDown: func(s *strat.StratJob) {
m, _ := s.More.(*AggMore)
code := strings.ReplaceAll(s.Symbol.Symbol, "/", "_")
code = strings.ReplaceAll(code, ":", "_")
err := dumpKline(m.bars, fmt.Sprintf("%s_%s", code, s.TimeFrame))
if err != nil {
log.Error("dump klines fail", zap.Error(err))
}
err = dumpKline(m.barsBig, fmt.Sprintf("%s_1h", code))
if err != nil {
log.Error("dump big klines fail", zap.Error(err))
}
},
}
}

40
demo/bar_stat.go Normal file
View File

@@ -0,0 +1,40 @@
package demo
import (
"fmt"
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
"go.uber.org/zap"
"slices"
)
type Stat struct {
rates []float64
}
func BarStat(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
WarmupNum: 50,
OnStartUp: func(s *strat.StratJob) {
s.More = &Stat{}
},
OnBar: func(s *strat.StratJob) {
m, _ := s.More.(*Stat)
// rate := genCurFutProbs(s.Env)[0]
m.rates = append(m.rates, 0)
},
OnShutDown: func(s *strat.StratJob) {
m, _ := s.More.(*Stat)
slices.Sort(m.rates)
var sum float64
var outs []string
for _, v := range m.rates {
sum += v
outs = append(outs, fmt.Sprintf("%.2f", v*100))
}
log.Info("", zap.Float64("avg", sum/float64(len(m.rates))),
zap.Strings("arr", outs))
},
}
}

48
demo/dca.go Normal file
View File

@@ -0,0 +1,48 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
)
type DCAState struct {
LastInvestTime int64 // 上次定投时间
}
func DCA(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 定义可调超参数
intvDays := pol.DefInt("intvDays", 7, core.PNorm(1, 365))
intvMSecs := int64(intvDays) * 86400000
return &strat.TradeStrat{
WarmupNum: 1, // 定投策略不需要太多预热数据
OnStartUp: func(s *strat.StratJob) {
// 初始化每个品种的状态
s.More = &DCAState{
LastInvestTime: 0,
}
},
OnBar: func(s *strat.StratJob) {
state, _ := s.More.(*DCAState)
currentTime := s.Env.TimeStop
// 检查是否到了定投时间间隔
if state.LastInvestTime > 0 && currentTime-state.LastInvestTime < intvMSecs {
return // 还未到定投时间
}
// 执行定投买入
err := s.OpenOrder(&strat.EnterReq{
Tag: "buy",
Log: true,
})
if err == nil {
// 更新上次定投时间
state.LastInvestTime = currentTime
}
},
}
}

71
demo/demo.go Normal file
View File

@@ -0,0 +1,71 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
func Demo(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 定义可调超参数
fastPeriod := pol.DefInt("fastPeriod", 5, core.PNorm(3, 20))
maRate := pol.Def("maRate", 4.0, core.PNorm(1.5, 9.0))
// 计算慢速均线周期
slowPeriod := int(float64(fastPeriod) * maRate)
return &strat.TradeStrat{
WarmupNum: slowPeriod + 10,
OnBar: func(s *strat.StratJob) {
e := s.Env
// 计算技术指标
maFast := ta.SMA(e.Close, fastPeriod) // 快速移动平均线
maSlow := ta.SMA(e.Close, slowPeriod) // 慢速移动平均线
maCrossState := maFast.Cross(maSlow) // 判断均线交叉状态
// 金叉:快线上穿慢线
if maCrossState == 1 {
// 先平掉空头持仓
if len(s.ShortOrders) > 0 {
s.CloseOrders(&strat.ExitReq{
Tag: "golden_cross_close_short",
Dirt: core.OdDirtShort,
})
}
// 再开多头仓位
if len(s.LongOrders) == 0 {
s.OpenOrder(&strat.EnterReq{
Tag: "golden_cross_long",
Short: false,
})
}
}
// 死叉:快线下穿慢线
if maCrossState == -1 {
// 先平掉多头持仓
if len(s.LongOrders) > 0 {
s.CloseOrders(&strat.ExitReq{
Tag: "death_cross_close_long",
Dirt: core.OdDirtLong,
})
}
// 再开空头仓位
if len(s.ShortOrders) == 0 {
s.OpenOrder(&strat.EnterReq{
Tag: "death_cross_short",
Short: true,
})
}
}
},
}
}
func init() {
strat.StratMake["demo"] = Demo
}

View File

@@ -0,0 +1,43 @@
package freqtrade
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
/*
Strategy 004
author@: Gerald Lonlas
github@: https://github.com/freqtrade/freqtrade-strategies/blob/main/user_data/strategies/Strategy004.py
*/
func Strategy004(p *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
WarmupNum: 100,
EachMaxLong: 1,
OnBar: func(s *strat.StratJob) {
e := s.Env
adx := ta.ADX(e.High, e.Low, e.Close, 14).Get(0)
adxSlow := ta.ADX(e.High, e.Low, e.Close, 35).Get(0)
cci := ta.CCI(e.Close, 14).Get(0)
fastD, _, fastK := ta.KDJBy(e.High, e.Low, e.Close, 5, 3, 3, "sma")
fastDBig, _, fastKBig := ta.KDJBy(e.High, e.Low, e.Close, 50, 3, 3, "sma")
ema := ta.EMA(e.Close, 5).Get(0)
KDX := fastK.Cross(fastD)
adxOk := adx > 50 || adxSlow > 26
fastKDOk := fastK.Get(1) < 20 && fastD.Get(1) < 20
fastKDBigOk := fastKBig.Get(1) < 30 && fastDBig.Get(1) < 30
fastKDgt70 := fastK.Get(0) > 70 || fastD.Get(0) > 70
if adxOk && cci < -100 && fastKDOk && fastKDBigOk && KDX == 1 {
s.OpenOrder(&strat.EnterReq{
Tag: "long",
StopLossVal: ema * 0.1,
})
} else if adxSlow < 25 && fastKDgt70 && fastK.Get(1) < fastD.Get(1) && e.Close.Get(0) > ema {
s.CloseOrders(&strat.ExitReq{Tag: "exit"})
}
},
}
}

View File

@@ -0,0 +1,26 @@
package freqtrade
/*
Strategy 005
author@: Gerald Lonlas
github@: https://github.com/freqtrade/freqtrade-strategies/blob/main/user_data/strategies/Strategy005.py
*/
//func Strategy005(p *config.RunPolicyConfig) *strat.TradeStrat {
// buyVolAvg := int(p.Def("buyVolAvg", 150, core.PNorm(50, 300)))
// buyRsi := p.Def("buyRsi", 26, core.PNorm(1, 100))
// buyFastD := p.Def("buyFastD", 1, core.PNorm(1, 100))
// buyFishNo := p.Def("buyFishNo", 5, core.PNorm(1, 100))
// sellRsi := p.Def("sellRsi", 74, core.PNorm(1, 100))
// sellMiDi := p.Def("sellMiDi", 4, core.PNorm(1, 100))
// sellFiRsiN := p.Def("sellFiRsiN", 30, core.PNorm(1, 100))
// return &strat.TradeStrat{
// WarmupNum: 100,
// OnBar: func(s *strat.StratJob) {
// e := s.Env
// macdCols := ta.MACD(e.Close, 12, 26, 9).Cols
// macd, macdSig := macdCols[0], macdCols[1]
//
// },
// }
//}

11
demo/freqtrade/common.go Normal file
View File

@@ -0,0 +1,11 @@
package freqtrade
import "github.com/banbox/banbot/strat"
func init() {
strat.AddStratGroup("freqtrade", map[string]strat.FuncMakeStrat{
"Strategy001": Strategy001,
"Strategy004": Strategy004,
})
}

View File

@@ -0,0 +1,46 @@
package freqtrade
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
/*
Strategy 001
author@: Gerald Lonlas
github@: https://github.com/freqtrade/freqtrade-strategies/blob/main/user_data/strategies/Strategy001.py
*/
func Strategy001(p *config.RunPolicyConfig) *strat.TradeStrat {
lenSml := int(p.Def("lenSml", 20, core.PNorm(10, 30)))
midRate := p.Def("midRate", 2.5, core.PNorm(1.5, 5))
bigRate := p.Def("bigRate", 2, core.PNorm(1.5, 3))
lenMid := int(float64(lenSml) * midRate)
lenBig := int(float64(lenMid) * bigRate)
return &strat.TradeStrat{
WarmupNum: 200,
EachMaxLong: 1,
OnBar: func(s *strat.StratJob) {
e := s.Env
maSml := ta.EMA(e.Close, lenSml)
maMid := ta.EMA(e.Close, lenMid)
maBig := ta.EMA(e.Close, lenBig)
haO, _, _, haC := ta.HeikinAshi(e)
haOpen := haO.Get(0)
haClose := haC.Get(0)
smlXmid := maSml.Cross(maMid)
midXBig := maMid.Cross(maBig)
if smlXmid == 1 && haClose > maSml.Get(0) && haOpen < haClose {
s.OpenOrder(&strat.EnterReq{
Tag: "long",
StopLossVal: haClose * 0.1,
})
} else if midXBig == 1 && haClose < maSml.Get(0) && haOpen > haClose {
s.CloseOrders(&strat.ExitReq{Tag: "exit"})
}
},
}
}

3
demo/here.java Normal file
View File

@@ -0,0 +1,3 @@
public static void main (String[] args) {
System.out.println("Hello World2!");
}

43
demo/hft.go Normal file
View File

@@ -0,0 +1,43 @@
package demo
import (
"fmt"
"github.com/banbox/banbot/biz"
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg"
"github.com/banbox/banexg/log"
"go.uber.org/zap"
)
func hft(p *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
WsSubs: map[string]string{
core.WsSubDepth: "",
core.WsSubTrade: "",
core.WsSubKLine: "",
},
OnBar: func(s *strat.StratJob) {
e := s.Env
log.Info(fmt.Sprintf("OnBar %v: %v", e.TimeStop, e.Close.Get(0)))
},
OnWsKline: func(s *strat.StratJob, pair string, k *banexg.Kline) {
log.Info(fmt.Sprintf("OnWsKline %v: %v", k.Time, k.Close))
s.OpenOrder(&strat.EnterReq{Tag: "long"})
_, _, err := biz.GetOdMgr(s.Account).ProcessOrders(s)
if err != nil {
log.Error("process order fail", zap.Error(err))
}
},
OnWsTrades: func(s *strat.StratJob, pair string, trades []*banexg.Trade) {
last := trades[len(trades)-1]
log.Info(fmt.Sprintf("OnWsTrades %v %v, %v", last.Timestamp, last.Price, last.Amount))
},
OnWsDepth: func(s *strat.StratJob, dep *banexg.OrderBook) {
bp1, bm1 := dep.Bids.Price[0], dep.Bids.Size[0]
ap1, am1 := dep.Asks.Price[0], dep.Asks.Size[0]
log.Info(fmt.Sprintf("OnWsDepth %v %v, %v,, %v, %v", dep.TimeStamp, bp1, bm1, ap1, am1))
},
}
}

19
demo/hold.go Normal file
View File

@@ -0,0 +1,19 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
)
func Hold(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
EachMaxLong: 1,
OnBar: func(s *strat.StratJob) {
if len(s.LongOrders) == 0 {
s.OpenOrder(&strat.EnterReq{
Tag: "hold",
})
}
},
}
}

37
demo/holdSl.go Normal file
View File

@@ -0,0 +1,37 @@
package demo
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"
)
func HoldSL(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
WarmupNum: 20,
EachMaxLong: 1,
OnBar: func(s *strat.StratJob) {
e := s.Env
ma5 := ta.VWMA(e.Close, e.Volume, 5)
ma20 := ta.VWMA(e.Close, e.Volume, 10)
maCrx := ma5.Cross(ma20)
atr := ta.ATR(e.High, e.Low, e.Close, 14).Get(0)
for _, od := range s.LongOrders {
od.SetStopLoss(&ormo.ExitTrigger{
Price: e.Close.Get(0) - atr*0.5,
})
}
if maCrx == -1 && len(s.LongOrders) > 0 {
s.CloseOrders(&strat.ExitReq{Tag: "exit", Dirt: core.OdDirtLong})
} else if maCrx == 1 || len(s.LongOrders) == 0 {
s.OpenOrder(&strat.EnterReq{
Tag: "long",
StopLossVal: atr * 0.5,
})
}
},
}
}

48
demo/limits.go Normal file
View File

@@ -0,0 +1,48 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/orm/ormo"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
"go.uber.org/zap"
)
func DemoV1(pol *config.RunPolicyConfig) *strat.TradeStrat {
dirt := -1.0
limitRate := 0.001
slRate := 0.005
return &strat.TradeStrat{
WarmupNum: 30,
OnBar: func(s *strat.StratJob) {
if s.OrderNum == 0 {
price := s.Env.Close.Get(0)
limit := price * (1 - dirt*limitRate)
stopLoss := price * (1 - dirt*slRate)
err := s.OpenOrder(&strat.EnterReq{
Tag: "enter",
Limit: limit,
Short: dirt < 0,
StopLoss: stopLoss,
})
if err != nil {
log.Error("open fail", zap.Error(err))
} else {
log.Info("new order", zap.Float64("limit", limit), zap.Float64("stopLoss", stopLoss))
}
}
},
OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq {
price := s.Env.Close.Get(0)
stopLoss := price * (1 - dirt*slRate)
sl := od.GetStopLoss()
if sl != nil && (stopLoss-sl.Price)*dirt > 0 {
// 有盈利,调整跟踪止损
log.Info("set stoploss", zap.Float64("val", stopLoss))
sl.Price = stopLoss
od.DirtyInfo = true
}
return nil
},
}
}

31
demo/ma_er.go Normal file
View File

@@ -0,0 +1,31 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
func DemoER(pol *config.RunPolicyConfig) *strat.TradeStrat {
smlLen := int(pol.Def("smlLen", 9, core.PNorm(3, 10)))
bigLen := int(pol.Def("bigLen", 23, core.PNorm(10, 40)))
erUpp := pol.Def("erUpp", 0.13, core.PNorm(0.1, 0.7))
return &strat.TradeStrat{
WarmupNum: 100,
OnBar: func(s *strat.StratJob) {
e := s.Env
ma5 := ta.SMA(e.Close, smlLen)
ma20 := ta.SMA(e.Close, bigLen)
maCrx := ma5.Cross(ma20)
er := ta.ER(e.Close, 50).Get(0)
if maCrx == 1 && er < erUpp {
s.OpenOrder(&strat.EnterReq{Tag: "open"})
} else if maCrx == -1 {
s.CloseOrders(&strat.ExitReq{Tag: "exit"})
}
},
}
}

86
demo/made.go Normal file
View File

@@ -0,0 +1,86 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
func Made(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 定义可调超参数
fastMAPeriod := pol.DefInt("fast_ma_period", 5, core.PUniform(3, 10))
slowMAPeriod := pol.DefInt("slow_ma_period", 20, core.PUniform(10, 40))
atrPeriod := pol.DefInt("atr_period", 14, core.PUniform(7, 21))
slRate := pol.Def("sl_rate", 2.0, core.PUniform(1.0, 4.0))
return &strat.TradeStrat{
WarmupNum: 100,
EachMaxLong: 1,
EachMaxShort: 1,
DrawDownExit: true, // 启用回撤止盈
OnBar: func(s *strat.StratJob) {
e := s.Env
// 计算技术指标
maFast := ta.SMA(e.Close, fastMAPeriod)
maSlow := ta.SMA(e.Close, slowMAPeriod)
atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
// 计算均线交叉信号
maCrossOver := maFast.Cross(maSlow) // 1 表示上穿
maCrossUnder := maSlow.Cross(maFast) // 1 表示下穿
// 获取当前价格和ATR值
currentPrice := e.Close.Get(0)
currentATR := atr.Get(0)
// 多头入场信号:快线上穿慢线(金叉)
if maCrossOver == 1 {
if s.GetOrderNum(core.OdDirtShort) > 0 {
s.CloseOrders(&strat.ExitReq{Dirt: core.OdDirtShort, Tag: "exit_short"})
}
if s.GetOrderNum(core.OdDirtLong) == 0 {
stopLossPrice := currentPrice - currentATR*slRate
s.OpenOrder(&strat.EnterReq{
Tag: "long",
StopLoss: stopLossPrice,
StopLossTag: "atr_sl_long",
})
}
}
// 空头入场信号:快线下穿慢线(死叉)
if maCrossUnder == 1 {
if s.GetOrderNum(core.OdDirtLong) > 0 {
s.CloseOrders(&strat.ExitReq{Dirt: core.OdDirtLong, Tag: "exit_long"})
}
if s.GetOrderNum(core.OdDirtShort) == 0 {
stopLossPrice := currentPrice + currentATR*slRate
s.OpenOrder(&strat.EnterReq{
Tag: "short",
Short: true,
StopLoss: stopLossPrice,
StopLossTag: "atr_sl_short",
})
}
}
// 多头出场信号:快线下穿慢线(死叉)
if maCrossUnder == 1 && s.GetOrderNum(core.OdDirtLong) > 0 {
s.CloseOrders(&strat.ExitReq{
Dirt: core.OdDirtLong,
Tag: "exit_long",
})
}
// 空头出场信号:快线上穿慢线(金叉)
if maCrossOver == 1 && s.GetOrderNum(core.OdDirtShort) > 0 {
s.CloseOrders(&strat.ExitReq{
Dirt: core.OdDirtShort,
Tag: "exit_short",
})
}
},
}
}

26
demo/main.go Normal file
View File

@@ -0,0 +1,26 @@
package demo
import (
"github.com/banbox/banbot/strat"
)
func init() {
strat.AddStratGroup("demo", map[string]strat.FuncMakeStrat{
"dca": DCA,
"tmp": tmp,
"01": DemoV1,
"agg": BarAgg,
"stat": BarStat,
"demo": Demo,
"demo_er": DemoER,
"riskRw": fixRiskReward,
"hold": Hold,
"holdSL": HoldSL,
"show": Show,
"tmp1": Tmp1,
"made": Made,
"test": Test,
"hft": hft,
"TrailStop": TrailStop,
})
}

97
demo/pairs_test.go Normal file
View File

@@ -0,0 +1,97 @@
package demo
import (
"github.com/banbox/banbot/biz"
"github.com/banbox/banbot/btime"
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/exg"
"github.com/banbox/banbot/goods"
"github.com/banbox/banbot/orm"
"github.com/banbox/banexg/log"
"go.uber.org/zap"
"math/rand"
"slices"
"sort"
"testing"
"time"
)
func genVolPairs(timeMS int64) []string {
err := biz.SetupComs(&config.CmdArgs{})
if err != nil {
panic(err)
}
_, err = orm.LoadMarkets(exg.Default, false)
if err != nil {
panic(err)
}
ft := goods.VolumePairFilter{
BaseFilter: goods.BaseFilter{Name: "VolumePairFilter"},
BackPeriod: "3d",
}
core.SetRunMode(core.RunModeBackTest)
core.SetRunEnv(core.RunEnvProd)
btime.CurTimeMS = timeMS
config.PairMgr.Cron = ""
pairs, err := ft.GenSymbols(btime.UTCStamp())
if err != nil {
panic(err)
}
res := make([]string, 0, len(pairs))
for _, pair := range pairs {
_, quote, _, _ := core.SplitSymbol(pair)
if _, ok := config.StakeCurrencyMap[quote]; ok {
res = append(res, pair)
}
}
return res
}
func TestRandPairs(t *testing.T) {
timeMS, _ := btime.ParseTimeMS("2021-11-01 00:00")
pairs := genVolPairs(timeMS)
total := len(pairs)
half := pairs[:total/3]
for i := range half {
j := rand.Intn(i + 1)
if i != j {
half[i], half[j] = half[j], half[i]
}
}
cutNum := 50
if len(half) > cutNum {
half = half[:cutNum]
}
log.Info("pairs", zap.Int("total", total), zap.Strings("arr", half))
}
func TestPairsLocs(t *testing.T) {
pairs := genVolPairs(time.Now().UnixMilli())
//pairs48 := []string{"1000FLOKI/USDT:USDT", "1000LUNC/USDT:USDT", "1000PEPE/USDT:USDT", "1000SHIB/USDT:USDT", "1000XEC/USDT:USDT", "ADA/USDT:USDT", "APT/USDT:USDT", "ARB/USDT:USDT", "AVAX/USDT:USDT", "BADGER/USDT:USDT", "BCH/USDT:USDT", "BNB/USDT:USDT", "BOME/USDT:USDT", "BTC/USDT:USDT", "CFX/USDT:USDT", "DOGE/USDT:USDT", "DOT/USDT:USDT", "ETC/USDT:USDT", "ETH/USDT:USDT", "ETHFI/USDT:USDT", "FET/USDT:USDT", "FIL/USDT:USDT", "FTM/USDT:USDT", "GALA/USDT:USDT", "HIFI/USDT:USDT", "ICP/USDT:USDT", "ID/USDT:USDT", "JUP/USDT:USDT", "LINK/USDT:USDT", "LTC/USDT:USDT", "LUNA2/USDT:USDT", "MATIC/USDT:USDT", "NEAR/USDT:USDT", "ONDO/USDT:USDT", "OP/USDT:USDT", "ORDI/USDT:USDT", "PENDLE/USDT:USDT", "PIXEL/USDT:USDT", "POLYX/USDT:USDT", "RSR/USDT:USDT", "SAND/USDT:USDT", "SOL/USDT:USDT", "STX/USDT:USDT", "SUI/USDT:USDT", "TOKEN/USDT:USDT", "TON/USDT:USDT", "WIF/USDT:USDT", "WLD/USDT:USDT", "XRP/USDT:USDT", "YGG/USDT:USDT"}
pairs170 := []string{"1000BONK/USDT:USDT", "1000FLOKI/USDT:USDT", "1000LUNC/USDT:USDT", "1000PEPE/USDT:USDT", "1000RATS/USDT:USDT", "1000XEC/USDT:USDT", "1INCH/USDT:USDT", "AAVE/USDT:USDT", "ACH/USDT:USDT", "ADA/USDT:USDT", "AGIX/USDT:USDT", "ALGO/USDT:USDT", "ANKR/USDT:USDT", "APE/USDT:USDT", "API3/USDT:USDT", "APT/USDT:USDT", "AR/USDT:USDT", "ARB/USDT:USDT", "ARPA/USDT:USDT", "ASTR/USDT:USDT", "ATOM/USDT:USDT", "AUCTION/USDT:USDT", "AVAX/USDT:USDT", "AXL/USDT:USDT", "AXS/USDT:USDT", "BADGER/USDT:USDT", "BAKE/USDT:USDT", "BCH/USDT:USDT", "BEAMX/USDT:USDT", "BEL/USDT:USDT", "BICO/USDT:USDT", "BLUR/USDT:USDT", "BNB/USDT:USDT", "BNX/USDT:USDT", "BOND/USDT:USDT", "BSV/USDT:USDT", "BTC/USDT:USDT", "CAKE/USDT:USDT", "CELO/USDT:USDT", "CFX/USDT:USDT", "CHR/USDT:USDT", "CHZ/USDT:USDT", "COMP/USDT:USDT", "CYBER/USDT:USDT", "DOGE/USDT:USDT", "DOT/USDT:USDT", "DUSK/USDT:USDT", "DYM/USDT:USDT", "EGLD/USDT:USDT", "ENA/USDT:USDT", "ENJ/USDT:USDT", "EOS/USDT:USDT", "ETC/USDT:USDT", "ETH/USDT:USDT", "ETHW/USDT:USDT", "FET/USDT:USDT", "FIL/USDT:USDT", "FLM/USDT:USDT", "FLOW/USDT:USDT", "FRONT/USDT:USDT", "FTM/USDT:USDT", "GAL/USDT:USDT", "GALA/USDT:USDT", "GAS/USDT:USDT", "GLM/USDT:USDT", "GMT/USDT:USDT", "GMX/USDT:USDT", "GRT/USDT:USDT", "HBAR/USDT:USDT", "HIFI/USDT:USDT", "HOOK/USDT:USDT", "HOT/USDT:USDT", "ICP/USDT:USDT", "ID/USDT:USDT", "ILV/USDT:USDT", "IMX/USDT:USDT", "INJ/USDT:USDT", "IOTA/USDT:USDT", "IOTX/USDT:USDT", "JASMY/USDT:USDT", "JOE/USDT:USDT", "JTO/USDT:USDT", "JUP/USDT:USDT", "KAS/USDT:USDT", "KAVA/USDT:USDT", "KEY/USDT:USDT", "KNC/USDT:USDT", "KSM/USDT:USDT", "LDO/USDT:USDT", "LEVER/USDT:USDT", "LINA/USDT:USDT", "LINK/USDT:USDT", "LPT/USDT:USDT", "LQTY/USDT:USDT", "LRC/USDT:USDT", "LTC/USDT:USDT", "LUNA2/USDT:USDT", "MAGIC/USDT:USDT", "MANA/USDT:USDT", "MANTA/USDT:USDT", "MASK/USDT:USDT", "MATIC/USDT:USDT", "MAV/USDT:USDT", "MEME/USDT:USDT", "METIS/USDT:USDT", "MINA/USDT:USDT", "MKR/USDT:USDT", "MTL/USDT:USDT", "MYRO/USDT:USDT", "NEO/USDT:USDT", "NKN/USDT:USDT", "OCEAN/USDT:USDT", "OM/USDT:USDT", "ONDO/USDT:USDT", "ONE/USDT:USDT", "OP/USDT:USDT", "ORDI/USDT:USDT", "PENDLE/USDT:USDT", "PEOPLE/USDT:USDT", "POLYX/USDT:USDT", "PYTH/USDT:USDT", "QNT/USDT:USDT", "QTUM/USDT:USDT", "RDNT/USDT:USDT", "REN/USDT:USDT", "RIF/USDT:USDT", "RLC/USDT:USDT", "RSR/USDT:USDT", "RUNE/USDT:USDT", "RVN/USDT:USDT", "SEI/USDT:USDT", "SKL/USDT:USDT", "SNX/USDT:USDT", "SPELL/USDT:USDT", "SSV/USDT:USDT", "STG/USDT:USDT", "STMX/USDT:USDT", "STORJ/USDT:USDT", "STX/USDT:USDT", "SUI/USDT:USDT", "SXP/USDT:USDT", "T/USDT:USDT", "THETA/USDT:USDT", "TIA/USDT:USDT", "TOKEN/USDT:USDT", "TON/USDT:USDT", "TRU/USDT:USDT", "TRX/USDT:USDT", "UMA/USDT:USDT", "VET/USDT:USDT", "WAVES/USDT:USDT", "WAXP/USDT:USDT", "WIF/USDT:USDT", "WOO/USDT:USDT", "XAI/USDT:USDT", "XLM/USDT:USDT", "XRP/USDT:USDT", "XTZ/USDT:USDT", "XVG/USDT:USDT", "YFI/USDT:USDT", "YGG/USDT:USDT", "ZEC/USDT:USDT", "ZEN/USDT:USDT", "ZETA/USDT:USDT", "ZIL/USDT:USDT", "ZRX/USDT:USDT"}
fails := make([]string, 0, 10)
idArr := make([]int, 0, 100)
for _, p := range pairs170 {
idx := slices.Index(pairs, p)
if idx < 0 {
fails = append(fails, p)
} else {
idArr = append(idArr, idx)
}
}
log.Warn("pairs", zap.Int("total", len(pairs)), zap.Strings("unknown", fails))
sort.Slice(idArr, func(i, j int) bool {
return idArr[i] < idArr[j]
})
step := 10
end, num := step, 0
for _, v := range idArr {
if v >= end {
log.Info("range", zap.Int("to", end), zap.Int("num", num))
num = 0
end += step
}
num += 1
}
}

63
demo/risk_reward.go Normal file
View File

@@ -0,0 +1,63 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/orm/ormo"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
func fixRiskReward(p *config.RunPolicyConfig) *strat.TradeStrat {
rewardRate := 4.0
return &strat.TradeStrat{
EachMaxLong: 1,
WarmupNum: 30,
OnBar: func(s *strat.StratJob) {
e := s.Env
atr := ta.ATR(e.High, e.Low, e.Close, 14).Get(0)
rsi := ta.RSI50(e.Close, 14).Get(0)
ma := ta.SMA(e.Close, 5).Get(0)
maMid := ta.SMA(e.Close, 10).Get(0)
maBig := ta.SMA(e.Close, 40).Get(0)
c := e.Close.Get(0)
if s.OrderNum == 0 && maMid > maBig && rsi > 15 && c < ma {
s.OpenOrder(&strat.EnterReq{
Tag: "long",
StopLossVal: atr,
StopLossTag: "init",
})
}
},
OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq {
sl := od.GetStopLoss()
if sl.Tag == "take" {
// 已设置止盈
return nil
}
if sl.Tag == "init" {
// 检查是否回本,如果是,设置止损到成本线
if od.ProfitRate > 0 {
// 保存初始止损空间
od.SetInfo("initSL", sl.Range)
feeRate := od.Enter.FeeQuote * 2 / od.EnterCost()
slPrice := od.Enter.Average * (1 + feeRate)
od.SetStopLoss(&ormo.ExitTrigger{
Price: slPrice,
Tag: "break_even",
})
}
} else {
//已设置成本止损,检查是否触发止盈
slRange := od.GetInfoFloat64("initSL")
if od.Profit > slRange*rewardRate {
// 触发止盈线,设置止盈线止损
od.SetStopLoss(&ormo.ExitTrigger{
Price: od.Enter.Average + slRange*rewardRate,
Tag: "take",
})
}
}
return nil
},
}
}

25
demo/sample.go Normal file
View File

@@ -0,0 +1,25 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
func Sample(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
WarmupNum: 100,
OnBar: func(s *strat.StratJob) {
e := s.Env
ma5 := ta.SMA(e.Close, 5)
ma20 := ta.SMA(e.Close, 20)
maCrx := ma5.Cross(ma20)
if maCrx == 1 {
s.OpenOrder(&strat.EnterReq{Tag: "open"})
} else if maCrx == -1 {
s.CloseOrders(&strat.ExitReq{Tag: "exit"})
}
},
}
}

45
demo/show.go Normal file
View File

@@ -0,0 +1,45 @@
package demo
import (
"github.com/banbox/banbot/btime"
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
"go.uber.org/zap"
"strings"
"sync"
)
func Show(pol *config.RunPolicyConfig) *strat.TradeStrat {
pairMap := make(map[string]int)
var copied []string
var lastMS int64
var lockPair sync.Mutex
return &strat.TradeStrat{
EachMaxLong: 1,
OnStartUp: func(s *strat.StratJob) {
lockPair.Lock()
base, _, _, _ := core.SplitSymbol(s.Symbol.Symbol)
pairMap[base] = 0
lockPair.Unlock()
},
OnBar: func(s *strat.StratJob) {
curMS := s.Env.TimeStop
if curMS > lastMS {
if len(copied) > 0 {
date := btime.ToDateStr(lastMS, core.DefaultDateFmt)
lockPair.Lock()
totalNum := len(pairMap)
lockPair.Unlock()
log.Info("OnBar", zap.String("date", date), zap.Int("got", len(copied)),
zap.Int("total", totalNum), zap.String("copied", strings.Join(copied, ",")))
}
lastMS = curMS
copied = nil
}
base, _, _, _ := core.SplitSymbol(s.Symbol.Symbol)
copied = append(copied, base)
},
}
}

45
demo/test.go Normal file
View File

@@ -0,0 +1,45 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
func Test(pol *config.RunPolicyConfig) *strat.TradeStrat {
//- 超参(极简)
highestDay := pol.DefInt("highest day", 20, core.PNorm(10, 40))
atrN := pol.DefInt("atrn", 14, core.PNorm(10, 30))
slNATR := pol.Def("sl natr", 2.0, core.PNorm(0.8, 4.0))
tpNATR := pol.Def("tp natr", 2.5, core.PNorm(1.0, 5.0))
//预热:技术指标所需长度的最大值 + 余量
warm := max(highestDay+1, atrN+1, 20) + 2
return &strat.TradeStrat{
WarmupNum: warm,
RunTimeFrames: []string{"15m"},
HedgeOff: true,
OnBar: func(s *strat.StratJob) {
e := s.Env
if e.Close.Len() <= warm {
return
}
last := e.Close.Get(0)
hhPrev := ta.Highest(e.High, highestDay).Get(1)
atr := ta.ATR(e.High, e.Low, e.Close, atrN).Get(0)
//仅在没有任何多单(含未成交挂单)时考虑新挂单倮京沋塑
if len(s.LongOrders) == 0 {
//只在价格位于前高下方时挂“突破买入"的 stop 触发单
if last < hhPrev && atr > 0 {
entry := hhPrev
_ = s.OpenOrder(&strat.EnterReq{
Tag: "Long",
Stop: entry,
StopLossVal: slNATR * atr,
TakeProfitVal: tpNATR * atr,
})
}
}
},
}
}

35
demo/tmp.go Normal file
View File

@@ -0,0 +1,35 @@
package demo
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"
"go.uber.org/zap"
)
func tmp(p *config.RunPolicyConfig) *strat.TradeStrat {
price := 2.236
return &strat.TradeStrat{
OnBar: func(s *strat.StratJob) {
s.OpenOrder(&strat.EnterReq{
Tag: "BUY",
OrderType: core.OrderTypeLimit,
Limit: price,
Amount: 4.65000,
TakeProfit: price * 1.01,
TakeProfitLimit: price * 1.012,
StopLoss: price * 0.99,
Log: true,
})
s.CloseOrders(&strat.ExitReq{
Tag: "all",
})
ods, err := strat.GetHistOrders(ormo.GetHistOrdersArgs{
Limit: 3,
})
log.Info("get hist", zap.Int("num", len(ods)), zap.Error(err))
},
}
}

29
demo/tmp1.go Normal file
View File

@@ -0,0 +1,29 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
"math"
)
func Tmp1(pol *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
WarmupNum: 100,
OnBar: func(s *strat.StratJob) {
e := s.Env
flag := math.Round(math.Mod(float64(e.TimeStop/e.TFMSecs), 2))
if flag == 1 {
s.OpenOrder(&strat.EnterReq{Tag: "long"})
if len(s.ShortOrders) > 0 {
s.CloseOrders(&strat.ExitReq{Tag: "exitS", Dirt: core.OdDirtShort})
}
} else {
s.OpenOrder(&strat.EnterReq{Tag: "short", Short: true})
if len(s.LongOrders) > 0 {
s.CloseOrders(&strat.ExitReq{Tag: "exitL", Dirt: core.OdDirtLong})
}
}
},
}
}

27
demo/trailing_stop.go Normal file
View File

@@ -0,0 +1,27 @@
package demo
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
)
func TrailStop(p *config.RunPolicyConfig) *strat.TradeStrat {
return &strat.TradeStrat{
WarmupNum: 100,
TimeFrames: "1h",
OnBar: func(s *strat.StratJob) {
e := s.Env
atr := ta.ATR(e.High, e.Low, e.Close, 14).Get(0)
atrPct := atr * 100 / e.Close.Get(0)
k, d, _ := ta.KDJ(e.High, e.Low, e.Close, 9, 3, 3)
kdCross := k.Cross(d)
if len(s.LongOrders) == 0 && kdCross == 1 {
s.OpenOrder(&strat.EnterReq{
Tag: "long",
CallbackPct: min(10, 5*atrPct),
})
}
},
}
}