mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-18 01:44:38 +08:00
feat(grid): enforce daily loss limit
- Add checkDailyLossLimit() function to check if daily loss exceeds limit - Track daily PnL with auto-reset at midnight - Pause grid when DailyLossLimitPct exceeded - Add updateDailyPnL() helper for realized PnL tracking - Prevent excessive single-day losses
This commit is contained in:
@@ -156,6 +156,42 @@ func (at *AutoTrader) checkMaxDrawdown() (bool, float64) {
|
|||||||
return drawdown >= gridConfig.MaxDrawdownPct, drawdown
|
return drawdown >= gridConfig.MaxDrawdownPct, drawdown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkDailyLossLimit checks if daily loss exceeds limit
|
||||||
|
// Returns: (exceeded bool, dailyLossPct float64)
|
||||||
|
func (at *AutoTrader) checkDailyLossLimit() (bool, float64) {
|
||||||
|
gridConfig := at.config.StrategyConfig.GridConfig
|
||||||
|
if gridConfig.DailyLossLimitPct <= 0 {
|
||||||
|
return false, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
at.gridState.mu.Lock()
|
||||||
|
// Reset daily PnL if new day
|
||||||
|
now := time.Now()
|
||||||
|
if now.YearDay() != at.gridState.LastDailyReset.YearDay() ||
|
||||||
|
now.Year() != at.gridState.LastDailyReset.Year() {
|
||||||
|
at.gridState.DailyPnL = 0
|
||||||
|
at.gridState.LastDailyReset = now
|
||||||
|
}
|
||||||
|
dailyPnL := at.gridState.DailyPnL
|
||||||
|
at.gridState.mu.Unlock()
|
||||||
|
|
||||||
|
// Calculate daily loss as percentage of total investment
|
||||||
|
dailyLossPct := 0.0
|
||||||
|
if gridConfig.TotalInvestment > 0 && dailyPnL < 0 {
|
||||||
|
dailyLossPct = (-dailyPnL) / gridConfig.TotalInvestment * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
return dailyLossPct >= gridConfig.DailyLossLimitPct, dailyLossPct
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateDailyPnL updates the daily PnL tracking
|
||||||
|
func (at *AutoTrader) updateDailyPnL(realizedPnL float64) {
|
||||||
|
at.gridState.mu.Lock()
|
||||||
|
at.gridState.DailyPnL += realizedPnL
|
||||||
|
at.gridState.TotalProfit += realizedPnL
|
||||||
|
at.gridState.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// emergencyExit closes all positions and cancels all orders
|
// emergencyExit closes all positions and cancels all orders
|
||||||
func (at *AutoTrader) emergencyExit(reason string) error {
|
func (at *AutoTrader) emergencyExit(reason string) error {
|
||||||
gridConfig := at.config.StrategyConfig.GridConfig
|
gridConfig := at.config.StrategyConfig.GridConfig
|
||||||
@@ -376,6 +412,16 @@ func (at *AutoTrader) RunGridCycle() error {
|
|||||||
return at.emergencyExit(fmt.Sprintf("max drawdown exceeded: %.2f%%", drawdown))
|
return at.emergencyExit(fmt.Sprintf("max drawdown exceeded: %.2f%%", drawdown))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CRITICAL: Check daily loss limit
|
||||||
|
dailyExceeded, dailyLossPct := at.checkDailyLossLimit()
|
||||||
|
if dailyExceeded {
|
||||||
|
logger.Errorf("[Grid] Daily loss limit exceeded: %.2f%%", dailyLossPct)
|
||||||
|
at.gridState.mu.Lock()
|
||||||
|
at.gridState.IsPaused = true
|
||||||
|
at.gridState.mu.Unlock()
|
||||||
|
return fmt.Errorf("daily loss limit exceeded: %.2f%%", dailyLossPct)
|
||||||
|
}
|
||||||
|
|
||||||
// Check if grid is paused
|
// Check if grid is paused
|
||||||
at.gridState.mu.RLock()
|
at.gridState.mu.RLock()
|
||||||
isPaused := at.gridState.IsPaused
|
isPaused := at.gridState.IsPaused
|
||||||
|
|||||||
Reference in New Issue
Block a user