Skip to content

Commit e18ef41

Browse files
the-dev-zclaude
andcommitted
fix: add comprehensive error handling for type assertions
系統性修復 38 處類型斷言的錯誤處理: **關鍵修復(trader/auto_trader.go)**: - ✅ markPrice 類型斷言:解析失敗返回錯誤(防止使用 0 值計算) - ✅ side/positionAmt 斷言:在 partial_close、adjust_stop_loss、adjust_take_profit 中添加錯誤處理 - ✅ 持倉查找循環:解析失敗時 continue 跳過 **Aster 交易所修復(trader/aster_trader.go)**: - ✅ 價格解析:entryPrice、markPrice 解析失敗時跳過持倉(記錄警告) - ✅ 訂單處理:orderType、orderID、positionSide 解析失敗時 continue - ✅ Filter 解析:filterType 解析失敗時 continue **日誌修復(logger/decision_logger.go)**: - ✅ posSymbol 斷言:添加 ok 檢查(容忍失敗) **測試結果**: - ✅ 編譯通過 - ✅ go fmt 格式化完成 - ⚡ 修復了 PR NoFxAiOS#713 xqliu 指出的問題 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 4731ee5 commit e18ef41

File tree

3 files changed

+150
-33
lines changed

3 files changed

+150
-33
lines changed

logger/decision_logger.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ func (l *DecisionLogger) AnalyzePerformance(lookbackCycles int) (*PerformanceAna
360360
// partial_close 需要根據持倉判斷方向
361361
if action.Action == "partial_close" && side == "" {
362362
for key, pos := range openPositions {
363-
if posSymbol, _ := pos["side"].(string); key == symbol+"_"+posSymbol {
363+
if posSymbol, ok := pos["side"].(string); ok && key == symbol+"_"+posSymbol {
364364
side = posSymbol
365365
break
366366
}
@@ -407,7 +407,7 @@ func (l *DecisionLogger) AnalyzePerformance(lookbackCycles int) (*PerformanceAna
407407
if action.Action == "partial_close" {
408408
// 從 openPositions 中查找持倉方向
409409
for key, pos := range openPositions {
410-
if posSymbol, _ := pos["side"].(string); key == symbol+"_"+posSymbol {
410+
if posSymbol, ok := pos["side"].(string); ok && key == symbol+"_"+posSymbol {
411411
side = posSymbol
412412
break
413413
}

trader/aster_trader.go

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,10 @@ func (t *AsterTrader) getPrecision(symbol string) (SymbolPrecision, error) {
120120

121121
// 解析filters获取tickSize和stepSize
122122
for _, filter := range s.Filters {
123-
filterType, _ := filter["filterType"].(string)
123+
filterType, ok := filter["filterType"].(string)
124+
if !ok {
125+
continue
126+
}
124127
switch filterType {
125128
case "PRICE_FILTER":
126129
if tickSizeStr, ok := filter["tickSize"].(string); ok {
@@ -538,11 +541,46 @@ func (t *AsterTrader) GetPositions() ([]map[string]interface{}, error) {
538541
continue // 跳过空仓位
539542
}
540543

541-
entryPrice, _ := strconv.ParseFloat(pos["entryPrice"].(string), 64)
542-
markPrice, _ := strconv.ParseFloat(pos["markPrice"].(string), 64)
543-
unRealizedProfit, _ := strconv.ParseFloat(pos["unRealizedProfit"].(string), 64)
544-
leverageVal, _ := strconv.ParseFloat(pos["leverage"].(string), 64)
545-
liquidationPrice, _ := strconv.ParseFloat(pos["liquidationPrice"].(string), 64)
544+
entryPriceStr, ok := pos["entryPrice"].(string)
545+
if !ok {
546+
log.Printf(" ⚠️ [%s] 无法解析 entryPrice,跳过此持仓", "Aster")
547+
continue
548+
}
549+
entryPrice, err := strconv.ParseFloat(entryPriceStr, 64)
550+
if err != nil {
551+
log.Printf(" ⚠️ [%s] entryPrice 格式错误: %v,跳过此持仓", "Aster", err)
552+
continue
553+
}
554+
555+
markPriceStr, ok := pos["markPrice"].(string)
556+
if !ok {
557+
log.Printf(" ⚠️ [%s] 无法解析 markPrice,跳过此持仓", "Aster")
558+
continue
559+
}
560+
markPrice, err := strconv.ParseFloat(markPriceStr, 64)
561+
if err != nil {
562+
log.Printf(" ⚠️ [%s] markPrice 格式错误: %v,跳过此持仓", "Aster", err)
563+
continue
564+
}
565+
566+
// 以下字段允许失败,使用默认值 0
567+
unRealizedProfitStr, ok := pos["unRealizedProfit"].(string)
568+
unRealizedProfit := 0.0
569+
if ok {
570+
unRealizedProfit, _ = strconv.ParseFloat(unRealizedProfitStr, 64)
571+
}
572+
573+
leverageStr, ok := pos["leverage"].(string)
574+
leverageVal := 0.0
575+
if ok {
576+
leverageVal, _ = strconv.ParseFloat(leverageStr, 64)
577+
}
578+
579+
liquidationPriceStr, ok := pos["liquidationPrice"].(string)
580+
liquidationPrice := 0.0
581+
if ok {
582+
liquidationPrice, _ = strconv.ParseFloat(liquidationPriceStr, 64)
583+
}
546584

547585
// 判断方向(与Binance一致)
548586
side := "long"
@@ -1063,15 +1101,21 @@ func (t *AsterTrader) CancelStopOrders(symbol string) error {
10631101
// 过滤出止盈止损单并取消
10641102
canceledCount := 0
10651103
for _, order := range orders {
1066-
orderType, _ := order["type"].(string)
1104+
orderType, ok := order["type"].(string)
1105+
if !ok {
1106+
continue
1107+
}
10671108

10681109
// 只取消止损和止盈订单
10691110
if orderType == "STOP_MARKET" ||
10701111
orderType == "TAKE_PROFIT_MARKET" ||
10711112
orderType == "STOP" ||
10721113
orderType == "TAKE_PROFIT" {
10731114

1074-
orderID, _ := order["orderId"].(float64)
1115+
orderID, ok := order["orderId"].(float64)
1116+
if !ok {
1117+
continue
1118+
}
10751119
cancelParams := map[string]interface{}{
10761120
"symbol": symbol,
10771121
"orderId": int64(orderID),
@@ -1119,12 +1163,21 @@ func (t *AsterTrader) CancelStopLossOrders(symbol string) error {
11191163
canceledCount := 0
11201164
var cancelErrors []error
11211165
for _, order := range orders {
1122-
orderType, _ := order["type"].(string)
1166+
orderType, ok := order["type"].(string)
1167+
if !ok {
1168+
continue
1169+
}
11231170

11241171
// 只取消止损订单(不取消止盈订单)
11251172
if orderType == "STOP_MARKET" || orderType == "STOP" {
1126-
orderID, _ := order["orderId"].(float64)
1127-
positionSide, _ := order["positionSide"].(string)
1173+
orderID, ok := order["orderId"].(float64)
1174+
if !ok {
1175+
continue
1176+
}
1177+
positionSide, ok := order["positionSide"].(string)
1178+
if !ok {
1179+
continue
1180+
}
11281181
cancelParams := map[string]interface{}{
11291182
"symbol": symbol,
11301183
"orderId": int64(orderID),
@@ -1178,12 +1231,21 @@ func (t *AsterTrader) CancelTakeProfitOrders(symbol string) error {
11781231
canceledCount := 0
11791232
var cancelErrors []error
11801233
for _, order := range orders {
1181-
orderType, _ := order["type"].(string)
1234+
orderType, ok := order["type"].(string)
1235+
if !ok {
1236+
continue
1237+
}
11821238

11831239
// 只取消止盈订单(不取消止损订单)
11841240
if orderType == "TAKE_PROFIT_MARKET" || orderType == "TAKE_PROFIT" {
1185-
orderID, _ := order["orderId"].(float64)
1186-
positionSide, _ := order["positionSide"].(string)
1241+
orderID, ok := order["orderId"].(float64)
1242+
if !ok {
1243+
continue
1244+
}
1245+
positionSide, ok := order["positionSide"].(string)
1246+
if !ok {
1247+
continue
1248+
}
11871249
cancelParams := map[string]interface{}{
11881250
"symbol": symbol,
11891251
"orderId": int64(orderID),

trader/auto_trader.go

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,10 @@ func (at *AutoTrader) autoSyncBalanceIfNeeded() {
375375

376376
if !pnlFound {
377377
pnlFieldMissing = true
378-
posSymbol, _ := pos["symbol"].(string)
378+
posSymbol, ok := pos["symbol"].(string)
379+
if !ok {
380+
posSymbol = "未知"
381+
}
379382
log.Printf(" ⚠️ [%s] 持仓 %s 缺少未实现盈亏字段", at.name, posSymbol)
380383
}
381384
}
@@ -1202,9 +1205,18 @@ func (at *AutoTrader) queryHyperliquidStopLossOrder(symbol, positionSide string,
12021205
// checkDualSidePosition 检查是否存在双向持仓(防御性检查)
12031206
func (at *AutoTrader) checkDualSidePosition(symbol, positionSide string, positions []map[string]interface{}) {
12041207
for _, pos := range positions {
1205-
posSymbol, _ := pos["symbol"].(string)
1206-
posSide, _ := pos["side"].(string)
1207-
posAmt, _ := pos["positionAmt"].(float64)
1208+
posSymbol, ok := pos["symbol"].(string)
1209+
if !ok {
1210+
continue
1211+
}
1212+
posSide, ok := pos["side"].(string)
1213+
if !ok {
1214+
continue
1215+
}
1216+
posAmt, ok := pos["positionAmt"].(float64)
1217+
if !ok {
1218+
continue
1219+
}
12081220
if posSymbol == symbol && posAmt != 0 && strings.ToUpper(posSide) != positionSide {
12091221
oppositeSide := strings.ToUpper(posSide)
12101222
log.Printf(" 🚨 警告:检测到 %s 存在双向持仓(%s + %s),这违反了策略规则",
@@ -1274,8 +1286,14 @@ func (at *AutoTrader) executeUpdateStopLossWithRecord(decision *decision.Decisio
12741286
// 查找目标持仓
12751287
var targetPosition map[string]interface{}
12761288
for _, pos := range positions {
1277-
symbol, _ := pos["symbol"].(string)
1278-
posAmt, _ := pos["positionAmt"].(float64)
1289+
symbol, ok := pos["symbol"].(string)
1290+
if !ok {
1291+
continue
1292+
}
1293+
posAmt, ok := pos["positionAmt"].(float64)
1294+
if !ok {
1295+
continue
1296+
}
12791297
if symbol == decision.Symbol && posAmt != 0 {
12801298
targetPosition = pos
12811299
break
@@ -1287,9 +1305,16 @@ func (at *AutoTrader) executeUpdateStopLossWithRecord(decision *decision.Decisio
12871305
}
12881306

12891307
// 获取持仓方向和数量
1290-
side, _ := targetPosition["side"].(string)
1308+
side, ok := targetPosition["side"].(string)
1309+
if !ok || side == "" {
1310+
return fmt.Errorf("无法解析持仓方向")
1311+
}
12911312
positionSide := strings.ToUpper(side)
1292-
positionAmt, _ := targetPosition["positionAmt"].(float64)
1313+
1314+
positionAmt, ok := targetPosition["positionAmt"].(float64)
1315+
if !ok {
1316+
return fmt.Errorf("无法解析持仓数量")
1317+
}
12931318

12941319
// 验证新止损价格合理性
12951320
if positionSide == "LONG" && decision.NewStopLoss >= marketData.CurrentPrice {
@@ -1381,8 +1406,14 @@ func (at *AutoTrader) executeUpdateTakeProfitWithRecord(decision *decision.Decis
13811406
// 查找目标持仓
13821407
var targetPosition map[string]interface{}
13831408
for _, pos := range positions {
1384-
symbol, _ := pos["symbol"].(string)
1385-
posAmt, _ := pos["positionAmt"].(float64)
1409+
symbol, ok := pos["symbol"].(string)
1410+
if !ok {
1411+
continue
1412+
}
1413+
posAmt, ok := pos["positionAmt"].(float64)
1414+
if !ok {
1415+
continue
1416+
}
13861417
if symbol == decision.Symbol && posAmt != 0 {
13871418
targetPosition = pos
13881419
break
@@ -1394,9 +1425,16 @@ func (at *AutoTrader) executeUpdateTakeProfitWithRecord(decision *decision.Decis
13941425
}
13951426

13961427
// 获取持仓方向和数量
1397-
side, _ := targetPosition["side"].(string)
1428+
side, ok := targetPosition["side"].(string)
1429+
if !ok || side == "" {
1430+
return fmt.Errorf("无法解析持仓方向")
1431+
}
13981432
positionSide := strings.ToUpper(side)
1399-
positionAmt, _ := targetPosition["positionAmt"].(float64)
1433+
1434+
positionAmt, ok := targetPosition["positionAmt"].(float64)
1435+
if !ok {
1436+
return fmt.Errorf("无法解析持仓数量")
1437+
}
14001438

14011439
// 验证新止盈价格合理性
14021440
if positionSide == "LONG" && decision.NewTakeProfit <= marketData.CurrentPrice {
@@ -1461,8 +1499,14 @@ func (at *AutoTrader) executePartialCloseWithRecord(decision *decision.Decision,
14611499
// 查找目标持仓
14621500
var targetPosition map[string]interface{}
14631501
for _, pos := range positions {
1464-
symbol, _ := pos["symbol"].(string)
1465-
posAmt, _ := pos["positionAmt"].(float64)
1502+
symbol, ok := pos["symbol"].(string)
1503+
if !ok {
1504+
continue
1505+
}
1506+
posAmt, ok := pos["positionAmt"].(float64)
1507+
if !ok {
1508+
continue
1509+
}
14661510
if symbol == decision.Symbol && posAmt != 0 {
14671511
targetPosition = pos
14681512
break
@@ -1474,17 +1518,28 @@ func (at *AutoTrader) executePartialCloseWithRecord(decision *decision.Decision,
14741518
}
14751519

14761520
// 获取持仓方向和数量
1477-
side, _ := targetPosition["side"].(string)
1521+
side, ok := targetPosition["side"].(string)
1522+
if !ok || side == "" {
1523+
return fmt.Errorf("无法解析持仓方向")
1524+
}
14781525
positionSide := strings.ToUpper(side)
1479-
positionAmt, _ := targetPosition["positionAmt"].(float64)
1526+
1527+
positionAmt, ok := targetPosition["positionAmt"].(float64)
1528+
if !ok {
1529+
return fmt.Errorf("无法解析持仓数量")
1530+
}
14801531

14811532
// 计算平仓数量
14821533
totalQuantity := math.Abs(positionAmt)
14831534
closeQuantity := totalQuantity * (decision.ClosePercentage / 100.0)
14841535
actionRecord.Quantity = closeQuantity
14851536

14861537
// ✅ Layer 2: 最小仓位检查(防止产生小额剩余)
1487-
markPrice, _ := targetPosition["markPrice"].(float64)
1538+
markPrice, ok := targetPosition["markPrice"].(float64)
1539+
if !ok || markPrice <= 0 {
1540+
return fmt.Errorf("无法解析当前价格,无法执行最小仓位检查")
1541+
}
1542+
14881543
currentPositionValue := totalQuantity * markPrice
14891544
remainingQuantity := totalQuantity - closeQuantity
14901545
remainingValue := remainingQuantity * markPrice

0 commit comments

Comments
 (0)