diff --git a/trader/aster_trader.go b/trader/aster_trader.go index f46fb5f6..1d9a547b 100644 --- a/trader/aster_trader.go +++ b/trader/aster_trader.go @@ -1417,8 +1417,52 @@ func (t *AsterTrader) GetTrades(startTime time.Time, limit int) ([]TradeRecord, // GetOpenOrders gets all open/pending orders for a symbol func (t *AsterTrader) GetOpenOrders(symbol string) ([]OpenOrder, error) { - // TODO: Implement Aster open orders - return []OpenOrder{}, nil + params := map[string]interface{}{ + "symbol": symbol, + } + + body, err := t.request("GET", "/fapi/v3/openOrders", params) + if err != nil { + return nil, fmt.Errorf("failed to get open orders: %w", err) + } + + var orders []struct { + OrderID int64 `json:"orderId"` + Symbol string `json:"symbol"` + Side string `json:"side"` + PositionSide string `json:"positionSide"` + Type string `json:"type"` + Price string `json:"price"` + StopPrice string `json:"stopPrice"` + OrigQty string `json:"origQty"` + Status string `json:"status"` + } + + if err := json.Unmarshal(body, &orders); err != nil { + return nil, fmt.Errorf("failed to parse open orders: %w", err) + } + + var result []OpenOrder + for _, order := range orders { + price, _ := strconv.ParseFloat(order.Price, 64) + stopPrice, _ := strconv.ParseFloat(order.StopPrice, 64) + quantity, _ := strconv.ParseFloat(order.OrigQty, 64) + + result = append(result, OpenOrder{ + OrderID: fmt.Sprintf("%d", order.OrderID), + Symbol: order.Symbol, + Side: order.Side, + PositionSide: order.PositionSide, + Type: order.Type, + Price: price, + StopPrice: stopPrice, + Quantity: quantity, + Status: order.Status, + }) + } + + logger.Infof("✓ ASTER GetOpenOrders: found %d open orders for %s", len(result), symbol) + return result, nil } // PlaceLimitOrder places a limit order for grid trading diff --git a/trader/bitget_trader.go b/trader/bitget_trader.go index d758c1d8..bce15a00 100644 --- a/trader/bitget_trader.go +++ b/trader/bitget_trader.go @@ -1099,8 +1099,105 @@ func genBitgetClientOid() string { // GetOpenOrders gets all open/pending orders for a symbol func (t *BitgetTrader) GetOpenOrders(symbol string) ([]OpenOrder, error) { - // TODO: Implement Bitget open orders - return []OpenOrder{}, nil + symbol = t.convertSymbol(symbol) + var result []OpenOrder + + // 1. Get pending limit orders + params := map[string]interface{}{ + "symbol": symbol, + "productType": "USDT-FUTURES", + } + + data, err := t.doRequest("GET", bitgetPendingPath, params) + if err == nil { + var orders struct { + EntrustedList []struct { + OrderId string `json:"orderId"` + Symbol string `json:"symbol"` + Side string `json:"side"` // buy/sell + TradeSide string `json:"tradeSide"` // open/close + PosSide string `json:"posSide"` // long/short + OrderType string `json:"orderType"` // limit/market + Price string `json:"price"` + Size string `json:"size"` + State string `json:"state"` + } `json:"entrustedList"` + } + if err := json.Unmarshal(data, &orders); err == nil { + for _, order := range orders.EntrustedList { + price, _ := strconv.ParseFloat(order.Price, 64) + quantity, _ := strconv.ParseFloat(order.Size, 64) + + // Convert side to standard format + side := strings.ToUpper(order.Side) + positionSide := strings.ToUpper(order.PosSide) + + result = append(result, OpenOrder{ + OrderID: order.OrderId, + Symbol: symbol, + Side: side, + PositionSide: positionSide, + Type: strings.ToUpper(order.OrderType), + Price: price, + StopPrice: 0, + Quantity: quantity, + Status: "NEW", + }) + } + } + } + + // 2. Get pending plan orders (stop-loss/take-profit) + planParams := map[string]interface{}{ + "symbol": symbol, + "productType": "USDT-FUTURES", + } + + planData, err := t.doRequest("GET", "/api/v2/mix/order/orders-plan-pending", planParams) + if err == nil { + var planOrders struct { + EntrustedList []struct { + OrderId string `json:"orderId"` + Symbol string `json:"symbol"` + Side string `json:"side"` + PosSide string `json:"posSide"` + PlanType string `json:"planType"` // normal_plan/profit_plan/loss_plan + TriggerPrice string `json:"triggerPrice"` + Size string `json:"size"` + State string `json:"state"` + } `json:"entrustedList"` + } + if err := json.Unmarshal(planData, &planOrders); err == nil { + for _, order := range planOrders.EntrustedList { + triggerPrice, _ := strconv.ParseFloat(order.TriggerPrice, 64) + quantity, _ := strconv.ParseFloat(order.Size, 64) + + side := strings.ToUpper(order.Side) + positionSide := strings.ToUpper(order.PosSide) + + // Map Bitget plan type to order type + orderType := "STOP_MARKET" + if order.PlanType == "profit_plan" { + orderType = "TAKE_PROFIT_MARKET" + } + + result = append(result, OpenOrder{ + OrderID: order.OrderId, + Symbol: symbol, + Side: side, + PositionSide: positionSide, + Type: orderType, + Price: 0, + StopPrice: triggerPrice, + Quantity: quantity, + Status: "NEW", + }) + } + } + } + + logger.Infof("✓ BITGET GetOpenOrders: found %d open orders for %s", len(result), symbol) + return result, nil } // PlaceLimitOrder places a limit order for grid trading diff --git a/trader/okx_trader.go b/trader/okx_trader.go index 73275a87..fda737a6 100644 --- a/trader/okx_trader.go +++ b/trader/okx_trader.go @@ -1390,8 +1390,98 @@ func (t *OKXTrader) GetClosedPnL(startTime time.Time, limit int) ([]ClosedPnLRec // GetOpenOrders gets all open/pending orders for a symbol func (t *OKXTrader) GetOpenOrders(symbol string) ([]OpenOrder, error) { - // TODO: Implement OKX open orders - return []OpenOrder{}, nil + instId := t.convertSymbol(symbol) + var result []OpenOrder + + // 1. Get pending limit orders + path := fmt.Sprintf("%s?instId=%s&instType=SWAP", okxPendingOrdersPath, instId) + data, err := t.doRequest("GET", path, nil) + if err == nil { + var orders []struct { + OrdId string `json:"ordId"` + InstId string `json:"instId"` + Side string `json:"side"` // buy/sell + PosSide string `json:"posSide"` // long/short/net + OrdType string `json:"ordType"` // limit/market/post_only + Px string `json:"px"` // price + Sz string `json:"sz"` // size + State string `json:"state"` // live/partially_filled + } + if err := json.Unmarshal(data, &orders); err == nil { + for _, order := range orders { + price, _ := strconv.ParseFloat(order.Px, 64) + quantity, _ := strconv.ParseFloat(order.Sz, 64) + + // Convert OKX side to standard format + side := strings.ToUpper(order.Side) + positionSide := strings.ToUpper(order.PosSide) + if positionSide == "NET" { + positionSide = "BOTH" + } + + result = append(result, OpenOrder{ + OrderID: order.OrdId, + Symbol: symbol, + Side: side, + PositionSide: positionSide, + Type: strings.ToUpper(order.OrdType), + Price: price, + StopPrice: 0, + Quantity: quantity, + Status: "NEW", + }) + } + } + } + + // 2. Get pending algo orders (stop-loss/take-profit) + algoPath := fmt.Sprintf("%s?instId=%s&instType=SWAP", okxAlgoPendingPath, instId) + algoData, err := t.doRequest("GET", algoPath, nil) + if err == nil { + var algoOrders []struct { + AlgoId string `json:"algoId"` + InstId string `json:"instId"` + Side string `json:"side"` + PosSide string `json:"posSide"` + OrdType string `json:"ordType"` // conditional/oco/trigger + TriggerPx string `json:"triggerPx"` + Sz string `json:"sz"` + State string `json:"state"` + } + if err := json.Unmarshal(algoData, &algoOrders); err == nil { + for _, order := range algoOrders { + triggerPrice, _ := strconv.ParseFloat(order.TriggerPx, 64) + quantity, _ := strconv.ParseFloat(order.Sz, 64) + + side := strings.ToUpper(order.Side) + positionSide := strings.ToUpper(order.PosSide) + if positionSide == "NET" { + positionSide = "BOTH" + } + + // Map OKX algo order type + orderType := "STOP_MARKET" + if order.OrdType == "oco" { + orderType = "TAKE_PROFIT_MARKET" + } + + result = append(result, OpenOrder{ + OrderID: order.AlgoId, + Symbol: symbol, + Side: side, + PositionSide: positionSide, + Type: orderType, + Price: 0, + StopPrice: triggerPrice, + Quantity: quantity, + Status: "NEW", + }) + } + } + } + + logger.Infof("✓ OKX GetOpenOrders: found %d open orders for %s", len(result), symbol) + return result, nil } // PlaceLimitOrder places a limit order for grid trading