Merge pull request #1563 from c9s/edwin/bitget/test-place-order-2

TEST: [bitget] add market/limit maker tests for place order
This commit is contained in:
c9s 2024-03-06 22:39:05 +08:00 committed by GitHub
commit f4a8dc0f8c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 543 additions and 8 deletions

View File

@ -0,0 +1,26 @@
{
"code":"00000",
"msg":"success",
"requestTime":1709649001209,
"data":[
{
"userId":"8672173294",
"symbol":"BTCUSDT",
"orderId":"1148903850645331968",
"clientOid":"684a79df-f931-474f-a9a5-f1deab1cd770",
"price":"0",
"size":"6.0000000000000000",
"orderType":"market",
"side":"buy",
"status":"filled",
"priceAvg":"67360.8700000000000000",
"baseVolume":"0.0000890000000000",
"quoteVolume":"5.9951174300000000",
"enterPointSource":"API",
"feeDetail":"{\"BTC\":{\"deduction\":false,\"feeCoinCode\":\"BTC\",\"totalDeductionFee\":0,\"totalFee\":-8.90000000E-8},\"newFees\":{\"c\":0,\"d\":0,\"deduction\":false,\"r\":-8.9E-8,\"t\":-8.9E-8,\"totalDeductionFee\":0}}",
"orderSource":"market",
"cTime":"1709645944272",
"uTime":"1709645944272"
}
]
}

View File

@ -0,0 +1,25 @@
{
"code":"00000",
"msg":"success",
"requestTime":1709692258790,
"data":[
{
"userId":"8672173294",
"symbol":"BTCUSDT",
"orderId":"1148903850645331968",
"clientOid":"684a79df-f931-474f-a9a5-f1deab1cd770",
"priceAvg":"0",
"size":"6",
"orderType":"market",
"side":"buy",
"status":"live",
"basePrice":"0",
"baseVolume":"0",
"quoteVolume":"0",
"enterPointSource":"API",
"orderSource":"market",
"cTime":"1709645944272",
"uTime":"1709645944272"
}
]
}

View File

@ -0,0 +1,25 @@
{
"code":"00000",
"msg":"success",
"requestTime":1709692258790,
"data":[
{
"userId":"8672173294",
"symbol":"BTCUSDT",
"orderId":"1148903850645331968",
"clientOid":"684a79df-f931-474f-a9a5-f1deab1cd770",
"priceAvg":"0",
"size":"0.00009",
"orderType":"market",
"side":"sell",
"status":"live",
"basePrice":"0",
"baseVolume":"0",
"quoteVolume":"0",
"enterPointSource":"API",
"orderSource":"market",
"cTime":"1709645944272",
"uTime":"1709645944272"
}
]
}

View File

@ -3,6 +3,7 @@ package bitget
import (
"context"
"encoding/json"
"fmt"
"io"
"math"
"net/http"
@ -545,13 +546,15 @@ func TestExchange_QueryAccountBalances(t *testing.T) {
func TestExchange_SubmitOrder(t *testing.T) {
var (
assert = assert.New(t)
ex = New("key", "secret", "passphrase")
placeOrderUrl = "/api/v2/spot/trade/place-order"
openOrderUrl = "/api/v2/spot/trade/unfilled-orders"
clientOrderId = "684a79df-f931-474f-a9a5-f1deab1cd770"
expBtcSymbol = "BTCUSDT"
expOrder = &types.Order{
assert = assert.New(t)
ex = New("key", "secret", "passphrase")
placeOrderUrl = "/api/v2/spot/trade/place-order"
openOrderUrl = "/api/v2/spot/trade/unfilled-orders"
tickerUrl = "/api/v2/spot/market/tickers"
historyOrderUrl = "/api/v2/spot/trade/history-orders"
clientOrderId = "684a79df-f931-474f-a9a5-f1deab1cd770"
expBtcSymbol = "BTCUSDT"
expOrder = &types.Order{
SubmitOrder: types.SubmitOrder{
ClientOrderID: clientOrderId,
Symbol: expBtcSymbol,
@ -642,7 +645,277 @@ func TestExchange_SubmitOrder(t *testing.T) {
assert.Equal(expOrder, acct)
})
// TODO: add market buy, limit maker
t.Run("Limit Maker order", func(t *testing.T) {
transport := &httptesting.MockTransport{}
ex.client.HttpClient.Transport = transport
placeOrderFile, err := os.ReadFile("bitgetapi/v2/testdata/place_order_request.json")
assert.NoError(err)
transport.POST(placeOrderUrl, func(req *http.Request) (*http.Response, error) {
raw, err := io.ReadAll(req.Body)
assert.NoError(err)
reqq := &NewOrder{}
err = json.Unmarshal(raw, &reqq)
assert.NoError(err)
assert.Equal(&NewOrder{
ClientOid: expOrder.ClientOrderID,
Force: string(v2.OrderForcePostOnly),
OrderType: string(v2.OrderTypeLimit),
Price: "66000.00",
Side: string(v2.SideTypeBuy),
Size: "0.000090",
Symbol: expBtcSymbol,
}, reqq)
return httptesting.BuildResponseString(http.StatusOK, string(placeOrderFile)), nil
})
unfilledFile, err := os.ReadFile("bitgetapi/v2/testdata/get_unfilled_orders_request_limit_order.json")
assert.NoError(err)
transport.GET(openOrderUrl, func(req *http.Request) (*http.Response, error) {
query := req.URL.Query()
assert.Len(query, 1)
assert.Contains(query, "orderId")
assert.Equal(query["orderId"], []string{strconv.FormatUint(expOrder.OrderID, 10)})
return httptesting.BuildResponseString(http.StatusOK, string(unfilledFile)), nil
})
reqLimitOrder2 := reqLimitOrder
reqLimitOrder2.Type = types.OrderTypeLimitMaker
acct, err := ex.SubmitOrder(context.Background(), reqLimitOrder2)
assert.NoError(err)
assert.Equal(expOrder, acct)
})
t.Run("Market order", func(t *testing.T) {
t.Run("Buy", func(t *testing.T) {
transport := &httptesting.MockTransport{}
ex.client.HttpClient.Transport = transport
// get ticker to calculate btc amount
tickerFile, err := os.ReadFile("bitgetapi/v2/testdata/get_ticker_request.json")
assert.NoError(err)
transport.GET(tickerUrl, func(req *http.Request) (*http.Response, error) {
assert.Contains(req.URL.Query(), "symbol")
assert.Equal(req.URL.Query()["symbol"], []string{expBtcSymbol})
return httptesting.BuildResponseString(http.StatusOK, string(tickerFile)), nil
})
// place order
placeOrderFile, err := os.ReadFile("bitgetapi/v2/testdata/place_order_request.json")
assert.NoError(err)
transport.POST(placeOrderUrl, func(req *http.Request) (*http.Response, error) {
raw, err := io.ReadAll(req.Body)
assert.NoError(err)
reqq := &NewOrder{}
err = json.Unmarshal(raw, &reqq)
assert.NoError(err)
assert.Equal(&NewOrder{
ClientOid: expOrder.ClientOrderID,
Force: string(v2.OrderForceGTC),
OrderType: string(v2.OrderTypeMarket),
Price: "",
Side: string(v2.SideTypeBuy),
Size: reqLimitOrder.Market.FormatQuantity(fixedpoint.MustNewFromString("66554").Mul(fixedpoint.MustNewFromString("0.00009"))), // ticker: 66554, size: 0.00009
Symbol: expBtcSymbol,
}, reqq)
return httptesting.BuildResponseString(http.StatusOK, string(placeOrderFile)), nil
})
// unfilled order
unfilledFile, err := os.ReadFile("bitgetapi/v2/testdata/get_unfilled_orders_request_market_buy_order.json")
assert.NoError(err)
transport.GET(openOrderUrl, func(req *http.Request) (*http.Response, error) {
query := req.URL.Query()
assert.Len(query, 1)
assert.Contains(query, "orderId")
assert.Equal(query["orderId"], []string{strconv.FormatUint(expOrder.OrderID, 10)})
return httptesting.BuildResponseString(http.StatusOK, string(unfilledFile)), nil
})
reqMarketOrder := reqLimitOrder
reqMarketOrder.Side = types.SideTypeBuy
reqMarketOrder.Type = types.OrderTypeMarket
acct, err := ex.SubmitOrder(context.Background(), reqMarketOrder)
assert.NoError(err)
expOrder2 := *expOrder
expOrder2.Side = types.SideTypeBuy
expOrder2.Type = types.OrderTypeMarket
expOrder2.Quantity = fixedpoint.Zero
expOrder2.Price = fixedpoint.Zero
assert.Equal(&expOrder2, acct)
})
t.Run("Sell", func(t *testing.T) {
transport := &httptesting.MockTransport{}
ex.client.HttpClient.Transport = transport
// get ticker to calculate btc amount
tickerFile, err := os.ReadFile("bitgetapi/v2/testdata/get_ticker_request.json")
assert.NoError(err)
transport.GET(tickerUrl, func(req *http.Request) (*http.Response, error) {
assert.Contains(req.URL.Query(), "symbol")
assert.Equal(req.URL.Query()["symbol"], []string{expBtcSymbol})
return httptesting.BuildResponseString(http.StatusOK, string(tickerFile)), nil
})
// place order
placeOrderFile, err := os.ReadFile("bitgetapi/v2/testdata/place_order_request.json")
assert.NoError(err)
transport.POST(placeOrderUrl, func(req *http.Request) (*http.Response, error) {
raw, err := io.ReadAll(req.Body)
assert.NoError(err)
reqq := &NewOrder{}
err = json.Unmarshal(raw, &reqq)
assert.NoError(err)
assert.Equal(&NewOrder{
ClientOid: expOrder.ClientOrderID,
Force: string(v2.OrderForceGTC),
OrderType: string(v2.OrderTypeMarket),
Price: "",
Side: string(v2.SideTypeSell),
Size: "0.000090", // size: 0.00009
Symbol: expBtcSymbol,
}, reqq)
return httptesting.BuildResponseString(http.StatusOK, string(placeOrderFile)), nil
})
// unfilled order
unfilledFile, err := os.ReadFile("bitgetapi/v2/testdata/get_unfilled_orders_request_market_sell_order.json")
assert.NoError(err)
transport.GET(openOrderUrl, func(req *http.Request) (*http.Response, error) {
query := req.URL.Query()
assert.Len(query, 1)
assert.Contains(query, "orderId")
assert.Equal(query["orderId"], []string{strconv.FormatUint(expOrder.OrderID, 10)})
return httptesting.BuildResponseString(http.StatusOK, string(unfilledFile)), nil
})
reqMarketOrder := reqLimitOrder
reqMarketOrder.Side = types.SideTypeSell
reqMarketOrder.Type = types.OrderTypeMarket
acct, err := ex.SubmitOrder(context.Background(), reqMarketOrder)
assert.NoError(err)
expOrder2 := *expOrder
expOrder2.Side = types.SideTypeSell
expOrder2.Type = types.OrderTypeMarket
expOrder2.Price = fixedpoint.Zero
assert.Equal(&expOrder2, acct)
})
t.Run("failed to get ticker on buy", func(t *testing.T) {
transport := &httptesting.MockTransport{}
ex.client.HttpClient.Transport = transport
// get ticker to calculate btc amount
requestErrFile, err := os.ReadFile("bitgetapi/v2/testdata/request_error.json")
assert.NoError(err)
transport.GET(tickerUrl, func(req *http.Request) (*http.Response, error) {
assert.Contains(req.URL.Query(), "symbol")
assert.Equal(req.URL.Query()["symbol"], []string{expBtcSymbol})
return httptesting.BuildResponseString(http.StatusBadRequest, string(requestErrFile)), nil
})
reqMarketOrder := reqLimitOrder
reqMarketOrder.Side = types.SideTypeBuy
reqMarketOrder.Type = types.OrderTypeMarket
_, err = ex.SubmitOrder(context.Background(), reqMarketOrder)
assert.ErrorContains(err, "Invalid IP")
})
t.Run("get order from history due to unfilled order not found", func(t *testing.T) {
transport := &httptesting.MockTransport{}
ex.client.HttpClient.Transport = transport
// get ticker to calculate btc amount
tickerFile, err := os.ReadFile("bitgetapi/v2/testdata/get_ticker_request.json")
assert.NoError(err)
transport.GET(tickerUrl, func(req *http.Request) (*http.Response, error) {
assert.Contains(req.URL.Query(), "symbol")
assert.Equal(req.URL.Query()["symbol"], []string{expBtcSymbol})
return httptesting.BuildResponseString(http.StatusOK, string(tickerFile)), nil
})
// place order
placeOrderFile, err := os.ReadFile("bitgetapi/v2/testdata/place_order_request.json")
assert.NoError(err)
transport.POST(placeOrderUrl, func(req *http.Request) (*http.Response, error) {
raw, err := io.ReadAll(req.Body)
assert.NoError(err)
reqq := &NewOrder{}
err = json.Unmarshal(raw, &reqq)
assert.NoError(err)
assert.Equal(&NewOrder{
ClientOid: expOrder.ClientOrderID,
Force: string(v2.OrderForceGTC),
OrderType: string(v2.OrderTypeMarket),
Price: "",
Side: string(v2.SideTypeBuy),
Size: reqLimitOrder.Market.FormatQuantity(fixedpoint.MustNewFromString("66554").Mul(fixedpoint.MustNewFromString("0.00009"))), // ticker: 66554, size: 0.00009
Symbol: expBtcSymbol,
}, reqq)
return httptesting.BuildResponseString(http.StatusOK, string(placeOrderFile)), nil
})
// unfilled order
transport.GET(openOrderUrl, func(req *http.Request) (*http.Response, error) {
query := req.URL.Query()
assert.Len(query, 1)
assert.Contains(query, "orderId")
assert.Equal(query["orderId"], []string{strconv.FormatUint(expOrder.OrderID, 10)})
apiResp := v2.APIResponse{Code: "00000"}
raw, err := json.Marshal(apiResp)
assert.NoError(err)
return httptesting.BuildResponseString(http.StatusOK, string(raw)), nil
})
// order history
historyOrderFile, err := os.ReadFile("bitgetapi/v2/testdata/get_history_orders_request_market_buy.json")
assert.NoError(err)
transport.GET(historyOrderUrl, func(req *http.Request) (*http.Response, error) {
query := req.URL.Query()
assert.Len(query, 1)
assert.Contains(query, "orderId")
assert.Equal(query["orderId"], []string{strconv.FormatUint(expOrder.OrderID, 10)})
return httptesting.BuildResponseString(http.StatusOK, string(historyOrderFile)), nil
})
reqMarketOrder := reqLimitOrder
reqMarketOrder.Side = types.SideTypeBuy
reqMarketOrder.Type = types.OrderTypeMarket
acct, err := ex.SubmitOrder(context.Background(), reqMarketOrder)
assert.NoError(err)
expOrder2 := *expOrder
expOrder2.Side = types.SideTypeBuy
expOrder2.Type = types.OrderTypeMarket
expOrder2.Status = types.OrderStatusFilled
expOrder2.ExecutedQuantity = fixedpoint.MustNewFromString("0.000089")
expOrder2.Quantity = fixedpoint.MustNewFromString("0.000089")
expOrder2.Price = fixedpoint.MustNewFromString("67360.87")
expOrder2.IsWorking = false
assert.Equal(&expOrder2, acct)
})
})
t.Run("error on query open orders", func(t *testing.T) {
transport := &httptesting.MockTransport{}
@ -801,3 +1074,189 @@ func TestExchange_SubmitOrder(t *testing.T) {
assert.ErrorContains(err, "not supported")
})
}
func TestExchange_QueryOpenOrders(t *testing.T) {
var (
assert = assert.New(t)
ex = New("key", "secret", "passphrase")
expBtcSymbol = "BTCUSDT"
url = "/api/v2/spot/trade/unfilled-orders"
)
t.Run("succeeds", func(t *testing.T) {
transport := &httptesting.MockTransport{}
ex.client.HttpClient.Transport = transport
f, err := os.ReadFile("bitgetapi/v2/testdata/get_unfilled_orders_request_limit_order.json")
assert.NoError(err)
transport.GET(url, func(req *http.Request) (*http.Response, error) {
query := req.URL.Query()
assert.Len(query, 2)
assert.Contains(query, "symbol")
assert.Equal(query["symbol"], []string{expBtcSymbol})
assert.Equal(query["limit"], []string{strconv.FormatInt(queryLimit, 10)})
return httptesting.BuildResponseString(http.StatusOK, string(f)), nil
})
orders, err := ex.QueryOpenOrders(context.Background(), expBtcSymbol)
assert.NoError(err)
expOrder := []types.Order{
{
SubmitOrder: types.SubmitOrder{
ClientOrderID: "684a79df-f931-474f-a9a5-f1deab1cd770",
Symbol: expBtcSymbol,
Side: types.SideTypeBuy,
Type: types.OrderTypeLimit,
Quantity: fixedpoint.MustNewFromString("0.00009"),
Price: fixedpoint.MustNewFromString("66000"),
TimeInForce: types.TimeInForceGTC,
},
Exchange: types.ExchangeBitget,
OrderID: 1148903850645331968,
UUID: "1148903850645331968",
Status: types.OrderStatusNew,
ExecutedQuantity: fixedpoint.Zero,
IsWorking: true,
CreationTime: types.Time(types.NewMillisecondTimestampFromInt(1709645944272).Time()),
UpdateTime: types.Time(types.NewMillisecondTimestampFromInt(1709645944272).Time()),
},
}
assert.Equal(expOrder, orders)
})
t.Run("succeeds on pagination with mock limit + 1", func(t *testing.T) {
transport := &httptesting.MockTransport{}
ex.client.HttpClient.Transport = transport
dataTemplate := `{
"userId":"8672173294",
"symbol":"BTCUSDT",
"orderId":"%d",
"clientOid":"684a79df-f931-474f-a9a5-f1deab1cd770",
"priceAvg":"0",
"size":"0.00009",
"orderType":"market",
"side":"sell",
"status":"live",
"basePrice":"0",
"baseVolume":"0",
"quoteVolume":"0",
"enterPointSource":"API",
"orderSource":"market",
"cTime":"1709645944272",
"uTime":"1709645944272"
}`
openOrdersStr := make([]string, 0, queryLimit+1)
expOrders := make([]types.Order, 0, queryLimit+1)
for i := 0; i < queryLimit+1; i++ {
dataStr := fmt.Sprintf(dataTemplate, i)
openOrdersStr = append(openOrdersStr, dataStr)
unfilledOdr := &v2.UnfilledOrder{}
err := json.Unmarshal([]byte(dataStr), &unfilledOdr)
assert.NoError(err)
gOdr, err := unfilledOrderToGlobalOrder(*unfilledOdr)
assert.NoError(err)
expOrders = append(expOrders, *gOdr)
}
transport.GET(url, func(req *http.Request) (*http.Response, error) {
query := req.URL.Query()
assert.Contains(query, "symbol")
assert.Equal(query["symbol"], []string{expBtcSymbol})
assert.Equal(query["limit"], []string{strconv.FormatInt(queryLimit, 10)})
if len(query) == 2 {
// first time query
resp := &v2.APIResponse{
Code: "00000",
Data: []byte("[" + strings.Join(openOrdersStr[0:queryLimit], ",") + "]"),
}
respRaw, err := json.Marshal(resp)
assert.NoError(err)
return httptesting.BuildResponseString(http.StatusOK, string(respRaw)), nil
}
// second time query
// last order id, so need to -1
assert.Equal(query["idLessThan"], []string{strconv.FormatInt(queryLimit-1, 10)})
resp := &v2.APIResponse{
Code: "00000",
Data: []byte("[" + strings.Join(openOrdersStr[queryLimit:queryLimit+1], ",") + "]"),
}
respRaw, err := json.Marshal(resp)
assert.NoError(err)
return httptesting.BuildResponseString(http.StatusOK, string(respRaw)), nil
})
orders, err := ex.QueryOpenOrders(context.Background(), expBtcSymbol)
assert.NoError(err)
assert.Equal(expOrders, orders)
})
t.Run("succeeds on pagination with mock limit + 1", func(t *testing.T) {
transport := &httptesting.MockTransport{}
ex.client.HttpClient.Transport = transport
dataTemplate := `{
"userId":"8672173294",
"symbol":"BTCUSDT",
"orderId":"%d",
"clientOid":"684a79df-f931-474f-a9a5-f1deab1cd770",
"priceAvg":"0",
"size":"0.00009",
"orderType":"market",
"side":"sell",
"status":"live",
"basePrice":"0",
"baseVolume":"0",
"quoteVolume":"0",
"enterPointSource":"API",
"orderSource":"market",
"cTime":"1709645944272",
"uTime":"1709645944272"
}`
openOrdersStr := make([]string, 0, queryLimit+1)
for i := 0; i < queryLimit+1; i++ {
dataStr := fmt.Sprintf(dataTemplate, i)
openOrdersStr = append(openOrdersStr, dataStr)
}
transport.GET(url, func(req *http.Request) (*http.Response, error) {
query := req.URL.Query()
assert.Contains(query, "symbol")
assert.Equal(query["symbol"], []string{expBtcSymbol})
assert.Equal(query["limit"], []string{strconv.FormatInt(queryLimit, 10)})
// first time query
resp := &v2.APIResponse{
Code: "00000",
Data: []byte("[" + strings.Join(openOrdersStr, ",") + "]"),
}
respRaw, err := json.Marshal(resp)
assert.NoError(err)
return httptesting.BuildResponseString(http.StatusOK, string(respRaw)), nil
})
_, err := ex.QueryOpenOrders(context.Background(), expBtcSymbol)
assert.ErrorContains(err, "unexpected open orders length")
})
t.Run("error", func(t *testing.T) {
transport := &httptesting.MockTransport{}
ex.client.HttpClient.Transport = transport
f, err := os.ReadFile("bitgetapi/v2/testdata/request_error.json")
assert.NoError(err)
transport.GET(url, func(req *http.Request) (*http.Response, error) {
return httptesting.BuildResponseString(http.StatusBadRequest, string(f)), nil
})
_, err = ex.QueryOpenOrders(context.Background(), "BTCUSDT")
assert.ErrorContains(err, "Invalid IP")
})
}