diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index fd6f79d4..60c308ce 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -525,19 +525,19 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange func (bt *BackTest) loadExchangePairAssetBase(exch, base, quote, ass string) (gctexchange.IBotExchange, currency.Pair, asset.Item, error) { e, err := bt.exchangeManager.GetExchangeByName(exch) if err != nil { - return nil, currency.EMPTYPAIR, "", err + return nil, currency.EMPTYPAIR, asset.Empty, err } var cp, fPair currency.Pair cp, err = currency.NewPairFromStrings(base, quote) if err != nil { - return nil, currency.EMPTYPAIR, "", err + return nil, currency.EMPTYPAIR, asset.Empty, err } var a asset.Item a, err = asset.New(ass) if err != nil { - return nil, currency.EMPTYPAIR, "", err + return nil, currency.EMPTYPAIR, asset.Empty, err } exchangeBase := e.GetBase() @@ -547,7 +547,7 @@ func (bt *BackTest) loadExchangePairAssetBase(exch, base, quote, ass string) (gc fPair, err = exchangeBase.FormatExchangeCurrency(cp, a) if err != nil { - return nil, currency.EMPTYPAIR, "", err + return nil, currency.EMPTYPAIR, asset.Empty, err } return e, fPair, a, nil } diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index c1b928c6..3b99f00b 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -312,7 +312,7 @@ func applySlippageToPrice(direction gctorder.Side, price, slippageRate decimal.D // SetExchangeAssetCurrencySettings sets the settings for an exchange, asset, currency func (e *Exchange) SetExchangeAssetCurrencySettings(exch string, a asset.Item, cp currency.Pair, c *Settings) { if c.Exchange == "" || - c.Asset == "" || + c.Asset == asset.Empty || c.Pair.IsEmpty() { return } diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index c0cb684e..173f9abe 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -44,7 +44,7 @@ func TestReset(t *testing.T) { func TestSetCurrency(t *testing.T) { t.Parallel() e := Exchange{} - e.SetExchangeAssetCurrencySettings("", "", currency.EMPTYPAIR, &Settings{}) + e.SetExchangeAssetCurrencySettings("", asset.Empty, currency.EMPTYPAIR, &Settings{}) if len(e.CurrencySettings) != 0 { t.Error("expected 0") } @@ -265,7 +265,7 @@ func TestExecuteOrder(t *testing.T) { Item: gctkline.Item{ Exchange: "", Pair: currency.EMPTYPAIR, - Asset: "", + Asset: asset.Empty, Interval: 0, Candles: []gctkline.Candle{ { @@ -386,7 +386,7 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { Item: gctkline.Item{ Exchange: "", Pair: currency.EMPTYPAIR, - Asset: "", + Asset: asset.Empty, Interval: 0, Candles: []gctkline.Candle{ { diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 1af839f7..8b207657 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -433,7 +433,7 @@ func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings) (*Sett if settings.Exchange == "" { return nil, errExchangeUnset } - if settings.Asset == "" { + if settings.Asset == asset.Empty { return nil, errAssetUnset } if settings.Pair.IsEmpty() { diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index fd0765c4..86d14056 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -303,7 +303,7 @@ func TestUpdate(t *testing.T) { func TestGetFee(t *testing.T) { t.Parallel() p := Portfolio{} - f := p.GetFee("", "", currency.EMPTYPAIR) + f := p.GetFee("", asset.Empty, currency.EMPTYPAIR) if !f.IsZero() { t.Error("expected 0") } @@ -323,7 +323,7 @@ func TestGetFee(t *testing.T) { func TestGetComplianceManager(t *testing.T) { t.Parallel() p := Portfolio{} - _, err := p.GetComplianceManager("", "", currency.EMPTYPAIR) + _, err := p.GetComplianceManager("", asset.Empty, currency.EMPTYPAIR) if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index f3003a78..6da76234 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -852,18 +852,7 @@ func TestMatchesCurrency(t *testing.T) { func TestCreateSnapshot(t *testing.T) { f := FundManager{} f.CreateSnapshot(time.Time{}) - f.items = append(f.items, &Item{ - exchange: "", - asset: "", - currency: currency.EMPTYCODE, - initialFunds: decimal.Decimal{}, - available: decimal.Decimal{}, - reserved: decimal.Decimal{}, - transferFee: decimal.Decimal{}, - pairedWith: nil, - usdTrackingCandles: nil, - snapshot: nil, - }) + f.items = append(f.items, &Item{}) f.CreateSnapshot(time.Time{}) dfk := &kline.DataFromKline{ diff --git a/config/config_test.go b/config/config_test.go index 3dccaee9..4fad7bd8 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -535,7 +535,7 @@ func TestSupportsExchangeAssetType(t *testing.T) { t.Error(err) } - err = c.SupportsExchangeAssetType(testFakeExchangeName, "asdf") + err = c.SupportsExchangeAssetType(testFakeExchangeName, asset.Empty) if err == nil { t.Error("Expected error from invalid asset item") } @@ -988,7 +988,7 @@ func TestGetPairFormat(t *testing.T) { asset.Spot: new(currency.PairStore), }, } - _, err = c.GetPairFormat(testFakeExchangeName, asset.Item("invalid")) + _, err = c.GetPairFormat(testFakeExchangeName, asset.Empty) if err == nil { t.Error("Expected error from non-existent asset item") } diff --git a/currency/manager.go b/currency/manager.go index 246aaea0..2ee9ea8c 100644 --- a/currency/manager.go +++ b/currency/manager.go @@ -1,6 +1,7 @@ package currency import ( + "encoding/json" "errors" "fmt" @@ -224,3 +225,33 @@ func (p *PairsManager) getPairStore(a asset.Item) (*PairStore, error) { return c, nil } + +// UnmarshalJSON implements the unmarshal json interface so that the key can be +// correctly unmarshalled from a string into a uint. +func (fs *FullStore) UnmarshalJSON(d []byte) error { + var temp map[string]*PairStore + err := json.Unmarshal(d, &temp) + if err != nil { + return err + } + + *fs = make(FullStore, len(temp)) + for key, val := range temp { + ai, err := asset.New(key) + if err != nil { + return err + } + (*fs)[ai] = val + } + return nil +} + +// MarshalJSON implements the marshal json interface so that the key can be +// correctly marshalled from a uint. +func (fs FullStore) MarshalJSON() ([]byte, error) { + temp := make(map[string]*PairStore, len(fs)) + for key, val := range fs { + temp[key.String()] = val + } + return json.Marshal(temp) +} diff --git a/currency/manager_test.go b/currency/manager_test.go index 0ef80352..7c4c59f4 100644 --- a/currency/manager_test.go +++ b/currency/manager_test.go @@ -1,6 +1,8 @@ package currency import ( + "encoding/json" + "errors" "testing" "github.com/thrasher-corp/gocryptotrader/common/convert" @@ -158,7 +160,7 @@ func TestGetPairs(t *testing.T) { t.Fatal("pairs should be populated") } - pairs, err = p.GetPairs("blah", true) + pairs, err = p.GetPairs(asset.Empty, true) if err != nil { t.Fatal(err) } @@ -361,3 +363,40 @@ func TestIsAssetEnabled_SetAssetEnabled(t *testing.T) { t.Error("unexpected result") } } + +func TestUnmarshalMarshal(t *testing.T) { + t.Parallel() + var um = make(FullStore) + um[asset.Spot] = &PairStore{AssetEnabled: convert.BoolPtr(true)} + + data, err := json.Marshal(um) + if err != nil { + t.Fatal(err) + } + + if string(data) != `{"spot":{"assetEnabled":true,"enabled":"","available":""}}` { + t.Fatal("unexpected value") + } + + var another FullStore + err = json.Unmarshal(data, &another) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if _, ok := another[asset.Spot]; !ok { + t.Fatal("expected values to be associated with spot") + } + + data = []byte(`{123:{"assetEnabled":null,"enabled":"","available":""}}`) + err = json.Unmarshal(data, &another) + if errors.Is(err, nil) { + t.Fatalf("expected error") + } + + data = []byte(`{"bro":{"assetEnabled":null,"enabled":"","available":""}}`) + err = json.Unmarshal(data, &another) + if !errors.Is(err, asset.ErrNotSupported) { + t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported) + } +} diff --git a/currency/manager_types.go b/currency/manager_types.go index 7bc0e759..3575b824 100644 --- a/currency/manager_types.go +++ b/currency/manager_types.go @@ -8,15 +8,19 @@ import ( // PairsManager manages asset pairs type PairsManager struct { - BypassConfigFormatUpgrades bool `json:"bypassConfigFormatUpgrades"` - RequestFormat *PairFormat `json:"requestFormat,omitempty"` - ConfigFormat *PairFormat `json:"configFormat,omitempty"` - UseGlobalFormat bool `json:"useGlobalFormat,omitempty"` - LastUpdated int64 `json:"lastUpdated,omitempty"` - Pairs map[asset.Item]*PairStore `json:"pairs"` + BypassConfigFormatUpgrades bool `json:"bypassConfigFormatUpgrades"` + RequestFormat *PairFormat `json:"requestFormat,omitempty"` + ConfigFormat *PairFormat `json:"configFormat,omitempty"` + UseGlobalFormat bool `json:"useGlobalFormat,omitempty"` + LastUpdated int64 `json:"lastUpdated,omitempty"` + Pairs FullStore `json:"pairs"` m sync.RWMutex } +// FullStore holds all supported asset types with the enabled and available +// pairs for an exchange. +type FullStore map[asset.Item]*PairStore + // PairStore stores a currency pair store type PairStore struct { AssetEnabled *bool `json:"assetEnabled"` diff --git a/engine/currency_state_manager_test.go b/engine/currency_state_manager_test.go index c0e24099..b23bc1ff 100644 --- a/engine/currency_state_manager_test.go +++ b/engine/currency_state_manager_test.go @@ -240,7 +240,7 @@ func TestGetAllRPC(t *testing.T) { func TestCanWithdrawRPC(t *testing.T) { t.Parallel() - _, err := (*CurrencyStateManager)(nil).CanWithdrawRPC("", currency.EMPTYCODE, "") + _, err := (*CurrencyStateManager)(nil).CanWithdrawRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted) } @@ -248,7 +248,7 @@ func TestCanWithdrawRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true}, - }).CanWithdrawRPC("", currency.EMPTYCODE, "") + }).CanWithdrawRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errManager) { t.Fatalf("received: '%v' but expected: '%v'", err, errManager) } @@ -256,7 +256,7 @@ func TestCanWithdrawRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true}, - }).CanWithdrawRPC("", currency.EMPTYCODE, "") + }).CanWithdrawRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errExchange) { t.Fatalf("received: '%v' but expected: '%v'", err, errExchange) } @@ -264,7 +264,7 @@ func TestCanWithdrawRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{}, - }).CanWithdrawRPC("", currency.EMPTYCODE, "") + }).CanWithdrawRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } @@ -272,7 +272,7 @@ func TestCanWithdrawRPC(t *testing.T) { func TestCanDepositRPC(t *testing.T) { t.Parallel() - _, err := (*CurrencyStateManager)(nil).CanDepositRPC("", currency.EMPTYCODE, "") + _, err := (*CurrencyStateManager)(nil).CanDepositRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted) } @@ -280,7 +280,7 @@ func TestCanDepositRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true}, - }).CanDepositRPC("", currency.EMPTYCODE, "") + }).CanDepositRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errManager) { t.Fatalf("received: '%v' but expected: '%v'", err, errManager) } @@ -288,7 +288,7 @@ func TestCanDepositRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true}, - }).CanDepositRPC("", currency.EMPTYCODE, "") + }).CanDepositRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errExchange) { t.Fatalf("received: '%v' but expected: '%v'", err, errExchange) } @@ -296,7 +296,7 @@ func TestCanDepositRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{}, - }).CanDepositRPC("", currency.EMPTYCODE, "") + }).CanDepositRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } @@ -304,7 +304,7 @@ func TestCanDepositRPC(t *testing.T) { func TestCanTradeRPC(t *testing.T) { t.Parallel() - _, err := (*CurrencyStateManager)(nil).CanTradeRPC("", currency.EMPTYCODE, "") + _, err := (*CurrencyStateManager)(nil).CanTradeRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted) } @@ -312,7 +312,7 @@ func TestCanTradeRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true}, - }).CanTradeRPC("", currency.EMPTYCODE, "") + }).CanTradeRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errManager) { t.Fatalf("received: '%v' but expected: '%v'", err, errManager) } @@ -320,7 +320,7 @@ func TestCanTradeRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true}, - }).CanTradeRPC("", currency.EMPTYCODE, "") + }).CanTradeRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errExchange) { t.Fatalf("received: '%v' but expected: '%v'", err, errExchange) } @@ -328,7 +328,7 @@ func TestCanTradeRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{}, - }).CanTradeRPC("", currency.EMPTYCODE, "") + }).CanTradeRPC("", currency.EMPTYCODE, asset.Empty) if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } @@ -336,7 +336,7 @@ func TestCanTradeRPC(t *testing.T) { func TestCanTradePairRPC(t *testing.T) { t.Parallel() - _, err := (*CurrencyStateManager)(nil).CanTradePairRPC("", currency.EMPTYPAIR, "") + _, err := (*CurrencyStateManager)(nil).CanTradePairRPC("", currency.EMPTYPAIR, asset.Empty) if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted) } @@ -344,7 +344,7 @@ func TestCanTradePairRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true}, - }).CanTradePairRPC("", currency.EMPTYPAIR, "") + }).CanTradePairRPC("", currency.EMPTYPAIR, asset.Empty) if !errors.Is(err, errManager) { t.Fatalf("received: '%v' but expected: '%v'", err, errManager) } @@ -352,7 +352,7 @@ func TestCanTradePairRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true}, - }).CanTradePairRPC("", currency.EMPTYPAIR, "") + }).CanTradePairRPC("", currency.EMPTYPAIR, asset.Empty) if !errors.Is(err, errExchange) { t.Fatalf("received: '%v' but expected: '%v'", err, errExchange) } @@ -360,7 +360,7 @@ func TestCanTradePairRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{}, - }).CanTradePairRPC("", currency.EMPTYPAIR, "") + }).CanTradePairRPC("", currency.EMPTYPAIR, asset.Empty) if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } diff --git a/engine/datahistory_manager.go b/engine/datahistory_manager.go index 69678220..71890413 100644 --- a/engine/datahistory_manager.go +++ b/engine/datahistory_manager.go @@ -1479,11 +1479,17 @@ func (m *DataHistoryManager) convertDBModelToJob(dbModel *datahistoryjob.DataHis return nil, fmt.Errorf("job %s could not convert database job: %w", dbModel.Nickname, err) } + ai, err := asset.New(dbModel.Asset) + if err != nil { + return nil, fmt.Errorf("job %s could not derive asset: %w", + dbModel.Nickname, err) + } + resp := &DataHistoryJob{ ID: id, Nickname: dbModel.Nickname, Exchange: dbModel.ExchangeName, - Asset: asset.Item(dbModel.Asset), + Asset: ai, Pair: cp, StartDate: dbModel.StartDate, EndDate: dbModel.EndDate, diff --git a/engine/datahistory_manager_test.go b/engine/datahistory_manager_test.go index 348ce0f3..5b7d6abd 100644 --- a/engine/datahistory_manager_test.go +++ b/engine/datahistory_manager_test.go @@ -1498,11 +1498,15 @@ func dataHistoryTraderLoader(exch, a, base, quote string, start, _ time.Time) ([ if err != nil { return nil, err } + ai, err := asset.New(a) + if err != nil { + return nil, err + } return []trade.Data{ { Exchange: exch, CurrencyPair: cp, - AssetType: asset.Item(a), + AssetType: ai, Side: order.Buy, Price: 1337, Amount: 1337, diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index d645bf87..4d0094c4 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -520,14 +520,14 @@ func TestCancelOrder(t *testing.T) { func TestGetOrderInfo(t *testing.T) { m := OrdersSetup(t) - _, err := m.GetOrderInfo(context.Background(), "", "", currency.EMPTYPAIR, "") + _, err := m.GetOrderInfo(context.Background(), "", "", currency.EMPTYPAIR, asset.Empty) if err == nil { t.Error("Expected error due to empty order") } var result order.Detail result, err = m.GetOrderInfo(context.Background(), - testExchange, "1337", currency.EMPTYPAIR, "") + testExchange, "1337", currency.EMPTYPAIR, asset.Empty) if err != nil { t.Error(err) } @@ -536,7 +536,7 @@ func TestGetOrderInfo(t *testing.T) { } result, err = m.GetOrderInfo(context.Background(), - testExchange, "1337", currency.EMPTYPAIR, "") + testExchange, "1337", currency.EMPTYPAIR, asset.Empty) if err != nil { t.Error(err) } diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 49659ab8..21677da0 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -3547,8 +3547,9 @@ func (s *RPCServer) GetRecentTrades(ctx context.Context, r *gctrpc.GetSavedTrade if err != nil { return nil, err } + var trades []trade.Data - trades, err = exch.GetRecentTrades(ctx, cp, asset.Item(r.AssetType)) + trades, err = exch.GetRecentTrades(ctx, cp, a) if err != nil { return nil, err } @@ -4089,24 +4090,36 @@ func (s *RPCServer) CurrencyStateGetAll(_ context.Context, r *gctrpc.CurrencySta // CurrencyStateWithdraw determines via RPC if the currency code is operational for // withdrawal from an exchange func (s *RPCServer) CurrencyStateWithdraw(_ context.Context, r *gctrpc.CurrencyStateWithdrawRequest) (*gctrpc.GenericResponse, error) { + ai, err := asset.New(r.Asset) + if err != nil { + return nil, err + } return s.currencyStateManager.CanWithdrawRPC(r.Exchange, currency.NewCode(r.Code), - asset.Item(r.Asset)) + ai) } // CurrencyStateDeposit determines via RPC if the currency code is operational for // depositing to an exchange func (s *RPCServer) CurrencyStateDeposit(_ context.Context, r *gctrpc.CurrencyStateDepositRequest) (*gctrpc.GenericResponse, error) { + ai, err := asset.New(r.Asset) + if err != nil { + return nil, err + } return s.currencyStateManager.CanDepositRPC(r.Exchange, currency.NewCode(r.Code), - asset.Item(r.Asset)) + ai) } // CurrencyStateTrading determines via RPC if the currency code is operational for trading func (s *RPCServer) CurrencyStateTrading(_ context.Context, r *gctrpc.CurrencyStateTradingRequest) (*gctrpc.GenericResponse, error) { + ai, err := asset.New(r.Asset) + if err != nil { + return nil, err + } return s.currencyStateManager.CanTradeRPC(r.Exchange, currency.NewCode(r.Code), - asset.Item(r.Asset)) + ai) } // CurrencyStateTradingPair determines via RPC if the pair is operational for trading @@ -4121,19 +4134,23 @@ func (s *RPCServer) CurrencyStateTradingPair(_ context.Context, r *gctrpc.Curren return nil, err } - a := asset.Item(r.Asset) - err = checkParams(r.Exchange, exch, a, cp) + ai, err := asset.New(r.Asset) if err != nil { return nil, err } - err = exch.CanTradePair(cp, a) + err = checkParams(r.Exchange, exch, ai, cp) + if err != nil { + return nil, err + } + + err = exch.CanTradePair(cp, ai) if err != nil { return nil, err } return s.currencyStateManager.CanTradePairRPC(r.Exchange, cp, - asset.Item(r.Asset)) + ai) } // GetFuturesPositions returns pnl positions for an exchange asset pair @@ -4147,13 +4164,17 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture return nil, err } - a := asset.Item(r.Asset) - err = checkParams(r.Exchange, exch, a, cp) + ai, err := asset.New(r.Asset) if err != nil { return nil, err } - if !a.IsFutures() { - return nil, fmt.Errorf("%s %w", a, order.ErrNotFuturesAsset) + + err = checkParams(r.Exchange, exch, ai, cp) + if err != nil { + return nil, err + } + if !ai.IsFutures() { + return nil, fmt.Errorf("%s %w", ai, order.ErrNotFuturesAsset) } var start, end time.Time if r.StartDate != "" { @@ -4182,7 +4203,7 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture if creds.SubAccount != "" { subErr = "for subaccount: " + creds.SubAccount } - orders, err := exch.GetFuturesPositions(ctx, a, cp, start, end) + orders, err := exch.GetFuturesPositions(ctx, ai, cp, start, end) if err != nil { return nil, fmt.Errorf("%w %v", err, subErr) } @@ -4190,7 +4211,7 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture return orders[i].Date.Before(orders[j].Date) }) if r.Overwrite { - err = s.OrderManager.ClearFuturesTracking(r.Exchange, a, cp) + err = s.OrderManager.ClearFuturesTracking(r.Exchange, ai, cp) if err != nil { return nil, fmt.Errorf("%w %v", err, subErr) } @@ -4203,7 +4224,7 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture } } } - pos, err := s.OrderManager.GetFuturesPositionsForExchange(r.Exchange, a, cp) + pos, err := s.OrderManager.GetFuturesPositionsForExchange(r.Exchange, ai, cp) if err != nil { return nil, fmt.Errorf("%w %v", err, subErr) } @@ -4314,7 +4335,11 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe return nil, err } - a := asset.Item(r.Asset) + a, err := asset.New(r.Asset) + if err != nil { + return nil, err + } + err = checkParams(r.Exchange, exch, a, currency.Pair{}) if err != nil { return nil, err diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 3a836107..2a5e8a12 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -2002,7 +2002,16 @@ func TestCurrencyStateWithdraw(t *testing.T) { Engine: &Engine{}, }).CurrencyStateWithdraw(context.Background(), &gctrpc.CurrencyStateWithdrawRequest{ - Exchange: "wow"}) + Exchange: "wow", Asset: "meow"}) + if !errors.Is(err, asset.ErrNotSupported) { + t.Fatalf("received: %v, but expected: %v", err, asset.ErrNotSupported) + } + + _, err = (&RPCServer{ + Engine: &Engine{}, + }).CurrencyStateWithdraw(context.Background(), + &gctrpc.CurrencyStateWithdrawRequest{ + Exchange: "wow", Asset: "spot"}) if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: %v, but expected: %v", err, ErrSubSystemNotStarted) } @@ -2013,7 +2022,15 @@ func TestCurrencyStateDeposit(t *testing.T) { _, err := (&RPCServer{ Engine: &Engine{}, }).CurrencyStateDeposit(context.Background(), - &gctrpc.CurrencyStateDepositRequest{Exchange: "wow"}) + &gctrpc.CurrencyStateDepositRequest{Exchange: "wow", Asset: "meow"}) + if !errors.Is(err, asset.ErrNotSupported) { + t.Fatalf("received: %v, but expected: %v", err, asset.ErrNotSupported) + } + + _, err = (&RPCServer{ + Engine: &Engine{}, + }).CurrencyStateDeposit(context.Background(), + &gctrpc.CurrencyStateDepositRequest{Exchange: "wow", Asset: "spot"}) if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: %v, but expected: %v", err, ErrSubSystemNotStarted) } @@ -2024,7 +2041,15 @@ func TestCurrencyStateTrading(t *testing.T) { _, err := (&RPCServer{ Engine: &Engine{}, }).CurrencyStateTrading(context.Background(), - &gctrpc.CurrencyStateTradingRequest{Exchange: "wow"}) + &gctrpc.CurrencyStateTradingRequest{Exchange: "wow", Asset: "meow"}) + if !errors.Is(err, asset.ErrNotSupported) { + t.Fatalf("received: %v, but expected: %v", err, asset.ErrNotSupported) + } + + _, err = (&RPCServer{ + Engine: &Engine{}, + }).CurrencyStateTrading(context.Background(), + &gctrpc.CurrencyStateTradingRequest{Exchange: "wow", Asset: "spot"}) if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: %v, but expected: %v", err, ErrSubSystemNotStarted) } diff --git a/exchanges/account/account_test.go b/exchanges/account/account_test.go index f5fd4671..f29f2112 100644 --- a/exchanges/account/account_test.go +++ b/exchanges/account/account_test.go @@ -103,7 +103,7 @@ func TestHoldings(t *testing.T) { t.Error("error cannot be nil") } - _, err = GetHoldings("bla", asset.Item("hi")) + _, err = GetHoldings("bla", asset.Empty) if err == nil { t.Error("error cannot be nil since an invalid asset type is provided") } diff --git a/exchanges/asset/asset.go b/exchanges/asset/asset.go index b9106281..580db288 100644 --- a/exchanges/asset/asset.go +++ b/exchanges/asset/asset.go @@ -1,6 +1,7 @@ package asset import ( + "encoding/json" "errors" "fmt" "strings" @@ -8,54 +9,87 @@ import ( var ( // ErrNotSupported is an error for an unsupported asset type - ErrNotSupported = errors.New("received unsupported asset type") + ErrNotSupported = errors.New("unsupported asset type") ) // Item stores the asset type -type Item string +type Item uint16 // Items stores a list of assets types type Items []Item // Const vars for asset package const ( - Spot = Item("spot") - Margin = Item("margin") - MarginFunding = Item("marginfunding") - Index = Item("index") - Binary = Item("binary") - PerpetualContract = Item("perpetualcontract") - PerpetualSwap = Item("perpetualswap") - Futures = Item("futures") - UpsideProfitContract = Item("upsideprofitcontract") - DownsideProfitContract = Item("downsideprofitcontract") - CoinMarginedFutures = Item("coinmarginedfutures") - USDTMarginedFutures = Item("usdtmarginedfutures") + Empty Item = 0 + Spot Item = 1 << iota + Margin + MarginFunding + Index + Binary + PerpetualContract + PerpetualSwap + Futures + UpsideProfitContract + DownsideProfitContract + CoinMarginedFutures + USDTMarginedFutures + + futuresFlag = PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures + supportedFlag = Spot | Margin | MarginFunding | Index | Binary | PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures + + spot = "spot" + margin = "margin" + marginFunding = "marginfunding" + index = "index" + binary = "binary" + perpetualContract = "perpetualcontract" + perpetualSwap = "perpetualswap" + futures = "futures" + upsideProfitContract = "upsideprofitcontract" + downsideProfitContract = "downsideprofitcontract" + coinMarginedFutures = "coinmarginedfutures" + usdtMarginedFutures = "usdtmarginedfutures" ) -var supported = Items{ - Spot, - Margin, - MarginFunding, - Index, - Binary, - PerpetualContract, - PerpetualSwap, - Futures, - UpsideProfitContract, - DownsideProfitContract, - CoinMarginedFutures, - USDTMarginedFutures, -} +var ( + supportedList = Items{Spot, Margin, MarginFunding, Index, Binary, PerpetualContract, PerpetualSwap, Futures, UpsideProfitContract, DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures} +) // Supported returns a list of supported asset types func Supported() Items { - return supported + return supportedList } // returns an Item to string func (a Item) String() string { - return string(a) + switch a { + case Spot: + return spot + case Margin: + return margin + case MarginFunding: + return marginFunding + case Index: + return index + case Binary: + return binary + case PerpetualContract: + return perpetualContract + case PerpetualSwap: + return perpetualSwap + case Futures: + return futures + case UpsideProfitContract: + return upsideProfitContract + case DownsideProfitContract: + return downsideProfitContract + case CoinMarginedFutures: + return coinMarginedFutures + case USDTMarginedFutures: + return usdtMarginedFutures + default: + return "" + } } // Strings converts an asset type array to a string array @@ -70,13 +104,11 @@ func (a Items) Strings() []string { // Contains returns whether or not the supplied asset exists // in the list of Items func (a Items) Contains(i Item) bool { - if !i.IsValid() { - return false - } - - for x := range a { - if a[x].String() == i.String() { - return true + if i.IsValid() { + for x := range a { + if a[x] == i { + return true + } } } return false @@ -91,26 +123,69 @@ func (a Items) JoinToString(separator string) string { // IsValid returns whether or not the supplied asset type is valid or // not func (a Item) IsValid() bool { - for x := range supported { - if supported[x].String() == a.String() { - return true - } + return a != Empty && supportedFlag&a == a +} + +// UnmarshalJSON comforms type to the umarshaler interface +func (a *Item) UnmarshalJSON(d []byte) error { + var assetString string + err := json.Unmarshal(d, &assetString) + if err != nil { + return err } - return false + + if assetString == "" { + return nil + } + + ai, err := New(assetString) + if err != nil { + return err + } + + *a = ai + return nil +} + +// MarshalJSON comforms type to the marshaller interface +func (a Item) MarshalJSON() ([]byte, error) { + return json.Marshal(a.String()) } // New takes an input matches to relevant package assets func New(input string) (Item, error) { input = strings.ToLower(input) - for i := range supported { - if string(supported[i]) == input { - return supported[i], nil - } + switch input { + case spot: + return Spot, nil + case margin: + return Margin, nil + case marginFunding: + return MarginFunding, nil + case index: + return Index, nil + case binary: + return Binary, nil + case perpetualContract: + return PerpetualContract, nil + case perpetualSwap: + return PerpetualSwap, nil + case futures: + return Futures, nil + case upsideProfitContract: + return UpsideProfitContract, nil + case downsideProfitContract: + return DownsideProfitContract, nil + case coinMarginedFutures: + return CoinMarginedFutures, nil + case usdtMarginedFutures: + return USDTMarginedFutures, nil + default: + return 0, fmt.Errorf("%w '%v', only supports %s", + ErrNotSupported, + input, + supportedList) } - return "", fmt.Errorf("%w %v, only supports %v", - ErrNotSupported, - input, - supported) } // UseDefault returns default asset type @@ -120,10 +195,5 @@ func UseDefault() Item { // IsFutures checks if the asset type is a futures contract based asset func (a Item) IsFutures() bool { - switch a { - case PerpetualContract, PerpetualSwap, Futures, UpsideProfitContract, - DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures: - return true - } - return false + return a != Empty && futuresFlag&a == a } diff --git a/exchanges/asset/asset_test.go b/exchanges/asset/asset_test.go index 9f274d3f..46429085 100644 --- a/exchanges/asset/asset_test.go +++ b/exchanges/asset/asset_test.go @@ -1,19 +1,28 @@ package asset import ( + "encoding/json" + "errors" "testing" "github.com/thrasher-corp/gocryptotrader/common" ) func TestString(t *testing.T) { + t.Parallel() a := Spot if a.String() != "spot" { t.Fatal("TestString returned an unexpected result") } + + a = 0 + if a.String() != "" { + t.Fatal("TestString returned an unexpected result") + } } func TestToStringArray(t *testing.T) { + t.Parallel() a := Items{Spot, Futures} result := a.Strings() for x := range a { @@ -24,8 +33,9 @@ func TestToStringArray(t *testing.T) { } func TestContains(t *testing.T) { + t.Parallel() a := Items{Spot, Futures} - if a.Contains("meow") { + if a.Contains(666) { t.Fatal("TestContains returned an unexpected result") } @@ -39,12 +49,13 @@ func TestContains(t *testing.T) { // Every asset should be created and matched with func New so this should // not be matched against list - if a.Contains("SpOt") { + if a.Contains(0) { t.Error("TestContains returned an unexpected result") } } func TestJoinToString(t *testing.T) { + t.Parallel() a := Items{Spot, Futures} if a.JoinToString(",") != "spot,futures" { t.Fatal("TestJoinToString returned an unexpected result") @@ -52,7 +63,8 @@ func TestJoinToString(t *testing.T) { } func TestIsValid(t *testing.T) { - if Item("rawr").IsValid() { + t.Parallel() + if Item(0).IsValid() { t.Fatal("TestIsValid returned an unexpected result") } @@ -62,27 +74,49 @@ func TestIsValid(t *testing.T) { } func TestNew(t *testing.T) { - if _, err := New("Spota"); err == nil { - t.Fatal("TestNew returned an unexpected result") + t.Parallel() + cases := []struct { + Input string + Expected Item + Error error + }{ + {Input: "Spota", Error: ErrNotSupported}, + {Input: "MARGIN", Expected: Margin}, + {Input: "MARGINFUNDING", Expected: MarginFunding}, + {Input: "INDEX", Expected: Index}, + {Input: "BINARY", Expected: Binary}, + {Input: "PERPETUALCONTRACT", Expected: PerpetualContract}, + {Input: "PERPETUALSWAP", Expected: PerpetualSwap}, + {Input: "FUTURES", Expected: Futures}, + {Input: "UpsideProfitContract", Expected: UpsideProfitContract}, + {Input: "DownsideProfitContract", Expected: DownsideProfitContract}, + {Input: "CoinMarginedFutures", Expected: CoinMarginedFutures}, + {Input: "USDTMarginedFutures", Expected: USDTMarginedFutures}, } - a, err := New("SpOt") - if err != nil { - t.Fatal("TestNew returned an unexpected result", err) - } - - if a != Spot { - t.Fatal("TestNew returned an unexpected result") + for x := range cases { + tt := cases[x] + t.Run("", func(t *testing.T) { + t.Parallel() + returned, err := New(tt.Input) + if !errors.Is(err, tt.Error) { + t.Fatalf("receieved: '%v' but expected: '%v'", err, tt.Error) + } + if returned != tt.Expected { + t.Fatalf("receieved: '%v' but expected: '%v'", returned, tt.Expected) + } + }) } } func TestSupported(t *testing.T) { + t.Parallel() s := Supported() - if len(supported) != len(s) { + if len(supportedList) != len(s) { t.Fatal("TestSupported mismatched lengths") } - for i := 0; i < len(supported); i++ { - if s[i] != supported[i] { + for i := 0; i < len(supportedList); i++ { + if s[i] != supportedList[i] { t.Fatal("TestSupported returned an unexpected result") } } @@ -154,3 +188,57 @@ func TestIsFutures(t *testing.T) { }) } } + +func TestUnmarshalMarshal(t *testing.T) { + t.Parallel() + data, err := json.Marshal(Item(0)) + if !errors.Is(err, nil) { + t.Fatalf("receieved: '%v' but expected: '%v'", err, nil) + } + + if string(data) != `""` { + t.Fatal("unexpected value") + } + + data, err = json.Marshal(Spot) + if !errors.Is(err, nil) { + t.Fatalf("receieved: '%v' but expected: '%v'", err, nil) + } + + if string(data) != `"spot"` { + t.Fatal("unexpected value") + } + + var spot Item + + err = json.Unmarshal(data, &spot) + if !errors.Is(err, nil) { + t.Fatalf("receieved: '%v' but expected: '%v'", err, nil) + } + + if spot != Spot { + t.Fatal("unexpected value") + } + + err = json.Unmarshal([]byte(`"confused"`), &spot) + if !errors.Is(err, ErrNotSupported) { + t.Fatalf("receieved: '%v' but expected: '%v'", err, ErrNotSupported) + } + + err = json.Unmarshal([]byte(`""`), &spot) + if !errors.Is(err, nil) { + t.Fatalf("receieved: '%v' but expected: '%v'", err, nil) + } + + err = json.Unmarshal([]byte(`123`), &spot) + if errors.Is(err, nil) { + t.Fatalf("receieved: '%v' but expected: '%v'", nil, "an error") + } +} + +func TestUseDefault(t *testing.T) { + t.Parallel() + if UseDefault() != Spot { + t.Fatalf("receieved: '%v' but expected: '%v'", UseDefault(), Spot) + } +} diff --git a/exchanges/bithumb/bithumb_test.go b/exchanges/bithumb/bithumb_test.go index bb7faa59..af281a57 100644 --- a/exchanges/bithumb/bithumb_test.go +++ b/exchanges/bithumb/bithumb_test.go @@ -666,7 +666,7 @@ func TestGetHistoricTrades(t *testing.T) { func TestUpdateOrderExecutionLimits(t *testing.T) { t.Parallel() - err := b.UpdateOrderExecutionLimits(context.Background(), "") + err := b.UpdateOrderExecutionLimits(context.Background(), asset.Empty) if err != nil { t.Fatal(err) } diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 6dc6ea1c..0de4e24b 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -212,7 +212,7 @@ func (b *Bithumb) Run() { b.PrintEnabledPairs() } - err := b.UpdateOrderExecutionLimits(context.TODO(), "") + err := b.UpdateOrderExecutionLimits(context.TODO(), asset.Empty) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to set exchange order execution limits. Err: %v", diff --git a/exchanges/currencystate/currency_state_test.go b/exchanges/currencystate/currency_state_test.go index b8a2ef80..ae260891 100644 --- a/exchanges/currencystate/currency_state_test.go +++ b/exchanges/currencystate/currency_state_test.go @@ -43,18 +43,18 @@ func TestGetSnapshot(t *testing.T) { func TestCanTradePair(t *testing.T) { t.Parallel() - err := (*States)(nil).CanTradePair(currency.EMPTYPAIR, "") + err := (*States)(nil).CanTradePair(currency.EMPTYPAIR, asset.Empty) if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).CanTradePair(currency.EMPTYPAIR, "") + err = (&States{}).CanTradePair(currency.EMPTYPAIR, asset.Empty) if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } cp := currency.NewPair(currency.BTC, currency.USD) - err = (&States{}).CanTradePair(cp, "") + err = (&States{}).CanTradePair(cp, asset.Empty) if !errors.Is(err, asset.ErrNotSupported) { t.Fatalf("received: %v, but expected: %v", err, asset.ErrNotSupported) } @@ -115,11 +115,11 @@ func TestCanTradePair(t *testing.T) { func TestStatesCanTrade(t *testing.T) { t.Parallel() - err := (*States)(nil).CanTrade(currency.EMPTYCODE, "") + err := (*States)(nil).CanTrade(currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).CanTrade(currency.EMPTYCODE, "") + err = (&States{}).CanTrade(currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } @@ -127,11 +127,11 @@ func TestStatesCanTrade(t *testing.T) { func TestStatesCanWithdraw(t *testing.T) { t.Parallel() - err := (*States)(nil).CanWithdraw(currency.EMPTYCODE, "") + err := (*States)(nil).CanWithdraw(currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).CanWithdraw(currency.EMPTYCODE, "") + err = (&States{}).CanWithdraw(currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } @@ -161,11 +161,11 @@ func TestStatesCanWithdraw(t *testing.T) { func TestStatesCanDeposit(t *testing.T) { t.Parallel() - err := (*States)(nil).CanDeposit(currency.EMPTYCODE, "") + err := (*States)(nil).CanDeposit(currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).CanDeposit(currency.EMPTYCODE, "") + err = (&States{}).CanDeposit(currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } @@ -195,12 +195,12 @@ func TestStatesCanDeposit(t *testing.T) { func TestStatesUpdateAll(t *testing.T) { t.Parallel() - err := (*States)(nil).UpdateAll("", nil) + err := (*States)(nil).UpdateAll(asset.Empty, nil) if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).UpdateAll("", nil) + err = (&States{}).UpdateAll(asset.Empty, nil) if !errors.Is(err, asset.ErrNotSupported) { t.Fatalf("received: %v, but expected: %v", err, asset.ErrNotSupported) } @@ -246,17 +246,17 @@ func TestStatesUpdateAll(t *testing.T) { func TestStatesUpdate(t *testing.T) { t.Parallel() - err := (*States)(nil).Update(currency.EMPTYCODE, "", Options{}) + err := (*States)(nil).Update(currency.EMPTYCODE, asset.Empty, Options{}) if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).Update(currency.EMPTYCODE, "", Options{}) + err = (&States{}).Update(currency.EMPTYCODE, asset.Empty, Options{}) if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } - err = (&States{}).Update(currency.BTC, "", Options{}) + err = (&States{}).Update(currency.BTC, asset.Empty, Options{}) if !errors.Is(err, asset.ErrNotSupported) { t.Fatalf("received: %v, but expected: %v", err, asset.ErrNotSupported) } @@ -273,17 +273,17 @@ func TestStatesUpdate(t *testing.T) { func TestStatesGet(t *testing.T) { t.Parallel() - _, err := (*States)(nil).Get(currency.EMPTYCODE, "") + _, err := (*States)(nil).Get(currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - _, err = (&States{}).Get(currency.EMPTYCODE, "") + _, err = (&States{}).Get(currency.EMPTYCODE, asset.Empty) if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } - _, err = (&States{}).Get(currency.BTC, "") + _, err = (&States{}).Get(currency.BTC, asset.Empty) if !errors.Is(err, asset.ErrNotSupported) { t.Fatalf("received: %v, but expected: %v", err, asset.ErrNotSupported) } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 0bbc5dde..96b97ee4 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -165,13 +165,13 @@ func (b *Base) GetPairAssetType(c currency.Pair) (asset.Item, error) { for i := range assetTypes { avail, err := b.GetAvailablePairs(assetTypes[i]) if err != nil { - return "", err + return asset.Empty, err } if avail.Contains(c, true) { return assetTypes[i], nil } } - return "", errors.New("asset type not associated with currency pair") + return asset.Empty, errors.New("asset type not associated with currency pair") } // GetClientBankAccounts returns banking details associated with @@ -366,7 +366,7 @@ func (b *Base) GetRequestFormattedPairAndAssetType(p string) (currency.Pair, ass } } } - return currency.EMPTYPAIR, "", fmt.Errorf("%s %w", p, currency.ErrPairNotFound) + return currency.EMPTYPAIR, asset.Empty, fmt.Errorf("%s %w", p, currency.ErrPairNotFound) } // GetAvailablePairs is a method that returns the available currency pairs diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index 04cef071..bf9fab49 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -1643,7 +1643,7 @@ func TestStoreAssetPairFormat(t *testing.T) { Config: &config.Exchange{Name: "kitties"}, } - err := b.StoreAssetPairFormat(asset.Item(""), currency.PairStore{}) + err := b.StoreAssetPairFormat(asset.Empty, currency.PairStore{}) if err == nil { t.Error("error cannot be nil") } @@ -1679,12 +1679,12 @@ func TestSetGlobalPairsManager(t *testing.T) { Config: &config.Exchange{Name: "kitties"}, } - err := b.SetGlobalPairsManager(nil, nil, "") + err := b.SetGlobalPairsManager(nil, nil, asset.Empty) if err == nil { t.Error("error cannot be nil") } - err = b.SetGlobalPairsManager(¤cy.PairFormat{Uppercase: true}, nil, "") + err = b.SetGlobalPairsManager(¤cy.PairFormat{Uppercase: true}, nil, asset.Empty) if err == nil { t.Error("error cannot be nil") } @@ -1696,7 +1696,7 @@ func TestSetGlobalPairsManager(t *testing.T) { } err = b.SetGlobalPairsManager(¤cy.PairFormat{Uppercase: true}, - ¤cy.PairFormat{Uppercase: true}, "") + ¤cy.PairFormat{Uppercase: true}, asset.Empty) if err == nil { t.Error("error cannot be nil") } diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 4ef74f38..3d3f80b3 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1736,7 +1736,7 @@ func TestStakeRequest(t *testing.T) { func TestUpdateOrderExecutionLimits(t *testing.T) { t.Parallel() - err := f.UpdateOrderExecutionLimits(context.Background(), "") + err := f.UpdateOrderExecutionLimits(context.Background(), asset.Empty) if err != nil { t.Fatal(err) } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 5b358318..2b1031cd 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -243,7 +243,7 @@ func (f *FTX) Run() { f.PrintEnabledPairs() } - err := f.UpdateOrderExecutionLimits(context.TODO(), "") + err := f.UpdateOrderExecutionLimits(context.TODO(), asset.Empty) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to set exchange order execution limits. Err: %v", diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index 42029325..044b414c 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -604,7 +604,7 @@ func (g *Gateio) GetOrderInfo(ctx context.Context, orderID string, pair currency return orderDetail, errors.New("failed to get open orders") } - if assetType == "" { + if assetType == asset.Empty { assetType = asset.Spot } diff --git a/exchanges/okgroup/okgroup_websocket.go b/exchanges/okgroup/okgroup_websocket.go index bb0e908b..502a063f 100644 --- a/exchanges/okgroup/okgroup_websocket.go +++ b/exchanges/okgroup/okgroup_websocket.go @@ -1060,6 +1060,6 @@ func (o *OKGroup) GetAssetTypeFromTableName(table string) asset.Item { log.Warnf(log.ExchangeSys, "%s unhandled asset type %s", o.Name, table[:assetIndex]) - return asset.Item(table[:assetIndex]) + return asset.Empty } } diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go index 3b82f199..7e99ec8b 100644 --- a/exchanges/okgroup/okgroup_wrapper.go +++ b/exchanges/okgroup/okgroup_wrapper.go @@ -397,15 +397,15 @@ func (o *OKGroup) CancelAllOrders(ctx context.Context, orderCancellation *order. // GetOrderInfo returns order information based on order ID func (o *OKGroup) GetOrderInfo(ctx context.Context, orderID string, pair currency.Pair, assetType asset.Item) (resp order.Detail, err error) { + if assetType != asset.Spot { + return resp, fmt.Errorf("%s %w", assetType, asset.ErrNotSupported) + } + mOrder, err := o.GetSpotOrder(ctx, GetSpotOrderRequest{OrderID: orderID}) if err != nil { return } - if assetType == "" { - assetType = asset.Spot - } - format, err := o.GetPairFormat(assetType, false) if err != nil { return resp, err diff --git a/exchanges/order/order_test.go b/exchanges/order/order_test.go index e25cd54f..f345fc08 100644 --- a/exchanges/order/order_test.go +++ b/exchanges/order/order_test.go @@ -726,7 +726,6 @@ func TestUpdateOrderFromModify(t *testing.T) { Type: "", Side: "", Status: "", - AssetType: "", Date: time.Time{}, LastUpdated: time.Time{}, Pair: currency.EMPTYPAIR, @@ -763,7 +762,7 @@ func TestUpdateOrderFromModify(t *testing.T) { Type: "1", Side: "1", Status: "1", - AssetType: "1", + AssetType: 1, LastUpdated: updated, Pair: pair, Trades: []TradeHistory{}, @@ -836,7 +835,7 @@ func TestUpdateOrderFromModify(t *testing.T) { if od.Status != "1" { t.Error("Failed to update") } - if od.AssetType != "1" { + if od.AssetType != 1 { t.Error("Failed to update") } if od.LastUpdated != updated { @@ -918,7 +917,6 @@ func TestUpdateOrderFromDetail(t *testing.T) { Type: "", Side: "", Status: "", - AssetType: "", Date: time.Time{}, LastUpdated: time.Time{}, Pair: currency.EMPTYPAIR, @@ -955,7 +953,7 @@ func TestUpdateOrderFromDetail(t *testing.T) { Type: "1", Side: "1", Status: "1", - AssetType: "1", + AssetType: 1, LastUpdated: updated, Pair: pair, Trades: []TradeHistory{}, @@ -1028,7 +1026,7 @@ func TestUpdateOrderFromDetail(t *testing.T) { if od.Status != "1" { t.Error("Failed to update") } - if od.AssetType != "1" { + if od.AssetType != 1 { t.Error("Failed to update") } if od.LastUpdated != updated { diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index 2fd1420d..a00da94b 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -10,6 +10,7 @@ import ( "github.com/gofrs/uuid" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/validate" ) @@ -25,7 +26,7 @@ func (s *Submit) Validate(opt ...validate.Checker) error { return ErrPairIsEmpty } - if s.AssetType == "" { + if s.AssetType == asset.Empty { return ErrAssetNotSet } @@ -156,7 +157,7 @@ func (d *Detail) UpdateOrderFromDetail(m *Detail) { d.Status = m.Status updated = true } - if m.AssetType != "" && m.AssetType != d.AssetType { + if m.AssetType != asset.Empty && m.AssetType != d.AssetType { d.AssetType = m.AssetType updated = true } @@ -320,7 +321,7 @@ func (d *Detail) UpdateOrderFromModify(m *Modify) { d.Status = m.Status updated = true } - if m.AssetType != "" && m.AssetType != d.AssetType { + if m.AssetType != asset.Empty && m.AssetType != d.AssetType { d.AssetType = m.AssetType updated = true } @@ -391,7 +392,7 @@ func (d *Detail) MatchFilter(f *Filter) bool { if f.Exchange != "" && !strings.EqualFold(d.Exchange, f.Exchange) { return false } - if f.AssetType != "" && d.AssetType != f.AssetType { + if f.AssetType != asset.Empty && d.AssetType != f.AssetType { return false } if !f.Pair.IsEmpty() && !d.Pair.Equal(f.Pair) { @@ -879,7 +880,7 @@ func (c *Cancel) PairAssetRequired() validate.Checker { return ErrPairIsEmpty } - if c.AssetType == "" { + if c.AssetType == asset.Empty { return ErrAssetNotSet } return nil diff --git a/exchanges/orderbook/depth_test.go b/exchanges/orderbook/depth_test.go index 0db7d199..adbb0f61 100644 --- a/exchanges/orderbook/depth_test.go +++ b/exchanges/orderbook/depth_test.go @@ -44,7 +44,7 @@ func TestRetrieve(t *testing.T) { d.options = options{ exchange: "THE BIG ONE!!!!!!", pair: currency.NewPair(currency.THETA, currency.USD), - asset: "Silly asset", + asset: asset.DownsideProfitContract, lastUpdated: time.Now(), lastUpdateID: 1337, priceDuplication: true, diff --git a/exchanges/orderbook/orderbook_test.go b/exchanges/orderbook/orderbook_test.go index 46b4a0a9..3075e6d8 100644 --- a/exchanges/orderbook/orderbook_test.go +++ b/exchanges/orderbook/orderbook_test.go @@ -229,7 +229,7 @@ func TestGetOrderbook(t *testing.T) { t.Error(err) } - _, err = Get("Exchange", newCurrency, "meowCats") + _, err = Get("Exchange", newCurrency, asset.Empty) if err == nil { t.Error("error cannot be nil") } @@ -288,7 +288,7 @@ func TestGetDepth(t *testing.T) { t.Error(err) } - _, err = GetDepth("Exchange", newCurrency, "meowCats") + _, err = GetDepth("Exchange", newCurrency, asset.Empty) if !errors.Is(err, errCannotFindOrderbook) { t.Fatalf("expecting %s error but received %v", errCannotFindOrderbook, err) } @@ -307,7 +307,7 @@ func TestDeployDepth(t *testing.T) { if !errors.Is(err, errPairNotSet) { t.Fatalf("expecting %s error but received %v", errPairNotSet, err) } - _, err = DeployDepth("test", c, "") + _, err = DeployDepth("test", c, asset.Empty) if !errors.Is(err, errAssetTypeNotSet) { t.Fatalf("expecting %s error but received %v", errAssetTypeNotSet, err) } @@ -434,13 +434,13 @@ func TestProcessOrderbook(t *testing.T) { } base.Asks = []Item{{Price: 200, Amount: 200}} - base.Asset = "monthly" + base.Asset = asset.Spot err = base.Process() if err != nil { t.Error("Process() error", err) } - result, err = Get("ProcessOrderbook", c, "monthly") + result, err = Get("ProcessOrderbook", c, asset.Spot) if err != nil { t.Fatal("TestProcessOrderbook failed to retrieve new orderbook") } @@ -452,13 +452,13 @@ func TestProcessOrderbook(t *testing.T) { base.Bids = []Item{{Price: 420, Amount: 200}} base.Exchange = "Blah" - base.Asset = "quarterly" + base.Asset = asset.CoinMarginedFutures err = base.Process() if err != nil { t.Error("Process() error", err) } - _, err = Get("Blah", c, "quarterly") + _, err = Get("Blah", c, asset.CoinMarginedFutures) if err != nil { t.Fatal("TestProcessOrderbook failed to create new orderbook") } diff --git a/exchanges/stats/stats.go b/exchanges/stats/stats.go index 9668a481..be9bf3e7 100644 --- a/exchanges/stats/stats.go +++ b/exchanges/stats/stats.go @@ -53,7 +53,7 @@ func (b ByVolume) Swap(i, j int) { // Add adds or updates the item stats func Add(exchange string, p currency.Pair, a asset.Item, price, volume float64) error { if exchange == "" || - a == "" || + a == asset.Empty || price == 0 || volume == 0 || p.Base.IsEmpty() || diff --git a/exchanges/stats/stats_test.go b/exchanges/stats/stats_test.go index ace39ad9..538dae50 100644 --- a/exchanges/stats/stats_test.go +++ b/exchanges/stats/stats_test.go @@ -127,7 +127,7 @@ func TestAdd(t *testing.T) { t.Error("stats Add did not add exchange info.") } - err = Add("", p, "", 0, 0) + err = Add("", p, asset.Empty, 0, 0) if err == nil { t.Fatal("error cannot be nil") } diff --git a/exchanges/ticker/ticker.go b/exchanges/ticker/ticker.go index 7057f5b0..5fb82839 100644 --- a/exchanges/ticker/ticker.go +++ b/exchanges/ticker/ticker.go @@ -129,7 +129,7 @@ func ProcessTicker(p *Price) error { return fmt.Errorf("%s %s", p.ExchangeName, errPairNotSet) } - if p.AssetType == "" { + if p.AssetType == asset.Empty { return fmt.Errorf("%s %s %s", p.ExchangeName, p.Pair, diff --git a/exchanges/ticker/ticker_test.go b/exchanges/ticker/ticker_test.go index e5627806..2ab1ac4e 100644 --- a/exchanges/ticker/ticker_test.go +++ b/exchanges/ticker/ticker_test.go @@ -29,7 +29,7 @@ func TestMain(m *testing.M) { var cpyMux *dispatch.Mux func TestSubscribeTicker(t *testing.T) { - _, err := SubscribeTicker("", currency.EMPTYPAIR, asset.Item("")) + _, err := SubscribeTicker("", currency.EMPTYPAIR, asset.Empty) if err == nil { t.Error("error cannot be nil") } @@ -68,7 +68,7 @@ func TestSubscribeTicker(t *testing.T) { err = ProcessTicker(&Price{ Pair: sillyP, ExchangeName: "subscribetest", - AssetType: "silly", + AssetType: asset.DownsideProfitContract, }) if err == nil { t.Error("error cannot be nil") @@ -166,13 +166,13 @@ func TestGetTicker(t *testing.T) { priceStruct.PriceATH = 9001 priceStruct.Pair.Base = currency.ETH - priceStruct.AssetType = "futures_3m" + priceStruct.AssetType = asset.DownsideProfitContract err = ProcessTicker(&priceStruct) if err != nil { t.Fatal("ProcessTicker error", err) } - tickerPrice, err = GetTicker("bitfinex", newPair, "futures_3m") + tickerPrice, err = GetTicker("bitfinex", newPair, asset.DownsideProfitContract) if err != nil { t.Errorf("Ticker GetTicker init error: %s", err) } @@ -181,12 +181,12 @@ func TestGetTicker(t *testing.T) { t.Error("ticker tickerPrice.PriceATH value is incorrect") } - _, err = GetTicker("bitfinex", newPair, "meowCats") + _, err = GetTicker("bitfinex", newPair, asset.UpsideProfitContract) if err == nil { t.Error("Ticker GetTicker error cannot be nil") } - priceStruct.AssetType = "meowCats" + priceStruct.AssetType = asset.UpsideProfitContract err = ProcessTicker(&priceStruct) if err != nil { t.Fatal("ProcessTicker error", err) diff --git a/exchanges/trade/trade.go b/exchanges/trade/trade.go index 86ed937a..a789689c 100644 --- a/exchanges/trade/trade.go +++ b/exchanges/trade/trade.go @@ -202,24 +202,22 @@ func tradeToSQLData(trades ...Data) ([]tradesql.Data, error) { } // SQLDataToTrade converts sql data to glorious trade data -func SQLDataToTrade(dbTrades ...tradesql.Data) (result []Data, err error) { +func SQLDataToTrade(dbTrades ...tradesql.Data) ([]Data, error) { + result := make([]Data, len(dbTrades)) for i := range dbTrades { - var cp currency.Pair - cp, err = currency.NewPairFromStrings(dbTrades[i].Base, dbTrades[i].Quote) + cp, err := currency.NewPairFromStrings(dbTrades[i].Base, dbTrades[i].Quote) if err != nil { return nil, err } - cp = cp.Upper() - var a = asset.Item(dbTrades[i].AssetType) - if !a.IsValid() { - return nil, fmt.Errorf("invalid asset type %v", a) - } - var s order.Side - s, err = order.StringToOrderSide(dbTrades[i].Side) + a, err := asset.New(dbTrades[i].AssetType) if err != nil { return nil, err } - result = append(result, Data{ + s, err := order.StringToOrderSide(dbTrades[i].Side) + if err != nil { + return nil, err + } + result[i] = Data{ ID: uuid.FromStringOrNil(dbTrades[i].ID), Timestamp: dbTrades[i].Timestamp.UTC(), Exchange: dbTrades[i].Exchange, @@ -228,7 +226,7 @@ func SQLDataToTrade(dbTrades ...tradesql.Data) (result []Data, err error) { Price: dbTrades[i].Price, Amount: dbTrades[i].Amount, Side: s, - }) + } } return result, nil } diff --git a/exchanges/zb/zb_test.go b/exchanges/zb/zb_test.go index 2314d5c6..3c47d0c8 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -979,11 +979,11 @@ func Test_FormatExchangeKlineInterval(t *testing.T) { } func TestValidateCandlesRequest(t *testing.T) { - _, err := z.validateCandlesRequest(currency.EMPTYPAIR, "", time.Time{}, time.Time{}, kline.Interval(-1)) + _, err := z.validateCandlesRequest(currency.EMPTYPAIR, asset.Empty, time.Time{}, time.Time{}, kline.Interval(-1)) if !errors.Is(err, common.ErrDateUnset) { t.Error(err) } - _, err = z.validateCandlesRequest(currency.EMPTYPAIR, "", time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Time{}, kline.Interval(-1)) + _, err = z.validateCandlesRequest(currency.EMPTYPAIR, asset.Empty, time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Time{}, kline.Interval(-1)) if !errors.Is(err, common.ErrDateUnset) { t.Error(err) } diff --git a/gctscript/wrappers/validator/validator.go b/gctscript/wrappers/validator/validator.go index 48a61d56..c01a9cff 100644 --- a/gctscript/wrappers/validator/validator.go +++ b/gctscript/wrappers/validator/validator.go @@ -180,7 +180,7 @@ func (w Wrapper) CancelOrder(ctx context.Context, exch, orderid string, cp curre if !cp.IsEmpty() && cp.IsInvalid() { return false, errTestFailed } - if a != "" && !a.IsValid() { + if a != asset.Empty && !a.IsValid() { return false, errTestFailed } return true, nil diff --git a/gctscript/wrappers/validator/validator_test.go b/gctscript/wrappers/validator/validator_test.go index a9722fbe..0fb5760e 100644 --- a/gctscript/wrappers/validator/validator_test.go +++ b/gctscript/wrappers/validator/validator_test.go @@ -105,7 +105,7 @@ func TestWrapper_CancelOrder(t *testing.T) { } _, err = testWrapper.CancelOrder(context.Background(), - exchName, orderID, cp, "") + exchName, orderID, cp, asset.Empty) if err != nil { t.Error(err) }