mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-15 00:36:56 +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:
@@ -330,26 +330,25 @@
|
|||||||
|
|
||||||
## 仓位计算公式
|
## 仓位计算公式
|
||||||
|
|
||||||
```
|
**重要**:position_size_usd 是**名义价值**(包含杠杆),非保证金需求。
|
||||||
仓位大小(USD) = 可用资金 × 风险预算 / 止损距离百分比
|
|
||||||
仓位数量(Coins) = 仓位大小(USD) / 当前价格
|
|
||||||
```
|
|
||||||
|
|
||||||
**示例**:
|
**计算步骤**:
|
||||||
```
|
1. **可用保证金** = Available Cash × 0.95 × Allocation %(预留5%给手续费)
|
||||||
账户净值:10,000 USDT
|
2. **名义价值** = 可用保证金 × Leverage
|
||||||
风险预算:2%(信心度 90-95)
|
3. **position_size_usd** = 名义价值(这是 JSON 中应填写的值)
|
||||||
止损距离:2%(50,000 → 49,000)
|
4. **Position Size (Coins)** = position_size_usd / Current Price
|
||||||
|
|
||||||
仓位大小 = 10,000 × 2% / 2% = 10,000 USDT
|
**示例**:Available Cash = $500, Leverage = 5x, Allocation = 100%
|
||||||
杠杆 5x → 保证金 2,000 USDT
|
- 可用保证金 = $500 × 0.95 × 100% = $475
|
||||||
```
|
- position_size_usd = $475 × 5 = **$2,375** ← JSON 中填写此值
|
||||||
|
- 实际占用保证金 = $475,剩余 $25 用于手续费
|
||||||
|
|
||||||
## 杠杆选择指南
|
## 杠杆选择指引
|
||||||
|
|
||||||
- 信心度 85-87: 3-5x 杠杆
|
基于信心度的杠杆配置:
|
||||||
- 信心度 88-92: 5-10x 杠杆
|
- 信心度 <85 → 不开仓
|
||||||
- 信心度 93-95: 10-15x 杠杆
|
- 信心度 85-90 → 杠杆 1-3x,风险预算 1.5%
|
||||||
|
- 信心度 90-95 → 杠杆 3-8x,风险预算 2%
|
||||||
- 信心度 >95: 最高 20x 杠杆(谨慎)
|
- 信心度 >95: 最高 20x 杠杆(谨慎)
|
||||||
|
|
||||||
## 风险控制原则
|
## 风险控制原则
|
||||||
|
|||||||
@@ -106,6 +106,21 @@
|
|||||||
3. 寻找新机会: 有强信号吗?多空机会?
|
3. 寻找新机会: 有强信号吗?多空机会?
|
||||||
4. 输出决策: 思维链分析 + JSON
|
4. 输出决策: 思维链分析 + JSON
|
||||||
|
|
||||||
|
# 仓位大小计算
|
||||||
|
|
||||||
|
**重要**:`position_size_usd` 是**名义价值**(包含杠杆),非保证金需求。
|
||||||
|
|
||||||
|
**计算步骤**:
|
||||||
|
1. **可用保证金** = Available Cash × 0.95 × 配置比例(预留5%手续费)
|
||||||
|
2. **名义价值** = 可用保证金 × Leverage
|
||||||
|
3. **position_size_usd** = 名义价值(JSON中填写此值)
|
||||||
|
4. **实际币数** = position_size_usd / Current Price
|
||||||
|
|
||||||
|
**示例**:可用资金 $500,杠杆 5x,配置 100%
|
||||||
|
- 可用保证金 = $500 × 0.95 = $475
|
||||||
|
- position_size_usd = $475 × 5 = **$2,375** ← JSON填此值
|
||||||
|
- 实际占用保证金 = $475,剩余 $25 用于手续费
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
记住:
|
记住:
|
||||||
|
|||||||
@@ -45,10 +45,19 @@ You have exactly FOUR possible actions per decision cycle:
|
|||||||
|
|
||||||
# POSITION SIZING FRAMEWORK
|
# POSITION SIZING FRAMEWORK
|
||||||
|
|
||||||
Calculate position size using this formula:
|
**IMPORTANT**: `position_size_usd` is the **notional value** (includes leverage), NOT margin requirement.
|
||||||
|
|
||||||
Position Size (USD) = Available Cash × Leverage × Allocation %
|
## Calculation Steps:
|
||||||
Position Size (Coins) = Position Size (USD) / Current Price
|
|
||||||
|
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)
|
||||||
|
4. **Position Size (Coins)** = position_size_usd / Current Price
|
||||||
|
|
||||||
|
**Example**: Available Cash = $500, Leverage = 5x, Allocation = 100%
|
||||||
|
- Available Margin = $500 × 0.95 × 100% = $475
|
||||||
|
- position_size_usd = $475 × 5 = **$2,375** ← Fill this value in JSON
|
||||||
|
- Actual margin used = $475, remaining $25 for fees
|
||||||
|
|
||||||
## Sizing Considerations
|
## Sizing Considerations
|
||||||
|
|
||||||
|
|||||||
@@ -626,6 +626,27 @@ func (at *AutoTrader) executeOpenLongWithRecord(decision *decision.Decision, act
|
|||||||
actionRecord.Quantity = quantity
|
actionRecord.Quantity = quantity
|
||||||
actionRecord.Price = marketData.CurrentPrice
|
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 {
|
if err := at.trader.SetMarginMode(decision.Symbol, at.config.IsCrossMargin); err != nil {
|
||||||
log.Printf(" ⚠️ 设置仓位模式失败: %v", err)
|
log.Printf(" ⚠️ 设置仓位模式失败: %v", err)
|
||||||
@@ -685,6 +706,27 @@ func (at *AutoTrader) executeOpenShortWithRecord(decision *decision.Decision, ac
|
|||||||
actionRecord.Quantity = quantity
|
actionRecord.Quantity = quantity
|
||||||
actionRecord.Price = marketData.CurrentPrice
|
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 {
|
if err := at.trader.SetMarginMode(decision.Symbol, at.config.IsCrossMargin); err != nil {
|
||||||
log.Printf(" ⚠️ 设置仓位模式失败: %v", err)
|
log.Printf(" ⚠️ 设置仓位模式失败: %v", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user