mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
fix(margin): correct position sizing formula to prevent insufficient margin errors
## Problem AI was calculating position_size_usd incorrectly, treating it as margin requirement instead of notional value, causing code=-2019 errors (insufficient margin). ## Solution ### 1. Updated AI prompts with correct formula - **prompts/adaptive.txt**: Added clear position sizing calculation steps - **prompts/nof1.txt**: Added English version with example - **prompts/default.txt**: Added Chinese version with example **Correct formula:** 1. Available Margin = Available Cash × 0.95 × Allocation % (reserve 5% for fees) 2. Notional Value = Available Margin × Leverage 3. position_size_usd = Notional Value (this is the value for JSON) **Example:** $500 cash, 5x leverage → position_size_usd = $2,375 (not $500) ### 2. Added code-level validation - **trader/auto_trader.go**: Added margin checks in executeOpenLong/ShortWithRecord - Validates required margin + fees ≤ available balance before opening position - Returns clear error message if insufficient ## Impact - Prevents code=-2019 errors - AI now understands the difference between notional value and margin requirement - Double validation: AI prompt + code check ## Testing - ✅ Compiles successfully - ⚠️ Requires live trading environment testing
This commit is contained in:
@@ -626,6 +626,27 @@ func (at *AutoTrader) executeOpenLongWithRecord(decision *decision.Decision, act
|
||||
actionRecord.Quantity = quantity
|
||||
actionRecord.Price = marketData.CurrentPrice
|
||||
|
||||
// ⚠️ 保证金验证:防止保证金不足错误(code=-2019)
|
||||
requiredMargin := decision.PositionSizeUSD / float64(decision.Leverage)
|
||||
|
||||
balance, err := at.trader.GetBalance()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取账户余额失败: %w", err)
|
||||
}
|
||||
availableBalance := 0.0
|
||||
if avail, ok := balance["availableBalance"].(float64); ok {
|
||||
availableBalance = avail
|
||||
}
|
||||
|
||||
// 手续费估算(Taker费率 0.04%)
|
||||
estimatedFee := decision.PositionSizeUSD * 0.0004
|
||||
totalRequired := requiredMargin + estimatedFee
|
||||
|
||||
if totalRequired > availableBalance {
|
||||
return fmt.Errorf("❌ 保证金不足: 需要 %.2f USDT(保证金 %.2f + 手续费 %.2f),可用 %.2f USDT",
|
||||
totalRequired, requiredMargin, estimatedFee, availableBalance)
|
||||
}
|
||||
|
||||
// 设置仓位模式
|
||||
if err := at.trader.SetMarginMode(decision.Symbol, at.config.IsCrossMargin); err != nil {
|
||||
log.Printf(" ⚠️ 设置仓位模式失败: %v", err)
|
||||
@@ -685,6 +706,27 @@ func (at *AutoTrader) executeOpenShortWithRecord(decision *decision.Decision, ac
|
||||
actionRecord.Quantity = quantity
|
||||
actionRecord.Price = marketData.CurrentPrice
|
||||
|
||||
// ⚠️ 保证金验证:防止保证金不足错误(code=-2019)
|
||||
requiredMargin := decision.PositionSizeUSD / float64(decision.Leverage)
|
||||
|
||||
balance, err := at.trader.GetBalance()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取账户余额失败: %w", err)
|
||||
}
|
||||
availableBalance := 0.0
|
||||
if avail, ok := balance["availableBalance"].(float64); ok {
|
||||
availableBalance = avail
|
||||
}
|
||||
|
||||
// 手续费估算(Taker费率 0.04%)
|
||||
estimatedFee := decision.PositionSizeUSD * 0.0004
|
||||
totalRequired := requiredMargin + estimatedFee
|
||||
|
||||
if totalRequired > availableBalance {
|
||||
return fmt.Errorf("❌ 保证金不足: 需要 %.2f USDT(保证金 %.2f + 手续费 %.2f),可用 %.2f USDT",
|
||||
totalRequired, requiredMargin, estimatedFee, availableBalance)
|
||||
}
|
||||
|
||||
// 设置仓位模式
|
||||
if err := at.trader.SetMarginMode(decision.Symbol, at.config.IsCrossMargin); err != nil {
|
||||
log.Printf(" ⚠️ 设置仓位模式失败: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user