mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
accounts: Move to instance methods, fix races and isolate tests (#1923)
* Bybit: Fix race in TestUpdateAccountInfo and TestWSHandleData * DriveBy rename TestWSHandleData * This doesn't address running with -race=2+ due to the singleton * Accounts: Add account.GetService() * exchange: Assertify TestSetupDefaults * Exchanges: Add account.Service override for testing * Exchanges: Remove duplicate IsWebsocketEnabled test from TestSetupDefaults * Dispatch: Replace nil checks with NilGuard * Engine: Remove deprecated printAccountHoldingsChangeSummary * Dispatcher: Add EnsureRunning method * Accounts: Move singleton accounts service to exchange Accounts * Move singleton accounts service to exchange Accounts This maintains the concept of a global store, whilst allowing exchanges to override it when needed, particularly for testing. APIServer: * Remove getAllActiveAccounts from apiserver Deprecated apiserver only thing using this, so remove it instead of updating it * Update comment for UpdateAccountBalances everywhere * Docs: Add punctuation to function comments * Bybit: Coverage for wsProcessWalletPushData Save
This commit is contained in:
@@ -16,33 +16,33 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
orderbookFunc = "orderbook"
|
||||
tickerFunc = "ticker"
|
||||
exchangesFunc = "exchanges"
|
||||
pairsFunc = "pairs"
|
||||
accountInfoFunc = "accountinfo"
|
||||
depositAddressFunc = "depositaddress"
|
||||
orderQueryFunc = "orderquery"
|
||||
orderCancelFunc = "ordercancel"
|
||||
orderSubmitFunc = "ordersubmit"
|
||||
withdrawCryptoFunc = "withdrawcrypto"
|
||||
withdrawFiatFunc = "withdrawfiat"
|
||||
ohlcvFunc = "ohlcv"
|
||||
orderbookFunc = "orderbook"
|
||||
tickerFunc = "ticker"
|
||||
exchangesFunc = "exchanges"
|
||||
pairsFunc = "pairs"
|
||||
accountBalancesFunc = "accountbalances"
|
||||
depositAddressFunc = "depositaddress"
|
||||
orderQueryFunc = "orderquery"
|
||||
orderCancelFunc = "ordercancel"
|
||||
orderSubmitFunc = "ordersubmit"
|
||||
withdrawCryptoFunc = "withdrawcrypto"
|
||||
withdrawFiatFunc = "withdrawfiat"
|
||||
ohlcvFunc = "ohlcv"
|
||||
)
|
||||
|
||||
var exchangeModule = map[string]objects.Object{
|
||||
orderbookFunc: &objects.UserFunction{Name: orderbookFunc, Value: ExchangeOrderbook},
|
||||
tickerFunc: &objects.UserFunction{Name: tickerFunc, Value: ExchangeTicker},
|
||||
exchangesFunc: &objects.UserFunction{Name: exchangesFunc, Value: ExchangeExchanges},
|
||||
pairsFunc: &objects.UserFunction{Name: pairsFunc, Value: ExchangePairs},
|
||||
accountInfoFunc: &objects.UserFunction{Name: accountInfoFunc, Value: ExchangeAccountInfo},
|
||||
depositAddressFunc: &objects.UserFunction{Name: depositAddressFunc, Value: ExchangeDepositAddress},
|
||||
orderQueryFunc: &objects.UserFunction{Name: orderQueryFunc, Value: ExchangeOrderQuery},
|
||||
orderCancelFunc: &objects.UserFunction{Name: orderCancelFunc, Value: ExchangeOrderCancel},
|
||||
orderSubmitFunc: &objects.UserFunction{Name: orderSubmitFunc, Value: ExchangeOrderSubmit},
|
||||
withdrawCryptoFunc: &objects.UserFunction{Name: withdrawCryptoFunc, Value: ExchangeWithdrawCrypto},
|
||||
withdrawFiatFunc: &objects.UserFunction{Name: withdrawFiatFunc, Value: ExchangeWithdrawFiat},
|
||||
ohlcvFunc: &objects.UserFunction{Name: ohlcvFunc, Value: exchangeOHLCV},
|
||||
orderbookFunc: &objects.UserFunction{Name: orderbookFunc, Value: ExchangeOrderbook},
|
||||
tickerFunc: &objects.UserFunction{Name: tickerFunc, Value: ExchangeTicker},
|
||||
exchangesFunc: &objects.UserFunction{Name: exchangesFunc, Value: ExchangeExchanges},
|
||||
pairsFunc: &objects.UserFunction{Name: pairsFunc, Value: ExchangePairs},
|
||||
accountBalancesFunc: &objects.UserFunction{Name: accountBalancesFunc, Value: ExchangeAccountBalances},
|
||||
depositAddressFunc: &objects.UserFunction{Name: depositAddressFunc, Value: ExchangeDepositAddress},
|
||||
orderQueryFunc: &objects.UserFunction{Name: orderQueryFunc, Value: ExchangeOrderQuery},
|
||||
orderCancelFunc: &objects.UserFunction{Name: orderCancelFunc, Value: ExchangeOrderCancel},
|
||||
orderSubmitFunc: &objects.UserFunction{Name: orderSubmitFunc, Value: ExchangeOrderSubmit},
|
||||
withdrawCryptoFunc: &objects.UserFunction{Name: withdrawCryptoFunc, Value: ExchangeWithdrawCrypto},
|
||||
withdrawFiatFunc: &objects.UserFunction{Name: withdrawFiatFunc, Value: ExchangeWithdrawFiat},
|
||||
ohlcvFunc: &objects.UserFunction{Name: ohlcvFunc, Value: exchangeOHLCV},
|
||||
}
|
||||
|
||||
// ExchangeOrderbook returns orderbook for requested exchange & currencypair
|
||||
@@ -239,23 +239,23 @@ func ExchangePairs(args ...objects.Object) (objects.Object, error) {
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// ExchangeAccountInfo returns account information for requested exchange
|
||||
func ExchangeAccountInfo(args ...objects.Object) (objects.Object, error) {
|
||||
// ExchangeAccountBalances returns account balances for requested exchange
|
||||
func ExchangeAccountBalances(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 3 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
scriptCtx, ok := objects.ToInterface(args[0]).(*Context)
|
||||
if !ok {
|
||||
return nil, constructRuntimeError(1, accountInfoFunc, "*gct.Context", args[0])
|
||||
return nil, constructRuntimeError(1, accountBalancesFunc, "*gct.Context", args[0])
|
||||
}
|
||||
exchangeName, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
return nil, constructRuntimeError(2, accountInfoFunc, "string", args[1])
|
||||
return nil, constructRuntimeError(2, accountBalancesFunc, "string", args[1])
|
||||
}
|
||||
assetString, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
return nil, constructRuntimeError(3, accountInfoFunc, "string", args[2])
|
||||
return nil, constructRuntimeError(3, accountBalancesFunc, "string", args[2])
|
||||
}
|
||||
assetType, err := asset.New(assetString)
|
||||
if err != nil {
|
||||
@@ -263,25 +263,24 @@ func ExchangeAccountInfo(args ...objects.Object) (objects.Object, error) {
|
||||
}
|
||||
|
||||
ctx := processScriptContext(scriptCtx)
|
||||
rtnValue, err := wrappers.GetWrapper().
|
||||
AccountInformation(ctx, exchangeName, assetType)
|
||||
rtnValue, err := wrappers.GetWrapper().AccountBalances(ctx, exchangeName, assetType)
|
||||
if err != nil {
|
||||
return errorResponsef(standardFormatting, err)
|
||||
}
|
||||
|
||||
var funds objects.Array
|
||||
for x := range rtnValue.Accounts {
|
||||
for y := range rtnValue.Accounts[x].Currencies {
|
||||
temp := make(map[string]objects.Object, 3)
|
||||
temp["name"] = &objects.String{Value: rtnValue.Accounts[x].Currencies[y].Currency.String()}
|
||||
temp["total"] = &objects.Float{Value: rtnValue.Accounts[x].Currencies[y].Total}
|
||||
temp["hold"] = &objects.Float{Value: rtnValue.Accounts[x].Currencies[y].Hold}
|
||||
funds.Value = append(funds.Value, &objects.Map{Value: temp})
|
||||
for i := range rtnValue {
|
||||
for curr, bal := range rtnValue[i].Balances {
|
||||
funds.Value = append(funds.Value, &objects.Map{Value: map[string]objects.Object{
|
||||
"name": &objects.String{Value: curr.String()},
|
||||
"total": &objects.Float{Value: bal.Total},
|
||||
"hold": &objects.Float{Value: bal.Hold},
|
||||
}})
|
||||
}
|
||||
}
|
||||
|
||||
data := make(map[string]objects.Object, 2)
|
||||
data["exchange"] = &objects.String{Value: rtnValue.Exchange}
|
||||
data["exchange"] = &objects.String{Value: exchangeName}
|
||||
data["currencies"] = &funds
|
||||
return &objects.Map{Value: data}, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
objects "github.com/d5/tengo/v2"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchange/accounts"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
)
|
||||
|
||||
@@ -189,7 +189,7 @@ func processScriptContext(scriptCtx *Context) context.Context {
|
||||
otp, _ = objects.ToString(object)
|
||||
}
|
||||
|
||||
ctx = account.DeployCredentialsToContext(ctx, &account.Credentials{
|
||||
ctx = accounts.DeployCredentialsToContext(ctx, &accounts.Credentials{
|
||||
Key: key,
|
||||
Secret: secret,
|
||||
SubAccount: subAccount,
|
||||
@@ -199,7 +199,7 @@ func processScriptContext(scriptCtx *Context) context.Context {
|
||||
})
|
||||
} else if object = scriptCtx.Value["subaccount"]; object != nil {
|
||||
subAccount, _ := objects.ToString(object)
|
||||
ctx = account.DeploySubAccountOverrideToContext(ctx, subAccount)
|
||||
ctx = accounts.DeploySubAccountOverrideToContext(ctx, subAccount)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchange/accounts"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/gctscript/modules"
|
||||
@@ -103,16 +103,16 @@ func TestExchangePairs(t *testing.T) {
|
||||
assert.ErrorIs(t, err, objects.ErrWrongNumArguments)
|
||||
}
|
||||
|
||||
func TestAccountInfo(t *testing.T) {
|
||||
func TestAccountBalances(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := ExchangeAccountInfo()
|
||||
_, err := ExchangeAccountBalances()
|
||||
assert.ErrorIs(t, err, objects.ErrWrongNumArguments)
|
||||
|
||||
_, err = ExchangeAccountInfo(ctx, exch, assetType)
|
||||
_, err = ExchangeAccountBalances(ctx, exch, assetType)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = ExchangeAccountInfo(ctx, exchError, assetType)
|
||||
_, err = ExchangeAccountBalances(ctx, exchError, assetType)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -391,7 +391,7 @@ func TestSetSubAccount(t *testing.T) {
|
||||
t.Fatal("should not be nil")
|
||||
}
|
||||
|
||||
subaccount, ok := ctx.Value(account.ContextSubAccountFlag).(string)
|
||||
subaccount, ok := ctx.Value(accounts.ContextSubAccountFlag).(string)
|
||||
if !ok {
|
||||
t.Fatal("wrong type")
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchange/accounts"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
@@ -35,7 +35,7 @@ type GCTExchange interface {
|
||||
QueryOrder(ctx context.Context, exch, orderid string, pair currency.Pair, assetType asset.Item) (*order.Detail, error)
|
||||
SubmitOrder(ctx context.Context, submit *order.Submit) (*order.SubmitResponse, error)
|
||||
CancelOrder(ctx context.Context, exch, orderid string, pair currency.Pair, item asset.Item) (bool, error)
|
||||
AccountInformation(ctx context.Context, exch string, assetType asset.Item) (account.Holdings, error)
|
||||
AccountBalances(ctx context.Context, exch string, assetType asset.Item) (accounts.SubAccounts, error)
|
||||
DepositAddress(exch, chain string, currencyCode currency.Code) (*deposit.Address, error)
|
||||
WithdrawalFiatFunds(ctx context.Context, bankAccountID string, request *withdraw.Request) (out string, err error)
|
||||
WithdrawalCryptoFunds(ctx context.Context, request *withdraw.Request) (out string, err error)
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/engine"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchange/accounts"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
@@ -130,16 +130,16 @@ func (e Exchange) CancelOrder(ctx context.Context, exch, orderID string, cp curr
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// AccountInformation returns account information (balance etc) for requested exchange
|
||||
func (e Exchange) AccountInformation(ctx context.Context, exch string, assetType asset.Item) (account.Holdings, error) {
|
||||
// AccountBalances returns account balances for requested exchange
|
||||
func (e Exchange) AccountBalances(ctx context.Context, exch string, assetType asset.Item) (accounts.SubAccounts, error) {
|
||||
ex, err := e.GetExchange(exch)
|
||||
if err != nil {
|
||||
return account.Holdings{}, err
|
||||
return accounts.SubAccounts{}, err
|
||||
}
|
||||
|
||||
accountInfo, err := ex.GetCachedAccountInfo(ctx, assetType)
|
||||
accountInfo, err := ex.GetCachedSubAccounts(ctx, assetType)
|
||||
if err != nil {
|
||||
return account.Holdings{}, err
|
||||
return accounts.SubAccounts{}, err
|
||||
}
|
||||
|
||||
return accountInfo, nil
|
||||
|
||||
@@ -119,11 +119,11 @@ func TestExchange_Pairs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchange_AccountInformation(t *testing.T) {
|
||||
func TestExchange_AccountBalances(t *testing.T) {
|
||||
if !configureExchangeKeys() {
|
||||
t.Skip("no exchange configured test skipped")
|
||||
}
|
||||
_, err := exchangeTest.AccountInformation(t.Context(),
|
||||
_, err := exchangeTest.AccountBalances(t.Context(),
|
||||
exchName, asset.Spot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -159,14 +159,14 @@ func TestExchangePairs(t *testing.T) {
|
||||
assert.ErrorIs(t, err, objects.ErrWrongNumArguments)
|
||||
}
|
||||
|
||||
func TestExchangeAccountInfo(t *testing.T) {
|
||||
func TestExchangeAccountBalances(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := gct.ExchangeAccountInfo()
|
||||
_, err := gct.ExchangeAccountBalances()
|
||||
require.ErrorIs(t, err, objects.ErrWrongNumArguments)
|
||||
obj, err := gct.ExchangeAccountInfo(ctx, exch, assetType)
|
||||
obj, err := gct.ExchangeAccountBalances(ctx, exch, assetType)
|
||||
require.NoError(t, err)
|
||||
rString, ok := objects.ToString(obj)
|
||||
require.True(t, ok, "ExchangeAccountInfo return value must return correctly from objects.ToString")
|
||||
require.True(t, ok, "ExchangeAccountBalances return value must return correctly from objects.ToString")
|
||||
require.Contains(t, rString, "Bitstamp REST or Websocket authentication support is not enabled")
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/core"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchange/accounts"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
@@ -184,32 +184,29 @@ func (w Wrapper) CancelOrder(_ context.Context, exch, orderid string, cp currenc
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// AccountInformation validator for test execution/scripts
|
||||
func (w Wrapper) AccountInformation(_ context.Context, exch string, assetType asset.Item) (account.Holdings, error) {
|
||||
// AccountBalances validator for test execution/scripts
|
||||
func (w Wrapper) AccountBalances(_ context.Context, exch string, assetType asset.Item) (accounts.SubAccounts, error) {
|
||||
if exch == exchError.String() {
|
||||
return account.Holdings{}, errTestFailed
|
||||
return nil, errTestFailed
|
||||
}
|
||||
|
||||
return account.Holdings{
|
||||
Exchange: exch,
|
||||
Accounts: []account.SubAccount{
|
||||
{
|
||||
ID: exch,
|
||||
AssetType: assetType,
|
||||
Currencies: []account.Balance{
|
||||
{
|
||||
Currency: currency.Code{
|
||||
Item: ¤cy.Item{
|
||||
ID: 0,
|
||||
FullName: "Bitcoin",
|
||||
Symbol: "BTC",
|
||||
Role: 1,
|
||||
AssocChain: "",
|
||||
},
|
||||
},
|
||||
Total: 100,
|
||||
Hold: 0,
|
||||
},
|
||||
c := currency.Code{
|
||||
Item: ¤cy.Item{
|
||||
ID: 0,
|
||||
FullName: "Bitcoin",
|
||||
Symbol: "BTC",
|
||||
Role: 1,
|
||||
AssocChain: "",
|
||||
},
|
||||
}
|
||||
return accounts.SubAccounts{
|
||||
{
|
||||
ID: "subacct1",
|
||||
AssetType: assetType,
|
||||
Balances: accounts.CurrencyBalances{
|
||||
c: accounts.Balance{
|
||||
Currency: c,
|
||||
Total: 100,
|
||||
Hold: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
@@ -60,20 +62,14 @@ func TestWrapper_IsEnabled(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrapper_AccountInformation(t *testing.T) {
|
||||
func TestWrapperAccountBalances(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := testWrapper.AccountInformation(t.Context(),
|
||||
exchName, asset.Spot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err := testWrapper.AccountBalances(t.Context(), exchName, asset.Spot)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = testWrapper.AccountInformation(t.Context(),
|
||||
exchError.String(), asset.Spot)
|
||||
if err == nil {
|
||||
t.Fatal("expected AccountInformation to return error on invalid name")
|
||||
}
|
||||
_, err = testWrapper.AccountBalances(t.Context(), exchError.String(), asset.Spot)
|
||||
assert.ErrorIs(t, err, errTestFailed)
|
||||
}
|
||||
|
||||
func TestWrapper_CancelOrder(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user