From 6e763838976dc46d3adbc69db13eb964446f8941 Mon Sep 17 00:00:00 2001 From: Samuel Reid <43227667+cranktakular@users.noreply.github.com> Date: Tue, 19 Nov 2024 18:17:28 +1100 Subject: [PATCH] Approaching test perfection --- .../exchange_wrapper_standards_test.go | 2 + exchanges/bitget/bitget_test.go | 149 +++++++++------ exchanges/bitget/bitget_wrapper.go | 179 +++++++++++++----- 3 files changed, 229 insertions(+), 101 deletions(-) diff --git a/cmd/exchange_wrapper_standards/exchange_wrapper_standards_test.go b/cmd/exchange_wrapper_standards/exchange_wrapper_standards_test.go index d1eedce4dec..8616c2fecd0 100644 --- a/cmd/exchange_wrapper_standards/exchange_wrapper_standards_test.go +++ b/cmd/exchange_wrapper_standards/exchange_wrapper_standards_test.go @@ -452,6 +452,7 @@ func generateMethodArg(ctx context.Context, t *testing.T, argGenerator *MethodAr ClientOrderID: "13371337", ImmediateOrCancel: true, Leverage: 1, + MarginType: margin.Isolated, }) case argGenerator.MethodInputType.AssignableTo(orderModifyParam): input = reflect.ValueOf(&order.Modify{ @@ -465,6 +466,7 @@ func generateMethodArg(ctx context.Context, t *testing.T, argGenerator *MethodAr ClientOrderID: "13371337", OrderID: "1337", ImmediateOrCancel: true, + TriggerPrice: 149, }) case argGenerator.MethodInputType.AssignableTo(orderCancelParam): input = reflect.ValueOf(&order.Cancel{ diff --git a/exchanges/bitget/bitget_test.go b/exchanges/bitget/bitget_test.go index 7a2c96bac72..7a2b190e2fe 100644 --- a/exchanges/bitget/bitget_test.go +++ b/exchanges/bitget/bitget_test.go @@ -8,6 +8,7 @@ import ( "net/http" "net/url" "os" + "slices" "strconv" "strings" "testing" @@ -98,7 +99,14 @@ func TestMain(m *testing.M) { if err != nil { log.Fatal(err) } + bi.Websocket.Wg.Add(1) go bi.wsReadData(bi.Websocket.Conn) + stream.Connection.SetupPingHandler(bi.Websocket.Conn, stream.PingHandler{ + Websocket: true, + Message: []byte(`ping`), + MessageType: websocket.TextMessage, + Delay: time.Second * 25, + }) os.Exit(m.Run()) } @@ -230,7 +238,7 @@ func TestGetMerchantAdvertisementList(t *testing.T) { _, err := bi.GetMerchantAdvertisementList(context.Background(), time.Time{}, time.Time{}, 0, 0, 0, 0, "", "", "", "", currency.Code{}, currency.Code{}) assert.ErrorIs(t, err, common.ErrDateUnset) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi) - _, err = bi.GetMerchantAdvertisementList(context.Background(), time.Now().Add(-time.Hour*24*7), time.Now(), 5, 1<<62, 0, 0, "", "sell", "USDT", "", currency.Code{}, currency.Code{}) + _, err = bi.GetMerchantAdvertisementList(context.Background(), time.Now().Add(-time.Hour*24*7), time.Now(), 5, 1<<62, 0, 0, "", "sell", "", "", testCrypto, currency.Code{}) assert.NoError(t, err) } @@ -761,8 +769,7 @@ func TestGetCurrentSpotPlanOrders(t *testing.T) { func TestSpotGetPlanSubOrder(t *testing.T) { t.Parallel() sharedtestvalues.SkipTestIfCredentialsUnset(t, bi) - var ordIDs *OrderIDStruct - ordIDs = getPlanOrdIDHelper(t, true) + ordIDs := getPlanOrdIDHelper(t, true) require.NotNil(t, ordIDs) // This gets the error "the current plan order does not exist or has not been triggered" even when using // a plan order that definitely exists and has definitely been triggered. Re-investigate later @@ -837,7 +844,7 @@ func TestTransferAsset(t *testing.T) { assert.ErrorIs(t, err, errToTypeEmpty) _, err = bi.TransferAsset(context.Background(), "meow", "woof", "", currency.Code{}, currency.Pair{}, 0) assert.ErrorIs(t, err, errCurrencyAndPairEmpty) - _, err = bi.TransferAsset(context.Background(), "meow", "woof", "neigh", currency.Code{}, currency.Pair{}, 0) + _, err = bi.TransferAsset(context.Background(), "meow", "woof", "", currency.Code{}, testPair, 0) assert.ErrorIs(t, err, errAmountEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) _, err = bi.TransferAsset(context.Background(), "spot", "p2p", clientIDGenerator(), testCrypto, testPair, testAmount) @@ -864,11 +871,11 @@ func TestSubaccountTransfer(t *testing.T) { assert.ErrorIs(t, err, errToTypeEmpty) _, err = bi.SubaccountTransfer(context.Background(), "meow", "woof", "", "", "", currency.Code{}, currency.Pair{}, 0) assert.ErrorIs(t, err, errCurrencyAndPairEmpty) - _, err = bi.SubaccountTransfer(context.Background(), "meow", "woof", "neigh", "", "", currency.Code{}, currency.Pair{}, 0) + _, err = bi.SubaccountTransfer(context.Background(), "meow", "woof", "", "", "", testCrypto, currency.Pair{}, 0) assert.ErrorIs(t, err, errFromIDEmpty) - _, err = bi.SubaccountTransfer(context.Background(), "meow", "woof", "neigh", "", "", testCrypto, currency.Pair{}, 0) + _, err = bi.SubaccountTransfer(context.Background(), "meow", "woof", "", "neigh", "", testCrypto, currency.Pair{}, 0) assert.ErrorIs(t, err, errToIDEmpty) - _, err = bi.SubaccountTransfer(context.Background(), "meow", "woof", "neigh", "", "", testCrypto, testPair, 0) + _, err = bi.SubaccountTransfer(context.Background(), "meow", "woof", "", "neigh", "moo", testCrypto, currency.Pair{}, 0) assert.ErrorIs(t, err, errAmountEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) fromID := subAccTestHelper(t, "", strings.ToLower(string(testSubaccountName[:3]))+"****@virtual-bitget.com") @@ -1143,10 +1150,10 @@ func TestChangeLeverage(t *testing.T) { assert.ErrorIs(t, err, errProductTypeEmpty) _, err = bi.ChangeLeverage(context.Background(), testPair, "woof", "", currency.Code{}, 0) assert.ErrorIs(t, err, errMarginCoinEmpty) - _, err = bi.ChangeLeverage(context.Background(), testPair, "woof", "neigh", currency.Code{}, 0) + _, err = bi.ChangeLeverage(context.Background(), testPair, "woof", "", testFiat, 0) assert.ErrorIs(t, err, errLeverageEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) - resp, err := bi.ChangeLeverage(context.Background(), testPair, "USDT-FUTURES", "USDT", currency.Code{}, 20) + resp, err := bi.ChangeLeverage(context.Background(), testPair, "USDT-FUTURES", "", testFiat, 20) require.NoError(t, err) assert.NotEmpty(t, resp) } @@ -1172,7 +1179,7 @@ func TestAdjustMargin(t *testing.T) { assert.ErrorIs(t, err, errProductTypeEmpty) err = bi.AdjustMargin(context.Background(), testPair2, "woof", "", currency.Code{}, 0) assert.ErrorIs(t, err, errMarginCoinEmpty) - err = bi.AdjustMargin(context.Background(), testPair2, "woof", "neigh", currency.Code{}, 0) + err = bi.AdjustMargin(context.Background(), testPair2, "woof", "", testFiat2, 0) assert.ErrorIs(t, err, errAmountEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) // This is getting the error "verification exception margin mode == FIXED", and I can't find a way to skirt around that @@ -1188,7 +1195,7 @@ func TestChangeMarginMode(t *testing.T) { assert.ErrorIs(t, err, errProductTypeEmpty) _, err = bi.ChangeMarginMode(context.Background(), testPair2, "woof", "", currency.Code{}) assert.ErrorIs(t, err, errMarginCoinEmpty) - _, err = bi.ChangeMarginMode(context.Background(), testPair2, "woof", "neigh", currency.Code{}) + _, err = bi.ChangeMarginMode(context.Background(), testPair2, "woof", "", testFiat2) assert.ErrorIs(t, err, errMarginModeEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) _, err = bi.ChangeMarginMode(context.Background(), testPair2, testFiat2.String()+"-FUTURES", "crossed", testFiat2) @@ -1278,13 +1285,13 @@ func TestPlaceFuturesOrder(t *testing.T) { assert.ErrorIs(t, err, errMarginModeEmpty) _, err = bi.PlaceFuturesOrder(context.Background(), testPair2, "woof", "neigh", "", "", "", "", "", currency.Code{}, 0, 0, 0, 0, false, false) assert.ErrorIs(t, err, errMarginCoinEmpty) - _, err = bi.PlaceFuturesOrder(context.Background(), testPair2, "woof", "neigh", "oink", "", "", "", "", currency.Code{}, 0, 0, 0, 0, false, false) + _, err = bi.PlaceFuturesOrder(context.Background(), testPair2, "woof", "neigh", "", "", "", "", "", testFiat2, 0, 0, 0, 0, false, false) assert.ErrorIs(t, err, errSideEmpty) - _, err = bi.PlaceFuturesOrder(context.Background(), testPair2, "woof", "neigh", "oink", "quack", "", "", "", currency.Code{}, 0, 0, 0, 0, false, false) + _, err = bi.PlaceFuturesOrder(context.Background(), testPair2, "woof", "neigh", "oink", "", "", "", "", testFiat2, 0, 0, 0, 0, false, false) assert.ErrorIs(t, err, errOrderTypeEmpty) - _, err = bi.PlaceFuturesOrder(context.Background(), testPair2, "woof", "neigh", "oink", "quack", "", "limit", "", currency.Code{}, 0, 0, 0, 0, false, false) + _, err = bi.PlaceFuturesOrder(context.Background(), testPair2, "woof", "neigh", "oink", "", "limit", "", "", testFiat2, 0, 0, 0, 0, false, false) assert.ErrorIs(t, err, errAmountEmpty) - _, err = bi.PlaceFuturesOrder(context.Background(), testPair2, "woof", "neigh", "oink", "quack", "", "limit", "", currency.Code{}, 0, 0, 1, 0, false, false) + _, err = bi.PlaceFuturesOrder(context.Background(), testPair2, "woof", "neigh", "oink", "", "limit", "", "", testFiat2, 0, 0, 1, 0, false, false) assert.ErrorIs(t, err, errLimitPriceEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) resp, err := bi.PlaceFuturesOrder(context.Background(), testPair2, testFiat2.String()+"-FUTURES", "isolated", "buy", "open", "limit", "GTC", clientIDGenerator(), testFiat2, testPrice2+1, testPrice2-1, testAmount2, testPrice2, true, true) @@ -1315,7 +1322,7 @@ func TestBatchPlaceFuturesOrders(t *testing.T) { assert.ErrorIs(t, err, errProductTypeEmpty) _, err = bi.BatchPlaceFuturesOrders(context.Background(), testPair2, "woof", "", currency.Code{}, nil, false) assert.ErrorIs(t, err, errMarginCoinEmpty) - _, err = bi.BatchPlaceFuturesOrders(context.Background(), testPair2, "woof", "neigh", currency.Code{}, nil, false) + _, err = bi.BatchPlaceFuturesOrders(context.Background(), testPair2, "woof", "", testFiat2, nil, false) assert.ErrorIs(t, err, errMarginModeEmpty) _, err = bi.BatchPlaceFuturesOrders(context.Background(), testPair2, "woof", "neigh", testFiat2, nil, false) assert.ErrorIs(t, err, errOrdersEmpty) @@ -1380,7 +1387,7 @@ func TestGetPendingFuturesOrders(t *testing.T) { t.Parallel() _, err := bi.GetPendingFuturesOrders(context.Background(), 0, 0, 0, "", "", "", currency.Pair{}, time.Time{}, time.Time{}) assert.ErrorIs(t, err, errProductTypeEmpty) - _, err = bi.GetPendingFuturesOrders(context.Background(), 0, 0, 0, "", "", "meow", currency.Pair{}, time.Now().Add(time.Hour), time.Time{}) + _, err = bi.GetPendingFuturesOrders(context.Background(), 0, 0, 0, "", "meow", "", currency.Pair{}, time.Now().Add(time.Hour), time.Time{}) assert.ErrorIs(t, err, common.ErrStartAfterTimeNow) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi) resp, err := bi.GetPendingFuturesOrders(context.Background(), 0, 1<<62, 5, "", testFiat2.String()+"-FUTURES", "", testPair2, time.Now().Add(-time.Hour*24*90), time.Now()) @@ -1396,7 +1403,7 @@ func TestGetHistoricalFuturesOrders(t *testing.T) { t.Parallel() _, err := bi.GetHistoricalFuturesOrders(context.Background(), 0, 0, 0, "", "", currency.Pair{}, time.Time{}, time.Time{}) assert.ErrorIs(t, err, errProductTypeEmpty) - _, err = bi.GetHistoricalFuturesOrders(context.Background(), 0, 0, 0, "", "", testPair2, time.Now().Add(time.Hour), time.Time{}) + _, err = bi.GetHistoricalFuturesOrders(context.Background(), 0, 0, 0, "", "meow", currency.Pair{}, time.Now().Add(time.Hour), time.Time{}) assert.ErrorIs(t, err, common.ErrStartAfterTimeNow) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi) resp, err := bi.GetHistoricalFuturesOrders(context.Background(), 0, 1<<62, 5, "", testFiat2.String()+"-FUTURES", testPair2, time.Time{}, time.Time{}) @@ -1438,9 +1445,9 @@ func TestPlaceTPSLFuturesOrder(t *testing.T) { assert.ErrorIs(t, err, errPlanTypeEmpty) _, err = bi.PlaceTPSLFuturesOrder(context.Background(), testFiat2, "woof", "neigh", "", "", "", "", testPair2, 0, 0, 0) assert.ErrorIs(t, err, errHoldSideEmpty) - _, err = bi.PlaceTPSLFuturesOrder(context.Background(), testFiat2, "woof", "neigh", "", "", "quack", "", testPair2, 0, 0, 0) + _, err = bi.PlaceTPSLFuturesOrder(context.Background(), testFiat2, "woof", "neigh", "", "quack", "", "", testPair2, 0, 0, 0) assert.ErrorIs(t, err, errTriggerPriceEmpty) - _, err = bi.PlaceTPSLFuturesOrder(context.Background(), testFiat2, "woof", "neigh", "", "", "quack", "", testPair2, 1, 0, 0) + _, err = bi.PlaceTPSLFuturesOrder(context.Background(), testFiat2, "woof", "neigh", "", "quack", "", "", testPair2, 1, 0, 0) assert.ErrorIs(t, err, errAmountEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) cID := clientIDGenerator() @@ -1467,15 +1474,15 @@ func TestPlaceTriggerFuturesOrder(t *testing.T) { assert.ErrorIs(t, err, errSideEmpty) _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "", "", "", "", testPair2, testFiat2, 0, 0, 0, 0, 0, 0, 0, 0, false) assert.ErrorIs(t, err, errOrderTypeEmpty) - _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "", "", "cluck", "", testPair2, testFiat2, 0, 0, 0, 0, 0, 0, 0, 0, false) + _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "cluck", "", "", "", testPair2, testFiat2, 0, 0, 0, 0, 0, 0, 0, 0, false) assert.ErrorIs(t, err, errAmountEmpty) - _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "", "", "cluck", "", testPair2, testFiat2, 1, 0, 0, 0, 0, 0, 0, 0, false) + _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "cluck", "", "", "", testPair2, testFiat2, 1, 0, 0, 0, 0, 0, 0, 0, false) assert.ErrorIs(t, err, errExecutePriceEmpty) - _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "", "", "cluck", "", testPair2, testFiat2, 1, 1, 0, 0, 0, 0, 0, 0, false) + _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "cluck", "", "", "", testPair2, testFiat2, 1, 1, 0, 0, 0, 0, 0, 0, false) assert.ErrorIs(t, err, errTriggerPriceEmpty) - _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "", "", "cluck", "", testPair2, testFiat2, 1, 1, 0, 1, 1, 0, 0, 0, false) + _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "cluck", "", "", "", testPair2, testFiat2, 1, 1, 0, 1, 1, 0, 0, 0, false) assert.ErrorIs(t, err, errTakeProfitParamsInconsistency) - _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "", "", "cluck", "", testPair2, testFiat2, 1, 1, 0, 1, 0, 0, 1, 0, false) + _, err = bi.PlaceTriggerFuturesOrder(context.Background(), "meow", "woof", "neigh", "oink", "quack", "", "cluck", "", "", "", testPair2, testFiat2, 1, 1, 0, 1, 0, 0, 1, 0, false) assert.ErrorIs(t, err, errStopLossParamsInconsistency) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) // This returns the error "The parameter does not meet the specification d delegateType is error". The documentation doesn't mention that parameter anywhere, nothing seems similar to it, and attempts to send that parameter with various values, or to tweak other parameters, yielded no difference @@ -2368,6 +2375,10 @@ func TestUpdateTicker(t *testing.T) { assert.Error(t, err) _, err = bi.UpdateTicker(context.Background(), testPair, asset.Futures) assert.NoError(t, err) + _, err = bi.UpdateTicker(context.Background(), fakePair, asset.Margin) + assert.Error(t, err) + _, err = bi.UpdateTicker(context.Background(), testPair, asset.Margin) + assert.NoError(t, err) _, err = bi.UpdateTicker(context.Background(), testPair, asset.Empty) assert.ErrorIs(t, err, asset.ErrNotSupported) } @@ -2381,6 +2392,8 @@ func TestUpdateTickers(t *testing.T) { assert.NoError(t, err) err = bi.UpdateTickers(context.Background(), asset.Empty) assert.ErrorIs(t, err, asset.ErrNotSupported) + err = bi.UpdateTickers(context.Background(), asset.Margin) + assert.NoError(t, err) } func TestFetchTicker(t *testing.T) { @@ -2470,7 +2483,9 @@ func TestGetRecentTrades(t *testing.T) { func TestGetHistoricTrades(t *testing.T) { t.Parallel() - _, err := bi.GetHistoricTrades(context.Background(), fakePair, asset.Spot, time.Time{}, time.Time{}) + _, err := bi.GetHistoricTrades(context.Background(), currency.Pair{}, asset.Spot, time.Time{}, time.Time{}) + assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty) + _, err = bi.GetHistoricTrades(context.Background(), fakePair, asset.Spot, time.Time{}, time.Time{}) assert.Error(t, err) _, err = bi.GetHistoricTrades(context.Background(), testPair, asset.Spot, time.Now().Add(-time.Hour*24*7), time.Now()) assert.NoError(t, err) @@ -2625,6 +2640,9 @@ func TestGetActiveOrders(t *testing.T) { assert.NoError(t, err) req.AssetType = asset.Spot _, err = bi.GetActiveOrders(context.Background(), req) + assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty) + req.Pairs = []currency.Pair{} + _, err = bi.GetActiveOrders(context.Background(), req) assert.NoError(t, err) req.Pairs = []currency.Pair{testPair} _, err = bi.GetActiveOrders(context.Background(), req) @@ -2659,7 +2677,9 @@ func TestGetOrderHistory(t *testing.T) { assert.NoError(t, err) req.AssetType = asset.Spot _, err = bi.GetOrderHistory(context.Background(), req) - // This is failing since the String() method on these novel pairs returns them with a delimiter for some reason + assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty) + req.Pairs = []currency.Pair{} + _, err = bi.GetOrderHistory(context.Background(), req) assert.NoError(t, err) req.Pairs = []currency.Pair{testPair} _, err = bi.GetOrderHistory(context.Background(), req) @@ -2997,9 +3017,9 @@ func TestModifyFuturesOrder(t *testing.T) { assert.ErrorIs(t, err, errOrderClientEmpty) _, err = bi.ModifyFuturesOrder(context.Background(), 1, "", "", "", currency.Pair{}, 0, 0, 0, 0) assert.ErrorIs(t, err, errPairEmpty) - _, err = bi.ModifyFuturesOrder(context.Background(), 1, "", "", "", currency.Pair{}, 0, 0, 0, 0) + _, err = bi.ModifyFuturesOrder(context.Background(), 1, "", "", "", currency.NewPairWithDelimiter("meow", "woof", ""), 0, 0, 0, 0) assert.ErrorIs(t, err, errProductTypeEmpty) - _, err = bi.ModifyFuturesOrder(context.Background(), 1, "", "meow", "", currency.Pair{}, 0, 0, 0, 0) + _, err = bi.ModifyFuturesOrder(context.Background(), 1, "", "meow", "", currency.NewPairWithDelimiter("meow", "woof", ""), 0, 0, 0, 0) assert.ErrorIs(t, err, errNewClientOrderIDEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) oID := getFuturesOrdIDHelper(t, true, true) @@ -3212,20 +3232,26 @@ func TestBatchCancelIsolatedOrders(t *testing.T) { } func TestWsAuth(t *testing.T) { - t.Parallel() + // t.Parallel() if bi.Websocket.IsEnabled() && !bi.API.AuthenticatedWebsocketSupport || !sharedtestvalues.AreAPICredentialsSet(bi) { t.Skip(stream.ErrWebsocketNotEnabled.Error()) } var dialer websocket.Dialer + go func() { + timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout) + select { + case resp := <-bi.Websocket.DataHandler: + t.Errorf("%+v\n%T\n", resp, resp) + case <-timer.C: + } + timer.Stop() + for { + <-bi.Websocket.DataHandler + } + }() err := bi.WsAuth(context.TODO(), &dialer) require.NoError(t, err) - timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout) - select { - case resp := <-bi.Websocket.DataHandler: - fmt.Printf("%+v\n%T\n", resp, resp) - case <-timer.C: - } - timer.Stop() + time.Sleep(sharedtestvalues.WebsocketResponseDefaultTimeout) } func TestWsReadData(t *testing.T) { @@ -3235,22 +3261,20 @@ func TestWsReadData(t *testing.T) { } func TestWsHandleData(t *testing.T) { - done := make(chan struct{}) + ch := make(chan struct{}) t.Cleanup(func() { - close(done) + close(ch) }) go func() { for { select { case <-bi.Websocket.DataHandler: continue - case <-done: + case <-ch: return } } }() - oldVerbose := bi.Verbose - bi.Verbose = true mockJSON := []byte(`pong`) err := bi.wsHandleData(mockJSON) assert.NoError(t, err) @@ -3531,7 +3555,6 @@ func TestWsHandleData(t *testing.T) { mockJSON = []byte(`{"event":"fakeEventNotReal"}`) err = bi.wsHandleData(mockJSON) assert.NoError(t, err) - bi.Verbose = oldVerbose } func TestCalculateUpdateOrderbookChecksum(t *testing.T) { @@ -3786,17 +3809,37 @@ func aBenchmarkHelper(a, pag int64) { } // irrelevant/outdated data retained for formatting -// 139 9192238 ns/op 271904 B/op 2038 allocs/op +// 763 1819054 ns/op 14336 B/op 1 allocs/op +// 87 13602672 ns/op 0 B/op 0 allocs/op func BenchmarkGen(b *testing.B) { - // var resp WsResponse - // err := json.Unmarshal(data, &resp) - // if err != nil { - // b.Fatal(err) - // } - // for i := 0; i < b.N; i++ { - // err = bi.orderbookDataHandler(resp) - // if err != nil { - // b.Fatal(err) + pairs, err := bi.GetSupportedCurrencies(context.Background()) + if err != nil { + panic(err) + } + check, err := bi.GetSymbolInfo(context.Background(), currency.Pair{}) + if err != nil { + panic(err) + } + b.ResetTimer() + // for j := 0; j < b.N; j++ { + // checkSlice := make([]string, len(check)) + // for i := range check { + // checkSlice[i] = check[i].Symbol + // } + // for x := range pairs { + // if !slices.Contains(checkSlice, pairs[x].Symbol) { + // continue + // } // } // } + + for j := 0; j < b.N; j++ { + for x := range pairs { + if !slices.ContainsFunc(check, func(s SymbolInfoResp) bool { + return s.Symbol == pairs[x].Symbol + }) { + continue + } + } + } } diff --git a/exchanges/bitget/bitget_wrapper.go b/exchanges/bitget/bitget_wrapper.go index 3d4eb593a82..a81d16e9549 100644 --- a/exchanges/bitget/bitget_wrapper.go +++ b/exchanges/bitget/bitget_wrapper.go @@ -3,6 +3,8 @@ package bitget import ( "context" "fmt" + "math" + "slices" "strconv" "strings" "time" @@ -276,38 +278,70 @@ func (bi *Bitget) UpdateTicker(ctx context.Context, p currency.Pair, assetType a return nil, err } switch assetType { - case asset.Spot, asset.Margin, asset.CrossMargin: + case asset.Spot: tick, err := bi.GetSpotTickerInformation(ctx, p) if err != nil { return nil, err } + if len(tick) == 0 { + return nil, errReturnEmpty + } tickerPrice = &ticker.Price{ - High: tick[0].High24H, - Low: tick[0].Low24H, - Bid: tick[0].BidPrice, - Ask: tick[0].AskPrice, - Volume: tick[0].BaseVolume, - QuoteVolume: tick[0].QuoteVolume, - Open: tick[0].Open, - Close: tick[0].LastPrice, - LastUpdated: tick[0].Timestamp.Time(), + High: tick[0].High24H, + Low: tick[0].Low24H, + Bid: tick[0].BidPrice, + Ask: tick[0].AskPrice, + Volume: tick[0].BaseVolume, + QuoteVolume: tick[0].QuoteVolume, + Open: tick[0].Open, + Close: tick[0].LastPrice, + LastUpdated: tick[0].Timestamp.Time(), + ExchangeName: bi.Name, + AssetType: assetType, + Pair: p, } case asset.Futures: tick, err := bi.GetFuturesTicker(ctx, p, getProductType(p)) if err != nil { return nil, err } + if len(tick) == 0 { + return nil, errReturnEmpty + } + tickerPrice = &ticker.Price{ + High: tick[0].High24H, + Low: tick[0].Low24H, + Bid: tick[0].BidPrice, + Ask: tick[0].AskPrice, + Volume: tick[0].BaseVolume, + QuoteVolume: tick[0].QuoteVolume, + Open: tick[0].Open24H, + Close: tick[0].LastPrice, + IndexPrice: tick[0].IndexPrice, + LastUpdated: tick[0].Timestamp.Time(), + ExchangeName: bi.Name, + AssetType: assetType, + Pair: p, + } + case asset.Margin, asset.CrossMargin: + tick, err := bi.GetSpotCandlestickData(ctx, p, formatExchangeKlineIntervalSpot(kline.OneDay), time.Now().Add(-time.Hour*24), time.Now(), 2, false) + if err != nil { + return nil, err + } + if len(tick.SpotCandles) == 0 { + return nil, errReturnEmpty + } tickerPrice = &ticker.Price{ - High: tick[0].High24H, - Low: tick[0].Low24H, - Bid: tick[0].BidPrice, - Ask: tick[0].AskPrice, - Volume: tick[0].BaseVolume, - QuoteVolume: tick[0].QuoteVolume, - Open: tick[0].Open24H, - Close: tick[0].LastPrice, - IndexPrice: tick[0].IndexPrice, - LastUpdated: tick[0].Timestamp.Time(), + High: tick.SpotCandles[0].High, + Low: tick.SpotCandles[0].Low, + Volume: tick.SpotCandles[0].BaseVolume, + QuoteVolume: tick.SpotCandles[0].QuoteVolume, + Open: tick.SpotCandles[0].Open, + Close: tick.SpotCandles[0].Close, + LastUpdated: tick.SpotCandles[0].Timestamp, + ExchangeName: bi.Name, + AssetType: assetType, + Pair: p, } default: return nil, asset.ErrNotSupported @@ -325,7 +359,7 @@ func (bi *Bitget) UpdateTicker(ctx context.Context, p currency.Pair, assetType a // UpdateTickers updates all currency pairs of a given asset type func (bi *Bitget) UpdateTickers(ctx context.Context, assetType asset.Item) error { switch assetType { - case asset.Spot, asset.Margin, asset.CrossMargin: + case asset.Spot: tick, err := bi.GetSpotTickerInformation(ctx, currency.Pair{}) if err != nil { return err @@ -384,6 +418,54 @@ func (bi *Bitget) UpdateTickers(ctx context.Context, assetType asset.Item) error } } } + case asset.Margin, asset.CrossMargin: + pairs, err := bi.GetSupportedCurrencies(ctx) + if err != nil { + return err + } + check, err := bi.GetSymbolInfo(ctx, currency.Pair{}) + if err != nil { + return err + } + checkSlice := make([]string, len(check)) + for i := range check { + checkSlice[i] = check[i].Symbol + } + for x := range pairs { + if !slices.Contains(checkSlice, pairs[x].Symbol) { + continue + } + p, err := bi.MatchSymbolWithAvailablePairs(pairs[x].Symbol, assetType, false) + if err != nil { + return err + } + p, err = bi.FormatExchangeCurrency(p, assetType) + if err != nil { + return err + } + resp, err := bi.GetSpotCandlestickData(ctx, p, formatExchangeKlineIntervalSpot(kline.OneDay), time.Now().Add(-time.Hour*24), time.Now(), 2, false) + if err != nil { + return err + } + if len(resp.SpotCandles) == 0 { + return errReturnEmpty + } + err = ticker.ProcessTicker(&ticker.Price{ + High: resp.SpotCandles[0].High, + Low: resp.SpotCandles[0].Low, + Volume: resp.SpotCandles[0].BaseVolume, + QuoteVolume: resp.SpotCandles[0].QuoteVolume, + Open: resp.SpotCandles[0].Open, + Close: resp.SpotCandles[0].Close, + LastUpdated: resp.SpotCandles[0].Timestamp, + Pair: p, + ExchangeName: bi.Name, + AssetType: assetType, + }) + if err != nil { + return err + } + } default: return asset.ErrNotSupported } @@ -698,6 +780,10 @@ func (bi *Bitget) GetRecentTrades(ctx context.Context, p currency.Pair, assetTyp // GetHistoricTrades returns historic trade data within the timeframe provided func (bi *Bitget) GetHistoricTrades(ctx context.Context, p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]trade.Data, error) { // This exchange only allows requests covering the last 7 days + p, err := bi.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } switch assetType { case asset.Spot, asset.Margin, asset.CrossMargin: resp, err := bi.GetSpotMarketTrades(ctx, p, timestampStart, timestampEnd, 1000, 0) @@ -766,7 +852,7 @@ func (bi *Bitget) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Subm case asset.Spot: IDs, err = bi.PlaceSpotOrder(ctx, s.Pair, s.Side.String(), s.Type.Lower(), strat, cID.String(), "", s.Price, s.Amount, s.TriggerPrice, 0, 0, 0, 0, false, 0) case asset.Futures: - IDs, err = bi.PlaceFuturesOrder(ctx, s.Pair, getProductType(s.Pair), marginStringer(s.MarginType), s.Pair.Quote.String(), sideEncoder(s.Side, false), s.Type.Lower(), strat, cID.String(), currency.Code{}, 0, 0, s.Amount, s.Price, s.ReduceOnly, false) + IDs, err = bi.PlaceFuturesOrder(ctx, s.Pair, getProductType(s.Pair), marginStringer(s.MarginType), sideEncoder(s.Side, false), "", s.Type.Lower(), strat, cID.String(), s.Pair.Quote, 0, 0, s.Amount, s.Price, s.ReduceOnly, false) case asset.Margin, asset.CrossMargin: loanType := "normal" if s.AutoBorrow { @@ -1037,13 +1123,13 @@ func (bi *Bitget) GetOrderInfo(ctx context.Context, orderID string, pair currenc var ordInfo *MarginOpenOrds var fillInfo *MarginOrderFills if assetType == asset.Margin { - ordInfo, err = bi.GetIsolatedOpenOrders(ctx, pair, "", ordID, 2, 0, time.Time{}, time.Time{}) + ordInfo, err = bi.GetIsolatedOpenOrders(ctx, pair, "", ordID, 2, 0, time.Now().Add(-time.Hour*24*90), time.Now()) if err != nil { return nil, err } fillInfo, err = bi.GetIsolatedOrderFills(ctx, pair, ordID, 0, 500, time.Now().Add(-time.Hour*24*90), time.Now()) } else { - ordInfo, err = bi.GetCrossOpenOrders(ctx, pair, "", ordID, 2, 0, time.Time{}, time.Time{}) + ordInfo, err = bi.GetCrossOpenOrders(ctx, pair, "", ordID, 2, 0, time.Now().Add(-time.Hour*24*90), time.Now()) if err != nil { return nil, err } @@ -1134,7 +1220,10 @@ func (bi *Bitget) GetActiveOrders(ctx context.Context, getOrdersRequest *order.M return nil, err } for x := range getOrdersRequest.Pairs { - getOrdersRequest.Pairs[x], err = bi.FormatExchangeCurrency(getOrdersRequest.Pairs[x], asset.Spot) + getOrdersRequest.Pairs[x], err = bi.FormatExchangeCurrency(getOrdersRequest.Pairs[x], getOrdersRequest.AssetType) + if err != nil { + return nil, err + } } if len(getOrdersRequest.Pairs) == 0 { getOrdersRequest.Pairs = append(getOrdersRequest.Pairs, currency.Pair{}) @@ -1270,8 +1359,7 @@ func (bi *Bitget) GetActiveOrders(ctx context.Context, getOrdersRequest *order.M return resp, nil } -// GetOrderHistory retrieves account order information -// Can Limit response to specific order status +// GetOrderHistory retrieves account order information. Can Limit response to specific order status func (bi *Bitget) GetOrderHistory(ctx context.Context, getOrdersRequest *order.MultiOrderRequest) (order.FilteredOrders, error) { err := getOrdersRequest.Validate() if err != nil { @@ -1279,6 +1367,9 @@ func (bi *Bitget) GetOrderHistory(ctx context.Context, getOrdersRequest *order.M } for x := range getOrdersRequest.Pairs { getOrdersRequest.Pairs[x], err = bi.FormatExchangeCurrency(getOrdersRequest.Pairs[x], asset.Spot) + if err != nil { + return nil, err + } } if len(getOrdersRequest.Pairs) == 0 { getOrdersRequest.Pairs = append(getOrdersRequest.Pairs, currency.Pair{}) @@ -1657,9 +1748,9 @@ func (bi *Bitget) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) limits[i] = order.MinMaxLevel{ Asset: a, Pair: currency.NewPair(currency.NewCode(resp[i].BaseCoin), currency.NewCode(resp[i].QuoteCoin)), - PriceStepIncrementSize: float64(resp[i].PricePrecision), - AmountStepIncrementSize: float64(resp[i].QuantityPrecision), - QuoteStepIncrementSize: float64(resp[i].QuotePrecision), + PriceStepIncrementSize: math.Pow10(-int(resp[i].PricePrecision)), + AmountStepIncrementSize: math.Pow10(-int(resp[i].QuantityPrecision)), + QuoteStepIncrementSize: math.Pow10(-int(resp[i].QuotePrecision)), MinNotional: resp[i].MinTradeUSDT, MarketMinQty: resp[i].MinTradeAmount, MarketMaxQty: resp[i].MaxTradeAmount, @@ -1695,8 +1786,8 @@ func (bi *Bitget) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) MinNotional: resp[i].MinTradeUSDT, MarketMinQty: resp[i].MinTradeAmount, MarketMaxQty: resp[i].MaxTradeAmount, - QuoteStepIncrementSize: float64(resp[i].PricePrecision), - AmountStepIncrementSize: float64(resp[i].QuantityPrecision), + QuoteStepIncrementSize: math.Pow10(-int(resp[i].PricePrecision)), + AmountStepIncrementSize: math.Pow10(-int(resp[i].QuantityPrecision)), } } default: @@ -1736,8 +1827,7 @@ func (bi *Bitget) UpdateCurrencyStates(ctx context.Context, a asset.Item) error return bi.States.UpdateAll(a, payload) } -// GetAvailableTransferChains returns a list of supported transfer chains based -// on the supplied cryptocurrency +// GetAvailableTransferChains returns a list of supported transfer chains based on the supplied cryptocurrency func (bi *Bitget) GetAvailableTransferChains(ctx context.Context, cur currency.Code) ([]string, error) { if cur.IsEmpty() { return nil, errCurrencyEmpty @@ -2236,8 +2326,7 @@ func (bi *Bitget) withdrawalHistGrabber(ctx context.Context, currency currency.C return allData, nil } -// PairFromStringHelper is a helper function that does some checks to help with common ambiguous cases in this -// exchange +// PairFromStringHelper is a helper function that does some checks to help with common ambiguous cases in this exchange func pairFromStringHelper(s string) (currency.Pair, error) { pair := currency.Pair{} i := strings.Index(s, "USD") @@ -2304,8 +2393,7 @@ func marginDecoder(s string) margin.Type { return margin.Unknown } -// ActiveFuturesOrderHelper is a helper function that repeatedly calls GetPendingFuturesOrders and -// GetPendingFuturesTriggerOrders, returning the data formatted appropriately +// ActiveFuturesOrderHelper is a helper function that repeatedly calls GetPendingFuturesOrders and GetPendingFuturesTriggerOrders, returning the data formatted appropriately func (bi *Bitget) activeFuturesOrderHelper(ctx context.Context, productType string, pairCan currency.Pair, resp []order.Detail) ([]order.Detail, error) { var pagination int64 for { @@ -2398,8 +2486,7 @@ func (bi *Bitget) activeFuturesOrderHelper(ctx context.Context, productType stri return resp, nil } -// SpotHistoricPlanOrdersHelper is a helper function that repeatedly calls GetHistoricalSpotOrders and returns -// all data formatted appropriately +// SpotHistoricPlanOrdersHelper is a helper function that repeatedly calls GetHistoricalSpotOrders and returns all data formatted appropriately func (bi *Bitget) spotHistoricPlanOrdersHelper(ctx context.Context, pairCan currency.Pair, resp []order.Detail, fillMap map[int64][]order.TradeHistory) ([]order.Detail, error) { var pagination int64 for { @@ -2440,8 +2527,7 @@ func (bi *Bitget) spotHistoricPlanOrdersHelper(ctx context.Context, pairCan curr return resp, nil } -// HistoricalFuturesOrderHelper is a helper function that repeatedly calls GetFuturesFills, -// GetHistoricalFuturesOrders, and GetHistoricalTriggerFuturesOrders, returning the data formatted appropriately +// HistoricalFuturesOrderHelper is a helper function that repeatedly calls GetFuturesFills, GetHistoricalFuturesOrders, and GetHistoricalTriggerFuturesOrders, returning the data formatted appropriately func (bi *Bitget) historicalFuturesOrderHelper(ctx context.Context, productType string, pairCan currency.Pair, resp []order.Detail) ([]order.Detail, error) { var pagination int64 fillMap := make(map[int64][]order.TradeHistory) @@ -2596,8 +2682,7 @@ func (bi *Bitget) spotFillsHelper(ctx context.Context, pair currency.Pair, fillM return nil } -// FormatExchangeKlineIntervalSpot is a helper function used to convert kline.Interval to the string format -// required by the spot API +// FormatExchangeKlineIntervalSpot is a helper function used to convert kline.Interval to the string format required by the spot API func formatExchangeKlineIntervalSpot(interval kline.Interval) string { switch interval { case kline.OneMin: @@ -2628,8 +2713,7 @@ func formatExchangeKlineIntervalSpot(interval kline.Interval) string { return errIntervalNotSupported } -// FormatExchangeKlineIntervalFutures is a helper function used to convert kline.Interval to the string format -// required by the futures API +// FormatExchangeKlineIntervalFutures is a helper function used to convert kline.Interval to the string format required by the futures API func formatExchangeKlineIntervalFutures(interval kline.Interval) string { switch interval { case kline.OneMin: @@ -2690,8 +2774,7 @@ func contractTypeDecoder(s string) futures.ContractType { return futures.Unknown } -// AllFuturesOrderHelper is a helper function that repeatedly calls GetPendingFuturesOrders and -// GetPendingFuturesTriggerOrders, returning the data formatted appropriately +// AllFuturesOrderHelper is a helper function that repeatedly calls GetPendingFuturesOrders and GetPendingFuturesTriggerOrders, returning the data formatted appropriately func (bi *Bitget) allFuturesOrderHelper(ctx context.Context, productType string, pairCan currency.Pair, resp []futures.PositionResponse) ([]futures.PositionResponse, error) { var pagination1 int64 var pagination2 int64