exchanges/wrappers: Refactor fetch orderbook/ticker/account info funcs (#1440)

* acrost: Pull thread, examine

* fix tests

* linter

* fix_linter

* revert rm ctx param to limit breakages when merging usptream

* linter fix

* Add in priority update grouping so that tests pass

* Update cmd/exchange_wrapper_standards/exchange_wrapper_standards_test.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious nits

* fixed spelling

* whoopsie

* aanother whoops

* glorious: NITTERS!

* glorious: further nitters

* srry linter gods

* glorious: nits continued

* sub test p ara lel

* drop main t.Parallel

* fix whoops

* wrappertests: use context with cancel (test)

* linter: fix

* ensure primary execution

* kucoin test fix

* revert standards test changes and bypass non critical errors

* rm single override

* wrap exported error for accounts

* thrasher: nits ch name

* gk: nits

* gk: nits FetchTickerCached -> GetCachedTicker

* gk: nits rn FetchOrderbookCached -> GetCachedOrderbook

* gk: nits rn FetchAccountInfoCached -> GetCachedAccountInfo

* linter: fix

* gk: nits

* thrasher: nitters 1

* thrasher: nitters tmpls

* gk: nitter

---------

Co-authored-by: shazbert <ryan.oharareid@thrasher.io>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
This commit is contained in:
Ryan O'Hara-Reid
2025-02-19 10:47:10 +11:00
committed by GitHub
parent 2fc7e8e3e3
commit 08e015a125
122 changed files with 545 additions and 1962 deletions

View File

@@ -18,11 +18,14 @@ func init() {
service.mux = dispatch.GetNewMux(nil)
}
// Public errors
var (
ErrExchangeHoldingsNotFound = errors.New("exchange holdings not found")
)
var (
errHoldingsIsNil = errors.New("holdings cannot be nil")
errExchangeNameUnset = errors.New("exchange name unset")
errExchangeHoldingsNotFound = errors.New("exchange holdings not found")
errAssetHoldingsNotFound = errors.New("asset holdings not found")
errExchangeAccountsNotFound = errors.New("exchange accounts not found")
errNoExchangeSubAccountBalances = errors.New("no exchange sub account balances")
errBalanceIsNil = errors.New("balance is nil")
@@ -92,16 +95,12 @@ func GetHoldings(exch string, creds *Credentials, assetType asset.Item) (Holding
defer service.mu.Unlock()
accounts, ok := service.exchangeAccounts[exch]
if !ok {
return Holdings{}, fmt.Errorf("%s %s %w", exch, assetType, errExchangeHoldingsNotFound)
return Holdings{}, fmt.Errorf("%s %w: `%s`", exch, ErrExchangeHoldingsNotFound, assetType)
}
subAccountHoldings, ok := accounts.SubAccounts[*creds]
if !ok {
return Holdings{}, fmt.Errorf("%s %s %s %w",
exch,
creds,
assetType,
errNoCredentialBalances)
return Holdings{}, fmt.Errorf("%s %s %s %w %w", exch, creds, assetType, errNoCredentialBalances, ErrExchangeHoldingsNotFound)
}
var currencyBalances = make([]Balance, 0, len(subAccountHoldings))
@@ -127,10 +126,7 @@ func GetHoldings(exch string, creds *Credentials, assetType asset.Item) (Holding
}
}
if len(currencyBalances) == 0 {
return Holdings{}, fmt.Errorf("%s %s %w",
exch,
assetType,
errAssetHoldingsNotFound)
return Holdings{}, fmt.Errorf("%s %s %w", exch, assetType, ErrExchangeHoldingsNotFound)
}
return Holdings{Exchange: exch, Accounts: []SubAccount{{
Credentials: Protected{creds: cpy},
@@ -164,7 +160,7 @@ func GetBalance(exch, subAccount string, creds *Credentials, ai asset.Item, c cu
accounts, ok := service.exchangeAccounts[exch]
if !ok {
return nil, fmt.Errorf("%s %w", exch, errExchangeHoldingsNotFound)
return nil, fmt.Errorf("%s %w", exch, ErrExchangeHoldingsNotFound)
}
subAccounts, ok := accounts.SubAccounts[*creds]

View File

@@ -6,6 +6,8 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/thrasher-corp/gocryptotrader/common/key"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/dispatch"
@@ -63,32 +65,20 @@ func TestCollectBalances(t *testing.T) {
func TestGetHoldings(t *testing.T) {
err := dispatch.Start(dispatch.DefaultMaxWorkers, dispatch.DefaultJobsLimit)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
err = Process(nil, nil)
if !errors.Is(err, errHoldingsIsNil) {
t.Fatalf("received: '%v' but expected: '%v'", err, errHoldingsIsNil)
}
assert.ErrorIs(t, err, errHoldingsIsNil)
err = Process(&Holdings{}, nil)
if !errors.Is(err, errExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchangeNameUnset)
}
assert.ErrorIs(t, err, errExchangeNameUnset)
holdings := Holdings{
Exchange: "Test",
}
holdings := Holdings{Exchange: "Test"}
err = Process(&holdings, nil)
if !errors.Is(err, errCredentialsAreNil) {
t.Fatalf("received: '%v' but expected: '%v'", err, errCredentialsAreNil)
}
assert.ErrorIs(t, err, errCredentialsAreNil)
err = Process(&holdings, happyCredentials)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
require.NoError(t, err)
err = Process(&Holdings{
Exchange: "Test",
@@ -97,9 +87,7 @@ func TestGetHoldings(t *testing.T) {
ID: "1337",
}},
}, happyCredentials)
if !errors.Is(err, asset.ErrNotSupported) {
t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported)
}
assert.ErrorIs(t, err, asset.ErrNotSupported)
err = Process(&Holdings{
Exchange: "Test",
@@ -120,9 +108,7 @@ func TestGetHoldings(t *testing.T) {
},
}},
}, happyCredentials)
if err != nil {
t.Error(err)
}
assert.NoError(t, err)
// process again with no changes
err = Process(&Holdings{
@@ -140,73 +126,43 @@ func TestGetHoldings(t *testing.T) {
},
}},
}, happyCredentials)
if err != nil {
t.Error(err)
}
assert.NoError(t, err)
_, err = GetHoldings("", nil, asset.Spot)
if !errors.Is(err, errExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchangeNameUnset)
}
assert.ErrorIs(t, err, errExchangeNameUnset)
_, err = GetHoldings("bla", nil, asset.Spot)
if !errors.Is(err, errCredentialsAreNil) {
t.Fatalf("received: '%v' but expected: '%v'", err, errCredentialsAreNil)
}
assert.ErrorIs(t, err, errCredentialsAreNil)
_, err = GetHoldings("bla", happyCredentials, asset.Spot)
if !errors.Is(err, errExchangeHoldingsNotFound) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchangeHoldingsNotFound)
}
assert.ErrorIs(t, err, ErrExchangeHoldingsNotFound)
_, err = GetHoldings("bla", happyCredentials, asset.Empty)
if !errors.Is(err, asset.ErrNotSupported) {
t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported)
}
assert.ErrorIs(t, err, asset.ErrNotSupported)
_, err = GetHoldings("Test", happyCredentials, asset.UpsideProfitContract)
if !errors.Is(err, errAssetHoldingsNotFound) {
t.Fatalf("received: '%v' but expected: '%v'", err, errAssetHoldingsNotFound)
}
assert.ErrorIs(t, err, ErrExchangeHoldingsNotFound)
_, err = GetHoldings("Test", &Credentials{Key: "BBBBB"}, asset.Spot)
if !errors.Is(err, errNoCredentialBalances) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNoCredentialBalances)
}
assert.ErrorIs(t, err, errNoCredentialBalances)
u, err := GetHoldings("Test", happyCredentials, asset.Spot)
if err != nil {
t.Error(err)
}
require.NoError(t, err)
if u.Accounts[0].ID != "1337" {
t.Errorf("expecting 1337 but received %s", u.Accounts[0].ID)
}
if !u.Accounts[0].Currencies[0].Currency.Equal(currency.BTC) {
t.Errorf("expecting BTC but received %s",
u.Accounts[0].Currencies[0].Currency)
}
if u.Accounts[0].Currencies[0].Total != 100 {
t.Errorf("expecting 100 but received %f",
u.Accounts[0].Currencies[0].Total)
}
if u.Accounts[0].Currencies[0].Hold != 20 {
t.Errorf("expecting 20 but received %f",
u.Accounts[0].Currencies[0].Hold)
}
assert.Equal(t, "test", u.Exchange)
require.Len(t, u.Accounts, 1)
assert.Equal(t, "1337", u.Accounts[0].ID)
assert.Equal(t, asset.Spot, u.Accounts[0].AssetType)
require.Len(t, u.Accounts[0].Currencies, 1)
assert.Equal(t, currency.BTC, u.Accounts[0].Currencies[0].Currency)
assert.Equal(t, 100.0, u.Accounts[0].Currencies[0].Total)
assert.Equal(t, 20.0, u.Accounts[0].Currencies[0].Hold)
_, err = SubscribeToExchangeAccount("nonsense")
if !errors.Is(err, errExchangeAccountsNotFound) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchangeAccountsNotFound)
}
assert.ErrorIs(t, err, errExchangeAccountsNotFound)
p, err := SubscribeToExchangeAccount("Test")
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
var wg sync.WaitGroup
wg.Add(1)
@@ -218,7 +174,6 @@ func TestGetHoldings(t *testing.T) {
case <-c.C:
}
}
wg.Done()
}(p, &wg)
@@ -236,38 +191,28 @@ func TestGetHoldings(t *testing.T) {
},
}},
}, happyCredentials)
if err != nil {
t.Error(err)
}
assert.NoError(t, err)
wg.Wait()
}
func TestGetBalance(t *testing.T) {
t.Parallel()
_, err := GetBalance("", "", nil, asset.Empty, currency.Code{})
if !errors.Is(err, errExchangeNameUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchangeNameUnset)
}
assert.ErrorIs(t, err, errExchangeNameUnset)
_, err = GetBalance("bruh", "", nil, asset.Empty, currency.Code{})
if !errors.Is(err, asset.ErrNotSupported) {
t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported)
}
assert.ErrorIs(t, err, asset.ErrNotSupported)
_, err = GetBalance("bruh", "", nil, asset.Spot, currency.Code{})
if !errors.Is(err, errCredentialsAreNil) {
t.Fatalf("received: '%v' but expected: '%v'", err, errCredentialsAreNil)
}
assert.ErrorIs(t, err, errCredentialsAreNil)
_, err = GetBalance("bruh", "", happyCredentials, asset.Spot, currency.Code{})
if !errors.Is(err, currency.ErrCurrencyCodeEmpty) {
t.Fatalf("received: '%v' but expected: '%v'", err, currency.ErrCurrencyCodeEmpty)
}
assert.ErrorIs(t, err, currency.ErrCurrencyCodeEmpty)
_, err = GetBalance("bruh", "", happyCredentials, asset.Spot, currency.BTC)
if !errors.Is(err, errExchangeHoldingsNotFound) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchangeHoldingsNotFound)
}
assert.ErrorIs(t, err, ErrExchangeHoldingsNotFound)
err = Process(&Holdings{
Exchange: "bruh",
@@ -278,24 +223,16 @@ func TestGetBalance(t *testing.T) {
},
},
}, happyCredentials)
if err != nil {
t.Error(err)
}
require.NoError(t, err, "process must not error")
_, err = GetBalance("bruh", "1336", &Credentials{Key: "BBBBB"}, asset.Spot, currency.BTC)
if !errors.Is(err, errNoCredentialBalances) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNoCredentialBalances)
}
assert.ErrorIs(t, err, errNoCredentialBalances)
_, err = GetBalance("bruh", "1336", happyCredentials, asset.Spot, currency.BTC)
if !errors.Is(err, errNoExchangeSubAccountBalances) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNoExchangeSubAccountBalances)
}
assert.ErrorIs(t, err, errNoExchangeSubAccountBalances)
_, err = GetBalance("bruh", "1337", happyCredentials, asset.Futures, currency.BTC)
if !errors.Is(err, errNoExchangeSubAccountBalances) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNoExchangeSubAccountBalances)
}
assert.ErrorIs(t, err, errNoExchangeSubAccountBalances)
err = Process(&Holdings{
Exchange: "bruh",
@@ -313,22 +250,15 @@ func TestGetBalance(t *testing.T) {
},
},
}, happyCredentials)
if err != nil {
t.Error(err)
}
require.NoError(t, err, "process must not error")
bal, err := GetBalance("bruh", "1337", happyCredentials, asset.Spot, currency.BTC)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
require.NoError(t, err, "get balance must not error")
bal.m.Lock()
if bal.total != 2 {
t.Fatal("unexpected value")
}
if bal.hold != 1 {
t.Fatal("unexpected value")
}
assert.Equal(t, 2.0, bal.total)
assert.Equal(t, 1.0, bal.hold)
bal.m.Unlock()
}
func TestBalanceInternalWait(t *testing.T) {