mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-17 17:34:39 +08:00
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:
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user