mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
feat: safe mode — auto-protect positions when AI fails 3+ times
- Track consecutive AI failures
- After 3 failures: activate safe mode (no new positions, close/hold only)
- Auto-deactivate when AI recovers
- Keep loop running in safe mode (retry each cycle)
- Log clearly: 🛡️ SAFE MODE ACTIVATED/DEACTIVATED
This commit is contained in:
@@ -124,9 +124,20 @@ func (at *AutoTrader) runCycle() error {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
at.consecutiveAIFailures++
|
||||
record.Success = false
|
||||
record.ErrorMessage = fmt.Sprintf("Failed to get AI decision: %v", err)
|
||||
|
||||
// Activate safe mode after 3 consecutive failures
|
||||
if at.consecutiveAIFailures >= 3 && !at.safeMode {
|
||||
at.safeMode = true
|
||||
at.safeModeReason = fmt.Sprintf("AI failed %d consecutive times: %v", at.consecutiveAIFailures, err)
|
||||
logger.Errorf("🛡️ [%s] SAFE MODE ACTIVATED — AI failed %d times in a row. No new positions will be opened. Existing positions are protected with current stop-loss settings.",
|
||||
at.name, at.consecutiveAIFailures)
|
||||
logger.Errorf("🛡️ [%s] Reason: %v", at.name, err)
|
||||
logger.Errorf("🛡️ [%s] Action: Will keep trying AI each cycle. Safe mode auto-deactivates when AI recovers.", at.name)
|
||||
}
|
||||
|
||||
// Print system prompt and AI chain of thought (output even with errors for debugging)
|
||||
if aiDecision != nil {
|
||||
logger.Info("\n" + strings.Repeat("=", 70) + "\n")
|
||||
@@ -145,9 +156,27 @@ func (at *AutoTrader) runCycle() error {
|
||||
}
|
||||
|
||||
at.saveDecision(record)
|
||||
|
||||
// In safe mode, don't return error — keep the loop running to retry next cycle
|
||||
if at.safeMode {
|
||||
logger.Warnf("🛡️ [%s] Safe mode: skipping this cycle, will retry in %v", at.name, at.config.ScanInterval)
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to get AI decision: %w", err)
|
||||
}
|
||||
|
||||
// AI succeeded — reset failure counter and deactivate safe mode
|
||||
if at.consecutiveAIFailures > 0 {
|
||||
logger.Infof("✅ [%s] AI recovered after %d consecutive failures", at.name, at.consecutiveAIFailures)
|
||||
}
|
||||
at.consecutiveAIFailures = 0
|
||||
if at.safeMode {
|
||||
logger.Infof("🛡️ [%s] SAFE MODE DEACTIVATED — AI is working again. Resuming normal trading.", at.name)
|
||||
at.safeMode = false
|
||||
at.safeModeReason = ""
|
||||
}
|
||||
|
||||
// // 5. Print system prompt
|
||||
// logger.Infof("\n" + strings.Repeat("=", 70))
|
||||
// logger.Infof("📋 System prompt [template: %s]", at.systemPromptTemplate)
|
||||
@@ -194,6 +223,22 @@ func (at *AutoTrader) runCycle() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Safe mode: filter out open positions, only allow close/hold
|
||||
if at.safeMode {
|
||||
filtered := make([]kernel.Decision, 0)
|
||||
for _, d := range sortedDecisions {
|
||||
if d.Action == "open_long" || d.Action == "open_short" {
|
||||
logger.Warnf("🛡️ [%s] Safe mode: BLOCKED %s %s (no new positions allowed)", at.name, d.Action, d.Symbol)
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, d)
|
||||
}
|
||||
sortedDecisions = filtered
|
||||
if len(sortedDecisions) == 0 {
|
||||
logger.Infof("🛡️ [%s] Safe mode: all decisions were open positions, nothing to execute", at.name)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute decisions and record results
|
||||
for _, d := range sortedDecisions {
|
||||
// Check if trader is stopped before each decision (allow immediate stop during execution)
|
||||
|
||||
Reference in New Issue
Block a user