mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
pkg/exchange: add query open orders
This commit is contained in:
parent
c01be14c70
commit
b352ae855f
|
@ -149,6 +149,49 @@ func toGlobalTrades(orderDetails []okexapi.OrderDetails) ([]types.Trade, error)
|
||||||
return trades, nil
|
return trades, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func openOrderToGlobal(order *okexapi.OpenOrder) (*types.Order, error) {
|
||||||
|
side := toGlobalSide(order.Side)
|
||||||
|
|
||||||
|
orderType, err := toGlobalOrderType(order.OrderType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
timeInForce := types.TimeInForceGTC
|
||||||
|
switch order.OrderType {
|
||||||
|
case okexapi.OrderTypeFOK:
|
||||||
|
timeInForce = types.TimeInForceFOK
|
||||||
|
case okexapi.OrderTypeIOC:
|
||||||
|
timeInForce = types.TimeInForceIOC
|
||||||
|
}
|
||||||
|
|
||||||
|
orderStatus, err := toGlobalOrderStatus(order.State)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.Order{
|
||||||
|
SubmitOrder: types.SubmitOrder{
|
||||||
|
ClientOrderID: order.ClientOrderId,
|
||||||
|
Symbol: toGlobalSymbol(order.InstrumentID),
|
||||||
|
Side: side,
|
||||||
|
Type: orderType,
|
||||||
|
Price: order.Price,
|
||||||
|
Quantity: order.Size,
|
||||||
|
TimeInForce: timeInForce,
|
||||||
|
},
|
||||||
|
Exchange: types.ExchangeOKEx,
|
||||||
|
OrderID: uint64(order.OrderId),
|
||||||
|
UUID: strconv.FormatInt(int64(order.OrderId), 10),
|
||||||
|
Status: orderStatus,
|
||||||
|
OriginalStatus: string(order.State),
|
||||||
|
ExecutedQuantity: order.AccumulatedFillSize,
|
||||||
|
IsWorking: order.State.IsWorking(),
|
||||||
|
CreationTime: types.Time(order.CreatedTime),
|
||||||
|
UpdateTime: types.Time(order.UpdatedTime),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func toGlobalOrders(orderDetails []okexapi.OrderDetails) ([]types.Order, error) {
|
func toGlobalOrders(orderDetails []okexapi.OrderDetails) ([]types.Order, error) {
|
||||||
var orders []types.Order
|
var orders []types.Order
|
||||||
var err error
|
var err error
|
||||||
|
|
84
pkg/exchange/okex/convert_test.go
Normal file
84
pkg/exchange/okex/convert_test.go
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package okex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/exchange/okex/okexapi"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_openOrderToGlobal(t *testing.T) {
|
||||||
|
var (
|
||||||
|
assert = assert.New(t)
|
||||||
|
|
||||||
|
orderId = 665576973905014786
|
||||||
|
// {"accFillSz":"0","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"","cTime":"1704957916401","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"0","feeCcy":"USDT","fillPx":"","fillSz":"0","fillTime":"","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665576973905014786","ordType":"limit","pnl":"0","posSide":"net","px":"48174.5","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"live","stpId":"","stpMode":"","sz":"0.00001","tag":"","tdMode":"cash","tgtCcy":"","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"","uTime":"1704957916401"}
|
||||||
|
openOrder = &okexapi.OpenOrder{
|
||||||
|
AccumulatedFillSize: fixedpoint.NewFromFloat(0),
|
||||||
|
AvgPrice: fixedpoint.NewFromFloat(0),
|
||||||
|
CreatedTime: types.NewMillisecondTimestampFromInt(1704957916401),
|
||||||
|
Category: "normal",
|
||||||
|
Currency: "BTC",
|
||||||
|
ClientOrderId: "",
|
||||||
|
Fee: fixedpoint.Zero,
|
||||||
|
FeeCurrency: "USDT",
|
||||||
|
FillTime: types.NewMillisecondTimestampFromInt(0),
|
||||||
|
InstrumentID: "BTC-USDT",
|
||||||
|
InstrumentType: okexapi.InstrumentTypeSpot,
|
||||||
|
OrderId: types.StrInt64(orderId),
|
||||||
|
OrderType: okexapi.OrderTypeLimit,
|
||||||
|
Price: fixedpoint.NewFromFloat(48174.5),
|
||||||
|
Side: okexapi.SideTypeBuy,
|
||||||
|
State: okexapi.OrderStateLive,
|
||||||
|
Size: fixedpoint.NewFromFloat(0.00001),
|
||||||
|
UpdatedTime: types.NewMillisecondTimestampFromInt(1704957916401),
|
||||||
|
}
|
||||||
|
expOrder = &types.Order{
|
||||||
|
SubmitOrder: types.SubmitOrder{
|
||||||
|
ClientOrderID: openOrder.ClientOrderId,
|
||||||
|
Symbol: toGlobalSymbol(openOrder.InstrumentID),
|
||||||
|
Side: types.SideTypeBuy,
|
||||||
|
Type: types.OrderTypeLimit,
|
||||||
|
Quantity: fixedpoint.NewFromFloat(0.00001),
|
||||||
|
Price: fixedpoint.NewFromFloat(48174.5),
|
||||||
|
AveragePrice: fixedpoint.Zero,
|
||||||
|
StopPrice: fixedpoint.Zero,
|
||||||
|
TimeInForce: types.TimeInForceGTC,
|
||||||
|
},
|
||||||
|
Exchange: types.ExchangeOKEx,
|
||||||
|
OrderID: uint64(orderId),
|
||||||
|
UUID: fmt.Sprintf("%d", orderId),
|
||||||
|
Status: types.OrderStatusNew,
|
||||||
|
OriginalStatus: string(okexapi.OrderStateLive),
|
||||||
|
ExecutedQuantity: fixedpoint.Zero,
|
||||||
|
IsWorking: true,
|
||||||
|
CreationTime: types.Time(types.NewMillisecondTimestampFromInt(1704957916401).Time()),
|
||||||
|
UpdateTime: types.Time(types.NewMillisecondTimestampFromInt(1704957916401).Time()),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("succeeds", func(t *testing.T) {
|
||||||
|
order, err := openOrderToGlobal(openOrder)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(expOrder, order)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unexpected order status", func(t *testing.T) {
|
||||||
|
newOrder := *openOrder
|
||||||
|
newOrder.State = "xxx"
|
||||||
|
_, err := openOrderToGlobal(&newOrder)
|
||||||
|
assert.ErrorContains(err, "xxx")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unexpected order type", func(t *testing.T) {
|
||||||
|
newOrder := *openOrder
|
||||||
|
newOrder.OrderType = "xxx"
|
||||||
|
_, err := openOrderToGlobal(&newOrder)
|
||||||
|
assert.ErrorContains(err, "xxx")
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
|
@ -30,15 +30,15 @@ var (
|
||||||
queryAccountLimiter = rate.NewLimiter(rate.Every(200*time.Millisecond), 5)
|
queryAccountLimiter = rate.NewLimiter(rate.Every(200*time.Millisecond), 5)
|
||||||
placeOrderLimiter = rate.NewLimiter(rate.Every(30*time.Millisecond), 30)
|
placeOrderLimiter = rate.NewLimiter(rate.Every(30*time.Millisecond), 30)
|
||||||
batchCancelOrderLimiter = rate.NewLimiter(rate.Every(5*time.Millisecond), 200)
|
batchCancelOrderLimiter = rate.NewLimiter(rate.Every(5*time.Millisecond), 200)
|
||||||
|
queryOpenOrderLimiter = rate.NewLimiter(rate.Every(30*time.Millisecond), 30)
|
||||||
)
|
)
|
||||||
|
|
||||||
const ID = "okex"
|
const (
|
||||||
|
ID = "okex"
|
||||||
|
|
||||||
// PlatformToken is the platform currency of OKEx, pre-allocate static string here
|
// PlatformToken is the platform currency of OKEx, pre-allocate static string here
|
||||||
const PlatformToken = "OKB"
|
PlatformToken = "OKB"
|
||||||
|
|
||||||
const (
|
|
||||||
// Constant For query limit
|
|
||||||
defaultQueryLimit = 100
|
defaultQueryLimit = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -295,15 +295,46 @@ func (e *Exchange) SubmitOrder(ctx context.Context, order types.SubmitOrder) (*t
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryOpenOrders retrieves the pending orders. The data returned is ordered by createdTime, and we utilized the
|
||||||
|
// `After` parameter to acquire all orders.
|
||||||
func (e *Exchange) QueryOpenOrders(ctx context.Context, symbol string) (orders []types.Order, err error) {
|
func (e *Exchange) QueryOpenOrders(ctx context.Context, symbol string) (orders []types.Order, err error) {
|
||||||
instrumentID := toLocalSymbol(symbol)
|
instrumentID := toLocalSymbol(symbol)
|
||||||
req := e.client.NewGetPendingOrderRequest().InstrumentType(okexapi.InstrumentTypeSpot).InstrumentID(instrumentID)
|
|
||||||
orderDetails, err := req.Do(ctx)
|
nextCursor := int64(0)
|
||||||
if err != nil {
|
for {
|
||||||
return orders, err
|
if err := queryOpenOrderLimiter.Wait(ctx); err != nil {
|
||||||
|
return nil, fmt.Errorf("query open orders rate limiter wait error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := e.client.NewGetOpenOrdersRequest().
|
||||||
|
InstrumentID(instrumentID).
|
||||||
|
After(strconv.FormatInt(nextCursor, 10))
|
||||||
|
openOrders, err := req.Do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to query open orders: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range openOrders {
|
||||||
|
o, err := openOrderToGlobal(&o)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to convert order, err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
orders = append(orders, *o)
|
||||||
|
}
|
||||||
|
|
||||||
|
orderLen := len(openOrders)
|
||||||
|
// a defensive programming to ensure the length of order response is expected.
|
||||||
|
if orderLen > defaultQueryLimit {
|
||||||
|
return nil, fmt.Errorf("unexpected open orders length %d", orderLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
if orderLen < defaultQueryLimit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
nextCursor = int64(openOrders[orderLen-1].OrderId)
|
||||||
}
|
}
|
||||||
|
|
||||||
orders, err = toGlobalOrders(orderDetails)
|
|
||||||
return orders, err
|
return orders, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,10 @@ const (
|
||||||
OrderStateFilled OrderState = "filled"
|
OrderStateFilled OrderState = "filled"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (o OrderState) IsWorking() bool {
|
||||||
|
return o == OrderStateLive || o == OrderStatePartiallyFilled
|
||||||
|
}
|
||||||
|
|
||||||
type RestClient struct {
|
type RestClient struct {
|
||||||
requestgen.BaseAPIClient
|
requestgen.BaseAPIClient
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,26 @@ func TestClient_CancelOrderRequest(t *testing.T) {
|
||||||
t.Log(cancelResp)
|
t.Log(cancelResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClient_OpenOrdersRequest(t *testing.T) {
|
||||||
|
client := getTestClientOrSkip(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
orders := []OpenOrder{}
|
||||||
|
beforeId := int64(0)
|
||||||
|
for {
|
||||||
|
c := client.NewGetOpenOrdersRequest().InstrumentID("BTC-USDT").Limit("1").After(fmt.Sprintf("%d", beforeId))
|
||||||
|
res, err := c.Do(ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if len(res) != 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
orders = append(orders, res...)
|
||||||
|
beforeId = int64(res[0].OrderId)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(orders)
|
||||||
|
}
|
||||||
|
|
||||||
func TestClient_BatchCancelOrderRequest(t *testing.T) {
|
func TestClient_BatchCancelOrderRequest(t *testing.T) {
|
||||||
client := getTestClientOrSkip(t)
|
client := getTestClientOrSkip(t)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -170,21 +190,6 @@ func TestClient_BatchCancelOrderRequest(t *testing.T) {
|
||||||
t.Log(cancelResp)
|
t.Log(cancelResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetPendingOrderRequest(t *testing.T) {
|
|
||||||
client := getTestClientOrSkip(t)
|
|
||||||
ctx := context.Background()
|
|
||||||
req := client.NewGetPendingOrderRequest()
|
|
||||||
odr_type := []string{string(OrderTypeLimit), string(OrderTypeIOC)}
|
|
||||||
|
|
||||||
pending_order, err := req.
|
|
||||||
InstrumentID("BTC-USDT").
|
|
||||||
OrderTypes(odr_type).
|
|
||||||
Do(ctx)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotEmpty(t, pending_order)
|
|
||||||
t.Logf("pending order: %+v", pending_order)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClient_GetOrderDetailsRequest(t *testing.T) {
|
func TestClient_GetOrderDetailsRequest(t *testing.T) {
|
||||||
client := getTestClientOrSkip(t)
|
client := getTestClientOrSkip(t)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
112
pkg/exchange/okex/okexapi/get_open_orders_request.go
Normal file
112
pkg/exchange/okex/okexapi/get_open_orders_request.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package okexapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/c9s/requestgen"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
|
||||||
|
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
|
||||||
|
|
||||||
|
type OpenOrder struct {
|
||||||
|
AccumulatedFillSize fixedpoint.Value `json:"accFillSz"`
|
||||||
|
// If none is filled, it will return "".
|
||||||
|
AvgPrice fixedpoint.Value `json:"avgPx"`
|
||||||
|
CreatedTime types.MillisecondTimestamp `json:"cTime"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
ClientOrderId string `json:"clOrdId"`
|
||||||
|
Fee fixedpoint.Value `json:"fee"`
|
||||||
|
FeeCurrency string `json:"feeCcy"`
|
||||||
|
// Last filled time
|
||||||
|
FillTime types.MillisecondTimestamp `json:"fillTime"`
|
||||||
|
InstrumentID string `json:"instId"`
|
||||||
|
InstrumentType InstrumentType `json:"instType"`
|
||||||
|
OrderId types.StrInt64 `json:"ordId"`
|
||||||
|
OrderType OrderType `json:"ordType"`
|
||||||
|
Price fixedpoint.Value `json:"px"`
|
||||||
|
Side SideType `json:"side"`
|
||||||
|
State OrderState `json:"state"`
|
||||||
|
Size fixedpoint.Value `json:"sz"`
|
||||||
|
TargetCurrency string `json:"tgtCcy"`
|
||||||
|
UpdatedTime types.MillisecondTimestamp `json:"uTime"`
|
||||||
|
|
||||||
|
// Margin currency
|
||||||
|
// Only applicable to cross MARGIN orders in Single-currency margin.
|
||||||
|
Currency string `json:"ccy"`
|
||||||
|
TradeId string `json:"tradeId"`
|
||||||
|
// Last filled price
|
||||||
|
FillPrice fixedpoint.Value `json:"fillPx"`
|
||||||
|
// Last filled quantity
|
||||||
|
FillSize fixedpoint.Value `json:"fillSz"`
|
||||||
|
// Leverage, from 0.01 to 125.
|
||||||
|
// Only applicable to MARGIN/FUTURES/SWAP
|
||||||
|
Lever string `json:"lever"`
|
||||||
|
// Profit and loss, Applicable to orders which have a trade and aim to close position. It always is 0 in other conditions
|
||||||
|
Pnl fixedpoint.Value `json:"pnl"`
|
||||||
|
PositionSide string `json:"posSide"`
|
||||||
|
// Options price in USDOnly applicable to options; return "" for other instrument types
|
||||||
|
PriceUsd fixedpoint.Value `json:"pxUsd"`
|
||||||
|
// Implied volatility of the options orderOnly applicable to options; return "" for other instrument types
|
||||||
|
PriceVol fixedpoint.Value `json:"pxVol"`
|
||||||
|
// Price type of options
|
||||||
|
PriceType string `json:"pxType"`
|
||||||
|
// Rebate amount, only applicable to spot and margin, the reward of placing orders from the platform (rebate)
|
||||||
|
// given to user who has reached the specified trading level. If there is no rebate, this field is "".
|
||||||
|
Rebate fixedpoint.Value `json:"rebate"`
|
||||||
|
RebateCcy string `json:"rebateCcy"`
|
||||||
|
// Client-supplied Algo ID when placing order attaching TP/SL.
|
||||||
|
AttachAlgoClOrdId string `json:"attachAlgoClOrdId"`
|
||||||
|
SlOrdPx fixedpoint.Value `json:"slOrdPx"`
|
||||||
|
SlTriggerPx fixedpoint.Value `json:"slTriggerPx"`
|
||||||
|
SlTriggerPxType string `json:"slTriggerPxType"`
|
||||||
|
AttachAlgoOrds []interface{} `json:"attachAlgoOrds"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
// Self trade prevention ID. Return "" if self trade prevention is not applicable
|
||||||
|
StpId string `json:"stpId"`
|
||||||
|
// Self trade prevention mode. Return "" if self trade prevention is not applicable
|
||||||
|
StpMode string `json:"stpMode"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
TradeMode string `json:"tdMode"`
|
||||||
|
TpOrdPx fixedpoint.Value `json:"tpOrdPx"`
|
||||||
|
TpTriggerPx fixedpoint.Value `json:"tpTriggerPx"`
|
||||||
|
TpTriggerPxType string `json:"tpTriggerPxType"`
|
||||||
|
ReduceOnly string `json:"reduceOnly"`
|
||||||
|
QuickMgnType string `json:"quickMgnType"`
|
||||||
|
AlgoClOrdId string `json:"algoClOrdId"`
|
||||||
|
AlgoId string `json:"algoId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate GetRequest -url "/api/v5/trade/orders-pending" -type GetOpenOrdersRequest -responseDataType []OpenOrder
|
||||||
|
type GetOpenOrdersRequest struct {
|
||||||
|
client requestgen.AuthenticatedAPIClient
|
||||||
|
|
||||||
|
instrumentID *string `param:"instId,query"`
|
||||||
|
|
||||||
|
instrumentType InstrumentType `param:"instType,query"`
|
||||||
|
|
||||||
|
orderType *OrderType `param:"ordType,query"`
|
||||||
|
|
||||||
|
state *OrderState `param:"state,query"`
|
||||||
|
category *string `param:"category,query"`
|
||||||
|
// Pagination of data to return records earlier than the requested ordId
|
||||||
|
after *string `param:"after,query"`
|
||||||
|
// Pagination of data to return records newer than the requested ordId
|
||||||
|
before *string `param:"before,query"`
|
||||||
|
// Filter with a begin timestamp. Unix timestamp format in milliseconds, e.g. 1597026383085
|
||||||
|
begin *time.Time `param:"begin,query"`
|
||||||
|
|
||||||
|
// Filter with an end timestamp. Unix timestamp format in milliseconds, e.g. 1597026383085
|
||||||
|
end *time.Time `param:"end,query"`
|
||||||
|
limit *string `param:"limit,query"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RestClient) NewGetOpenOrdersRequest() *GetOpenOrdersRequest {
|
||||||
|
return &GetOpenOrdersRequest{
|
||||||
|
client: c,
|
||||||
|
instrumentType: InstrumentTypeSpot,
|
||||||
|
}
|
||||||
|
}
|
321
pkg/exchange/okex/okexapi/get_open_orders_request_requestgen.go
Normal file
321
pkg/exchange/okex/okexapi/get_open_orders_request_requestgen.go
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/v5/trade/orders-pending -type GetOpenOrdersRequest -responseDataType []OpenOrder"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package okexapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) InstrumentID(instrumentID string) *GetOpenOrdersRequest {
|
||||||
|
g.instrumentID = &instrumentID
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) InstrumentType(instrumentType InstrumentType) *GetOpenOrdersRequest {
|
||||||
|
g.instrumentType = instrumentType
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) OrderType(orderType OrderType) *GetOpenOrdersRequest {
|
||||||
|
g.orderType = &orderType
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) State(state OrderState) *GetOpenOrdersRequest {
|
||||||
|
g.state = &state
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) Category(category string) *GetOpenOrdersRequest {
|
||||||
|
g.category = &category
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) After(after string) *GetOpenOrdersRequest {
|
||||||
|
g.after = &after
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) Before(before string) *GetOpenOrdersRequest {
|
||||||
|
g.before = &before
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) Begin(begin time.Time) *GetOpenOrdersRequest {
|
||||||
|
g.begin = &begin
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) End(end time.Time) *GetOpenOrdersRequest {
|
||||||
|
g.end = &end
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) Limit(limit string) *GetOpenOrdersRequest {
|
||||||
|
g.limit = &limit
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQueryParameters builds and checks the query parameters and returns url.Values
|
||||||
|
func (g *GetOpenOrdersRequest) GetQueryParameters() (url.Values, error) {
|
||||||
|
var params = map[string]interface{}{}
|
||||||
|
// check instrumentID field -> json key instId
|
||||||
|
if g.instrumentID != nil {
|
||||||
|
instrumentID := *g.instrumentID
|
||||||
|
|
||||||
|
// assign parameter of instrumentID
|
||||||
|
params["instId"] = instrumentID
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// check instrumentType field -> json key instType
|
||||||
|
instrumentType := g.instrumentType
|
||||||
|
|
||||||
|
// TEMPLATE check-valid-values
|
||||||
|
switch instrumentType {
|
||||||
|
case InstrumentTypeSpot, InstrumentTypeSwap, InstrumentTypeFutures, InstrumentTypeOption, InstrumentTypeMARGIN:
|
||||||
|
params["instType"] = instrumentType
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("instType value %v is invalid", instrumentType)
|
||||||
|
|
||||||
|
}
|
||||||
|
// END TEMPLATE check-valid-values
|
||||||
|
|
||||||
|
// assign parameter of instrumentType
|
||||||
|
params["instType"] = instrumentType
|
||||||
|
// check orderType field -> json key ordType
|
||||||
|
if g.orderType != nil {
|
||||||
|
orderType := *g.orderType
|
||||||
|
|
||||||
|
// TEMPLATE check-valid-values
|
||||||
|
switch orderType {
|
||||||
|
case OrderTypeMarket, OrderTypeLimit, OrderTypePostOnly, OrderTypeFOK, OrderTypeIOC:
|
||||||
|
params["ordType"] = orderType
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("ordType value %v is invalid", orderType)
|
||||||
|
|
||||||
|
}
|
||||||
|
// END TEMPLATE check-valid-values
|
||||||
|
|
||||||
|
// assign parameter of orderType
|
||||||
|
params["ordType"] = orderType
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// check state field -> json key state
|
||||||
|
if g.state != nil {
|
||||||
|
state := *g.state
|
||||||
|
|
||||||
|
// TEMPLATE check-valid-values
|
||||||
|
switch state {
|
||||||
|
case OrderStateCanceled, OrderStateLive, OrderStatePartiallyFilled, OrderStateFilled:
|
||||||
|
params["state"] = state
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("state value %v is invalid", state)
|
||||||
|
|
||||||
|
}
|
||||||
|
// END TEMPLATE check-valid-values
|
||||||
|
|
||||||
|
// assign parameter of state
|
||||||
|
params["state"] = state
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// check category field -> json key category
|
||||||
|
if g.category != nil {
|
||||||
|
category := *g.category
|
||||||
|
|
||||||
|
// assign parameter of category
|
||||||
|
params["category"] = category
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// check after field -> json key after
|
||||||
|
if g.after != nil {
|
||||||
|
after := *g.after
|
||||||
|
|
||||||
|
// assign parameter of after
|
||||||
|
params["after"] = after
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// check before field -> json key before
|
||||||
|
if g.before != nil {
|
||||||
|
before := *g.before
|
||||||
|
|
||||||
|
// assign parameter of before
|
||||||
|
params["before"] = before
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// check begin field -> json key begin
|
||||||
|
if g.begin != nil {
|
||||||
|
begin := *g.begin
|
||||||
|
|
||||||
|
// assign parameter of begin
|
||||||
|
params["begin"] = begin
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// check end field -> json key end
|
||||||
|
if g.end != nil {
|
||||||
|
end := *g.end
|
||||||
|
|
||||||
|
// assign parameter of end
|
||||||
|
params["end"] = end
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// check limit field -> json key limit
|
||||||
|
if g.limit != nil {
|
||||||
|
limit := *g.limit
|
||||||
|
|
||||||
|
// assign parameter of limit
|
||||||
|
params["limit"] = limit
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
query := url.Values{}
|
||||||
|
for _k, _v := range params {
|
||||||
|
query.Add(_k, fmt.Sprintf("%v", _v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return query, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetParameters builds and checks the parameters and return the result in a map object
|
||||||
|
func (g *GetOpenOrdersRequest) GetParameters() (map[string]interface{}, error) {
|
||||||
|
var params = map[string]interface{}{}
|
||||||
|
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
|
||||||
|
func (g *GetOpenOrdersRequest) GetParametersQuery() (url.Values, error) {
|
||||||
|
query := url.Values{}
|
||||||
|
|
||||||
|
params, err := g.GetParameters()
|
||||||
|
if err != nil {
|
||||||
|
return query, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _k, _v := range params {
|
||||||
|
if g.isVarSlice(_v) {
|
||||||
|
g.iterateSlice(_v, func(it interface{}) {
|
||||||
|
query.Add(_k+"[]", fmt.Sprintf("%v", it))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
query.Add(_k, fmt.Sprintf("%v", _v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetParametersJSON converts the parameters from GetParameters into the JSON format
|
||||||
|
func (g *GetOpenOrdersRequest) GetParametersJSON() ([]byte, error) {
|
||||||
|
params, err := g.GetParameters()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
|
||||||
|
func (g *GetOpenOrdersRequest) GetSlugParameters() (map[string]interface{}, error) {
|
||||||
|
var params = map[string]interface{}{}
|
||||||
|
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) applySlugsToUrl(url string, slugs map[string]string) string {
|
||||||
|
for _k, _v := range slugs {
|
||||||
|
needleRE := regexp.MustCompile(":" + _k + "\\b")
|
||||||
|
url = needleRE.ReplaceAllString(url, _v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
|
||||||
|
sliceValue := reflect.ValueOf(slice)
|
||||||
|
for _i := 0; _i < sliceValue.Len(); _i++ {
|
||||||
|
it := sliceValue.Index(_i).Interface()
|
||||||
|
_f(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) isVarSlice(_v interface{}) bool {
|
||||||
|
rt := reflect.TypeOf(_v)
|
||||||
|
switch rt.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GetOpenOrdersRequest) GetSlugsMap() (map[string]string, error) {
|
||||||
|
slugs := map[string]string{}
|
||||||
|
params, err := g.GetSlugParameters()
|
||||||
|
if err != nil {
|
||||||
|
return slugs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _k, _v := range params {
|
||||||
|
slugs[_k] = fmt.Sprintf("%v", _v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return slugs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPath returns the request path of the API
|
||||||
|
func (g *GetOpenOrdersRequest) GetPath() string {
|
||||||
|
return "/api/v5/trade/orders-pending"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do generates the request object and send the request object to the API endpoint
|
||||||
|
func (g *GetOpenOrdersRequest) Do(ctx context.Context) ([]OpenOrder, error) {
|
||||||
|
|
||||||
|
// no body params
|
||||||
|
var params interface{}
|
||||||
|
query, err := g.GetQueryParameters()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiURL string
|
||||||
|
|
||||||
|
apiURL = g.GetPath()
|
||||||
|
|
||||||
|
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := g.client.SendRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiResponse APIResponse
|
||||||
|
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseValidator interface {
|
||||||
|
Validate() error
|
||||||
|
}
|
||||||
|
validator, ok := interface{}(apiResponse).(responseValidator)
|
||||||
|
if ok {
|
||||||
|
if err := validator.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var data []OpenOrder
|
||||||
|
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
|
@ -4,11 +4,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *RestClient) NewBatchPlaceOrderRequest() *BatchPlaceOrderRequest {
|
func (c *RestClient) NewBatchPlaceOrderRequest() *BatchPlaceOrderRequest {
|
||||||
|
@ -29,12 +29,6 @@ func (c *RestClient) NewGetOrderDetailsRequest() *GetOrderDetailsRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RestClient) NewGetPendingOrderRequest() *GetPendingOrderRequest {
|
|
||||||
return &GetPendingOrderRequest{
|
|
||||||
client: c,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RestClient) NewGetTransactionDetailsRequest() *GetTransactionDetailsRequest {
|
func (c *RestClient) NewGetTransactionDetailsRequest() *GetTransactionDetailsRequest {
|
||||||
return &GetTransactionDetailsRequest{
|
return &GetTransactionDetailsRequest{
|
||||||
client: c,
|
client: c,
|
||||||
|
@ -249,89 +243,6 @@ func (r *GetOrderDetailsRequest) Do(ctx context.Context) (*OrderDetails, error)
|
||||||
return &data[0], nil
|
return &data[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetPendingOrderRequest struct {
|
|
||||||
client *RestClient
|
|
||||||
|
|
||||||
instId *string
|
|
||||||
|
|
||||||
instType *InstrumentType
|
|
||||||
|
|
||||||
orderTypes []string
|
|
||||||
|
|
||||||
state *OrderState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *GetPendingOrderRequest) InstrumentID(instId string) *GetPendingOrderRequest {
|
|
||||||
r.instId = &instId
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *GetPendingOrderRequest) InstrumentType(instType InstrumentType) *GetPendingOrderRequest {
|
|
||||||
r.instType = &instType
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *GetPendingOrderRequest) State(state OrderState) *GetPendingOrderRequest {
|
|
||||||
r.state = &state
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *GetPendingOrderRequest) OrderTypes(orderTypes []string) *GetPendingOrderRequest {
|
|
||||||
r.orderTypes = orderTypes
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *GetPendingOrderRequest) AddOrderTypes(orderTypes ...string) *GetPendingOrderRequest {
|
|
||||||
r.orderTypes = append(r.orderTypes, orderTypes...)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *GetPendingOrderRequest) Parameters() map[string]interface{} {
|
|
||||||
var payload = map[string]interface{}{}
|
|
||||||
|
|
||||||
if r.instId != nil {
|
|
||||||
payload["instId"] = r.instId
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.instType != nil {
|
|
||||||
payload["instType"] = r.instType
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.state != nil {
|
|
||||||
payload["state"] = r.state
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(r.orderTypes) > 0 {
|
|
||||||
payload["ordType"] = strings.Join(r.orderTypes, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
return payload
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *GetPendingOrderRequest) Do(ctx context.Context) ([]OrderDetails, error) {
|
|
||||||
payload := r.Parameters()
|
|
||||||
req, err := r.client.NewAuthenticatedRequest(ctx, "GET", "/api/v5/trade/orders-pending", nil, payload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := r.client.SendRequest(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var apiResponse APIResponse
|
|
||||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var data []OrderDetails
|
|
||||||
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetTransactionDetailsRequest struct {
|
type GetTransactionDetailsRequest struct {
|
||||||
client *RestClient
|
client *RestClient
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user