155 lines
4.6 KiB
Go
155 lines
4.6 KiB
Go
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
|
||
},
|
||
}
|
||
}
|