Files
banstagy/llmjob/3087.go
2026-08-24 20:53:21 +08:00

155 lines
4.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 llmjob
import (
"fmt"
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
"gonum.org/v1/gonum/floats"
"math"
"sort"
)
/*
基于初始的3087进行了简化,只保留盈利较好的14-26
*/
// DCAStrategyState 存储每个品种的策略状态
type DCAStrategyState struct {
currentSO int // 当前安全订单计数器
firstPrice float64 // 初始订单价格
totalQtys float64 // 总交易数量
totalCost float64 // 累计花费
realTotQtys float64 // 总交易数量
realTotCost float64 // 累计花费
}
// calculateSafetyOrderThreshold 计算安全订单触发价格阈值
func calculateSafetyOrderThreshold(initPrice, priceDeviation, stepScale float64, currentSO int) float64 {
curSO := float64(currentSO)
if stepScale == 1.0 {
return initPrice - initPrice*priceDeviation*stepScale*curSO
}
rate := priceDeviation*math.Pow(stepScale, curSO) - priceDeviation
return initPrice - initPrice*(rate/(stepScale-1))
}
// DynamicDCA 动态DCA策略
func J3087(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 定义可调参数
priceDeviation := pol.Def("priceDeviation", 0.82, core.PNorm(0.5, 3.0))
volumeScale := pol.Def("volumeScale", 1.16, core.PNorm(1.05, 1.5))
stepScale := pol.Def("stepScale", 1.06, core.PNorm(1.05, 1.3))
takeProfit := pol.Def("takeProfit", 1.15, core.PNorm(0.5, 2.5))
baseOrder := pol.Def("baseOrder", 10.0, core.PNorm(5, 20))
startLvl := pol.DefInt("startLvl", 14.0, core.PNorm(1, 20))
endLvl := pol.DefInt("endLvl", 26.0, core.PNorm(3, 30))
maxSafeOrders := pol.DefInt("maxSafeOrders", 30, core.PNorm(5, 50))
lenAtr := 20
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
var rates []float64
// 14/15/16/17/18/19/20/21/22/23/24/25/26
return &strat.TradeStrat{
WarmupNum: 1,
HedgeOff: true, // 单向持仓
RunTimeFrames: []string{"15m"},
OnStartUp: func(s *strat.StratJob) {
// 初始化策略状态
s.More = &DCAStrategyState{}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
state, _ := s.More.(*DCAStrategyState)
// 计算OHLC4加权均价
price := (e.High.Get(0) + e.Low.Get(0) + e.Close.Get(0) + e.Open.Get(0)) / 4
closeP := e.Close.Get(0)
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
openRate := 0.004 * closeP / atrBase
if openRate < 1 {
openRate = math.Sqrt(openRate)
}
openRate = max(0.7, min(1.5, openRate))
// 计算止盈水平
hasOrders := state.currentSO > 0
var avgPrice, realAvgPrice float64
if hasOrders && state.totalQtys > 0 {
avgPrice = state.totalCost / state.totalQtys
realAvgPrice = state.realTotCost / state.realTotQtys
}
takeProfitLevel := avgPrice * (1 + takeProfit/100)
// 初始建仓逻辑
if state.currentSO == 0 && price > 0 {
// 计算买入数量
qty := baseOrder / price
// 首次买入表现不佳,禁止实际买入
state.totalQtys += qty
state.totalCost += qty * closeP
state.firstPrice = price
state.currentSO = 1
}
// 计算安全订单触发价格阈值
priceRate := priceDeviation / 100
threshold := calculateSafetyOrderThreshold(state.firstPrice, priceRate, stepScale, state.currentSO)
// 动态补仓逻辑
if hasOrders && price <= threshold && state.currentSO <= maxSafeOrders {
// 计算补仓数量
orderQty := baseOrder * openRate * math.Pow(volumeScale, float64(state.currentSO-1)) / price
// endLvl用于实际下单。maxSafeOrders用于止盈价格
if state.currentSO >= startLvl && state.currentSO <= endLvl {
s.OpenOrder(&strat.EnterReq{
Tag: fmt.Sprintf("%v", state.currentSO),
Amount: orderQty,
Log: true,
})
state.realTotQtys += orderQty
state.realTotCost += orderQty * closeP
}
state.totalQtys += orderQty
state.totalCost += orderQty * closeP
state.currentSO += 1
}
// 止盈平仓逻辑
if price >= takeProfitLevel && hasOrders {
s.CloseOrders(&strat.ExitReq{
Tag: "TakeProfit",
})
if realAvgPrice > 0 {
rates = append(rates, takeProfitLevel/realAvgPrice)
}
// 重置状态变量
state.currentSO = 0
state.totalQtys = 0.0
state.totalCost = 0
state.realTotQtys = 0
state.realTotCost = 0
state.firstPrice = 0
}
},
OnShutDown: func(s *strat.StratJob) {
if len(rates) == 0 {
return
}
sort.Float64s(rates)
step := len(rates) / 7
for i := 0; i < len(rates); i += step {
block := rates[i:min(len(rates), i+step)]
avg := floats.Sum(block) / float64(len(block))
fmt.Printf("rate: %v: %v\n", i, avg)
}
rates = nil
},
}
}