feat(grid): add automatic grid adjustment

- New checkGridSkew() detects imbalanced grid
- autoAdjustGrid() reinitializes around current price
- Prevents grid from becoming ineffective after drift
- Triggers when one side is 3x more filled than other
This commit is contained in:
tinkle-community
2026-01-14 12:49:22 +08:00
parent 73789f7fb7
commit 584bfae699

View File

@@ -858,6 +858,9 @@ func (at *AutoTrader) syncGridState() {
// CRITICAL: Check stop loss for filled levels
at.checkAndExecuteStopLoss()
// Check grid skew and auto-adjust if needed
at.autoAdjustGrid()
}
// saveGridDecisionRecord saves the grid decision to database
@@ -918,6 +921,89 @@ func (at *AutoTrader) IsGridStrategy() bool {
return at.config.StrategyConfig.StrategyType == "grid_trading" && at.config.StrategyConfig.GridConfig != nil
}
// checkGridSkew checks if grid is heavily skewed (too many fills on one side)
// Returns: (skewed bool, buyFilledCount int, sellFilledCount int)
func (at *AutoTrader) checkGridSkew() (bool, int, int) {
at.gridState.mu.RLock()
defer at.gridState.mu.RUnlock()
buyFilled := 0
sellFilled := 0
buyEmpty := 0
sellEmpty := 0
for _, level := range at.gridState.Levels {
if level.Side == "buy" {
if level.State == "filled" {
buyFilled++
} else if level.State == "empty" {
buyEmpty++
}
} else {
if level.State == "filled" {
sellFilled++
} else if level.State == "empty" {
sellEmpty++
}
}
}
// Grid is skewed if one side has 3x more fills than the other
// or if one side is completely empty
skewed := false
if buyFilled > 0 && sellFilled == 0 && sellEmpty > 5 {
skewed = true // All buys filled, no sells
} else if sellFilled > 0 && buyFilled == 0 && buyEmpty > 5 {
skewed = true // All sells filled, no buys
} else if buyFilled >= 3*sellFilled && buyFilled > 5 {
skewed = true
} else if sellFilled >= 3*buyFilled && sellFilled > 5 {
skewed = true
}
return skewed, buyFilled, sellFilled
}
// autoAdjustGrid automatically adjusts grid when heavily skewed
func (at *AutoTrader) autoAdjustGrid() {
skewed, buyFilled, sellFilled := at.checkGridSkew()
if !skewed {
return
}
logger.Warnf("[Grid] Grid heavily skewed: buy_filled=%d, sell_filled=%d. Auto-adjusting...",
buyFilled, sellFilled)
gridConfig := at.config.StrategyConfig.GridConfig
// Get current price
currentPrice, err := at.trader.GetMarketPrice(gridConfig.Symbol)
if err != nil {
logger.Errorf("[Grid] Failed to get price for auto-adjust: %v", err)
return
}
// Check if price is near grid boundary
at.gridState.mu.RLock()
upper := at.gridState.UpperPrice
lower := at.gridState.LowerPrice
at.gridState.mu.RUnlock()
// Only adjust if price has moved significantly (>30% of grid range)
gridRange := upper - lower
midPrice := (upper + lower) / 2
priceDeviation := math.Abs(currentPrice - midPrice)
if priceDeviation < gridRange*0.3 {
return // Price still near center, don't adjust
}
// Cancel existing orders and reinitialize
logger.Infof("[Grid] Adjusting grid around new price $%.2f", currentPrice)
at.cancelAllGridOrders()
at.initializeGridLevels(currentPrice, gridConfig)
}
// checkAndExecuteStopLoss checks if any filled level has exceeded stop loss and closes it
func (at *AutoTrader) checkAndExecuteStopLoss() {
gridConfig := at.config.StrategyConfig.GridConfig