fix: bitget order sync parsing for V2 API

- Support both wrapped (fillList) and direct array response formats
- Fix tradeSide parsing for one-way mode (buy_single/sell_single)
- Fix fee extraction from nested feeDetail array structure
This commit is contained in:
tinkle-community
2026-01-31 17:32:50 +08:00
parent b137122b18
commit 581ff57323

View File

@@ -48,52 +48,82 @@ func (t *BitgetTrader) GetTrades(startTime time.Time, limit int) ([]BitgetTrade,
return nil, fmt.Errorf("failed to get fill history: %w", err) return nil, fmt.Errorf("failed to get fill history: %w", err)
} }
var resp struct {
FillList []struct { // Bitget fill structure - supports both one-way and hedge mode
TradeID string `json:"tradeId"` type BitgetFill struct {
Symbol string `json:"symbol"` TradeID string `json:"tradeId"`
OrderID string `json:"orderId"` Symbol string `json:"symbol"`
Side string `json:"side"` // buy, sell OrderID string `json:"orderId"`
Price string `json:"price"` // Fill price Side string `json:"side"` // buy, sell
BaseVolume string `json:"baseVolume"` // Fill size in base currency Price string `json:"price"` // Fill price
Fee string `json:"fee"` // Fee (negative for cost) BaseVolume string `json:"baseVolume"` // Fill size in base currency
FeeCcy string `json:"feeCcy"` // Fee currency Profit string `json:"profit"` // Realized PnL
Profit string `json:"profit"` // Realized PnL CTime string `json:"cTime"` // Fill time (ms)
CTime string `json:"cTime"` // Fill time (ms) TradeSide string `json:"tradeSide"` // one-way: buy_single/sell_single, hedge: open/close
TradeSide string `json:"tradeSide"` // open, close FeeDetail []struct {
} `json:"fillList"` FeeCoin string `json:"feeCoin"`
TotalFee string `json:"totalFee"`
} `json:"feeDetail"`
} }
if err := json.Unmarshal(data, &resp); err != nil { // Try parsing as wrapped response first (fillList field)
return nil, fmt.Errorf("failed to parse fills: %w", err) var wrappedResp struct {
FillList []BitgetFill `json:"fillList"`
} }
trades := make([]BitgetTrade, 0, len(resp.FillList)) // Try direct array format (Bitget V2 API returns data as direct array)
var directFills []BitgetFill
for _, fill := range resp.FillList { // Try wrapped format first
if err := json.Unmarshal(data, &wrappedResp); err == nil && len(wrappedResp.FillList) > 0 {
logger.Infof("🔍 Bitget: parsed as wrapped format, fillList count: %d", len(wrappedResp.FillList))
directFills = wrappedResp.FillList
} else {
// Try direct array format
if err := json.Unmarshal(data, &directFills); err != nil {
logger.Infof("⚠️ Bitget fill-history parse failed, raw: %s", string(data))
return nil, fmt.Errorf("failed to parse fills: %w", err)
}
logger.Infof("🔍 Bitget: parsed as direct array, fills count: %d", len(directFills))
}
trades := make([]BitgetTrade, 0, len(directFills))
for _, fill := range directFills {
fillPrice, _ := strconv.ParseFloat(fill.Price, 64) fillPrice, _ := strconv.ParseFloat(fill.Price, 64)
fillQty, _ := strconv.ParseFloat(fill.BaseVolume, 64) fillQty, _ := strconv.ParseFloat(fill.BaseVolume, 64)
fee, _ := strconv.ParseFloat(fill.Fee, 64)
profit, _ := strconv.ParseFloat(fill.Profit, 64) profit, _ := strconv.ParseFloat(fill.Profit, 64)
cTime, _ := strconv.ParseInt(fill.CTime, 10, 64) cTime, _ := strconv.ParseInt(fill.CTime, 10, 64)
// Extract fee from feeDetail array (Bitget V2 API)
var fee float64
var feeAsset string
if len(fill.FeeDetail) > 0 {
fee, _ = strconv.ParseFloat(fill.FeeDetail[0].TotalFee, 64)
feeAsset = fill.FeeDetail[0].FeeCoin
}
// Determine order action based on side and tradeSide // Determine order action based on side and tradeSide
// Bitget one-way mode: // Bitget one-way mode: buy_single (open long), sell_single (close long)
// - buy + open = open long // Bitget hedge mode: open + buy = open_long, close + sell = close_long
// - sell + open = open short
// - sell + close = close long
// - buy + close = close short
orderAction := "open_long" orderAction := "open_long"
side := strings.ToLower(fill.Side) side := strings.ToLower(fill.Side)
tradeSide := strings.ToLower(fill.TradeSide) tradeSide := strings.ToLower(fill.TradeSide)
if tradeSide == "open" { // One-way position mode (buy_single/sell_single)
if tradeSide == "buy_single" {
orderAction = "open_long"
} else if tradeSide == "sell_single" {
orderAction = "close_long"
} else if tradeSide == "open" {
// Hedge mode: open
if side == "buy" { if side == "buy" {
orderAction = "open_long" orderAction = "open_long"
} else { } else {
orderAction = "open_short" orderAction = "open_short"
} }
} else if tradeSide == "close" { } else if tradeSide == "close" {
// Hedge mode: close
if side == "sell" { if side == "sell" {
orderAction = "close_long" orderAction = "close_long"
} else { } else {
@@ -108,8 +138,8 @@ func (t *BitgetTrader) GetTrades(startTime time.Time, limit int) ([]BitgetTrade,
Side: fill.Side, Side: fill.Side,
FillPrice: fillPrice, FillPrice: fillPrice,
FillQty: fillQty, FillQty: fillQty,
Fee: -fee, // Bitget returns negative fee Fee: -fee, // Bitget returns negative fee, convert to positive
FeeAsset: fill.FeeCcy, FeeAsset: feeAsset,
ExecTime: time.UnixMilli(cTime).UTC(), ExecTime: time.UnixMilli(cTime).UTC(),
ProfitLoss: profit, ProfitLoss: profit,
OrderType: "MARKET", OrderType: "MARKET",