mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-08 23:16:54 +00:00
Config: AssetEnabled upgrade (#1735)
* Config: Move assetEnabled upgrade to Version management * Assets: Do not error on asset not enabled, or disabled This became more messy with Disabling something that's defaulted to disabled. Taking an idealogical stance against erroring that what you want to have done is already done. * CurrencyManager: Set AssetEnabled when StorePairs(enabled) * RPCServer: Fix tests expecting StoreAssetPairFormat to enable the asset Also assertifies * Bitfinex: Fix tests for MarginFunding subs * GCTWrapper: Improve TestMain clarity * BTSE: Add futures to testconfig * Exchanges: Rename StoreAssetPairStore Previously we were calling it "Format", but accepting everything from the PairStore. We were also defaulting to turning the Asset on. Now callers need to get their AssetEnabled set as they want it, so there's no magic This change also moves responsibility for error wrapping outside to the caller. * Config: AssetEnabled upgrade should respect assetTypes Previously we ignored the field and just turned on everything. I think that was because we couldn't get at the old value. In either case, we have the option to do better, and respect the assetEnabled value * Config: Improve exchange config version upgrade error messages
This commit is contained in:
@@ -7,15 +7,12 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
)
|
||||
|
||||
// Public errors
|
||||
var (
|
||||
ErrAssetAlreadyEnabled = errors.New("asset already enabled")
|
||||
ErrAssetIsNil = errors.New("asset is nil")
|
||||
ErrAssetNotFound = errors.New("asset type not found in pair store")
|
||||
ErrPairAlreadyEnabled = errors.New("pair already enabled")
|
||||
ErrPairFormatIsNil = errors.New("pair format is nil")
|
||||
@@ -38,7 +35,7 @@ func (p *PairsManager) GetAssetTypes(enabled bool) asset.Items {
|
||||
defer p.mutex.RUnlock()
|
||||
assetTypes := make(asset.Items, 0, len(p.Pairs))
|
||||
for k, ps := range p.Pairs {
|
||||
if enabled && (ps.AssetEnabled == nil || !*ps.AssetEnabled) {
|
||||
if enabled && !ps.AssetEnabled {
|
||||
continue
|
||||
}
|
||||
assetTypes = append(assetTypes, k)
|
||||
@@ -205,8 +202,11 @@ func (p *PairsManager) GetFormat(a asset.Item, request bool) (PairFormat, error)
|
||||
return *pFmt, nil
|
||||
}
|
||||
|
||||
// StorePairs stores a list of pairs based on the asset type and whether
|
||||
// they're enabled or not
|
||||
// StorePairs stores a list of pairs for an asset type
|
||||
// If enabled is true:
|
||||
// * AssetEnabled is set true if the pair list is not empty
|
||||
// * pairs replace the Enabled pairs
|
||||
// * pairs are added to Available pairs
|
||||
func (p *PairsManager) StorePairs(a asset.Item, pairs Pairs, enabled bool) error {
|
||||
if !a.IsValid() {
|
||||
return fmt.Errorf("%s %w", a, asset.ErrNotSupported)
|
||||
@@ -226,6 +226,10 @@ func (p *PairsManager) StorePairs(a asset.Item, pairs Pairs, enabled bool) error
|
||||
}
|
||||
|
||||
if enabled {
|
||||
if len(pairs) != 0 {
|
||||
pairStore.AssetEnabled = true
|
||||
pairStore.Available.Add(pairs...)
|
||||
}
|
||||
pairStore.Enabled = slices.Clone(pairs)
|
||||
} else {
|
||||
pairStore.Available = slices.Clone(pairs)
|
||||
@@ -246,9 +250,7 @@ func (p *PairsManager) EnsureOnePairEnabled() (Pair, asset.Item, error) {
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
for _, v := range p.Pairs {
|
||||
if v.AssetEnabled == nil ||
|
||||
!*v.AssetEnabled ||
|
||||
len(v.Available) == 0 {
|
||||
if !v.AssetEnabled || len(v.Available) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(v.Enabled) > 0 {
|
||||
@@ -256,9 +258,7 @@ func (p *PairsManager) EnsureOnePairEnabled() (Pair, asset.Item, error) {
|
||||
}
|
||||
}
|
||||
for k, v := range p.Pairs {
|
||||
if v.AssetEnabled == nil ||
|
||||
!*v.AssetEnabled ||
|
||||
len(v.Available) == 0 {
|
||||
if !v.AssetEnabled || len(v.Available) == 0 {
|
||||
continue
|
||||
}
|
||||
rp, err := v.Available.GetRandomPair()
|
||||
@@ -324,8 +324,7 @@ func (p *PairsManager) EnablePair(a asset.Item, pair Pair) error {
|
||||
}
|
||||
|
||||
if !pairStore.Available.Contains(pair, true) {
|
||||
return fmt.Errorf("%s %w in the list of available pairs",
|
||||
pair, ErrPairNotFound)
|
||||
return fmt.Errorf("%s %w in the list of available pairs", pair, ErrPairNotFound)
|
||||
}
|
||||
pairStore.Enabled = pairStore.Enabled.Add(pair)
|
||||
return nil
|
||||
@@ -348,10 +347,7 @@ func (p *PairsManager) IsPairAvailable(pair Pair, a asset.Item) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if pairStore.AssetEnabled == nil {
|
||||
return false, fmt.Errorf("%s %w", a, ErrAssetIsNil)
|
||||
}
|
||||
return *pairStore.AssetEnabled && pairStore.Available.Contains(pair, true), nil
|
||||
return pairStore.AssetEnabled && pairStore.Available.Contains(pair, true), nil
|
||||
}
|
||||
|
||||
// IsPairEnabled checks if a pair is enabled for an enabled asset type
|
||||
@@ -371,10 +367,7 @@ func (p *PairsManager) IsPairEnabled(pair Pair, a asset.Item) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if pairStore.AssetEnabled == nil {
|
||||
return false, fmt.Errorf("%s %w", a, ErrAssetIsNil)
|
||||
}
|
||||
return *pairStore.AssetEnabled && pairStore.Enabled.Contains(pair, true), nil
|
||||
return pairStore.AssetEnabled && pairStore.Enabled.Contains(pair, true), nil
|
||||
}
|
||||
|
||||
// IsAssetEnabled checks to see if an asset is enabled
|
||||
@@ -391,11 +384,7 @@ func (p *PairsManager) IsAssetEnabled(a asset.Item) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if pairStore.AssetEnabled == nil {
|
||||
return fmt.Errorf("%s %w", a, ErrAssetIsNil)
|
||||
}
|
||||
|
||||
if !*pairStore.AssetEnabled {
|
||||
if !pairStore.AssetEnabled {
|
||||
return fmt.Errorf("%s %w", a, asset.ErrNotEnabled)
|
||||
}
|
||||
return nil
|
||||
@@ -424,18 +413,8 @@ func (p *PairsManager) SetAssetEnabled(a asset.Item, enabled bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if pairStore.AssetEnabled == nil {
|
||||
pairStore.AssetEnabled = convert.BoolPtr(enabled)
|
||||
return nil
|
||||
}
|
||||
pairStore.AssetEnabled = enabled
|
||||
|
||||
if !*pairStore.AssetEnabled && !enabled {
|
||||
return errors.New("asset already disabled")
|
||||
} else if *pairStore.AssetEnabled && enabled {
|
||||
return ErrAssetAlreadyEnabled
|
||||
}
|
||||
|
||||
*pairStore.AssetEnabled = enabled
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -547,13 +526,8 @@ func (ps *PairStore) clone() *PairStore {
|
||||
return nil
|
||||
}
|
||||
|
||||
var assetEnabled *bool
|
||||
if ps.AssetEnabled != nil {
|
||||
assetEnabled = convert.BoolPtr(*ps.AssetEnabled)
|
||||
}
|
||||
|
||||
return &PairStore{
|
||||
AssetEnabled: assetEnabled,
|
||||
AssetEnabled: ps.AssetEnabled,
|
||||
Enabled: slices.Clone(ps.Enabled),
|
||||
Available: slices.Clone(ps.Available),
|
||||
RequestFormat: ps.RequestFormat.clone(),
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
)
|
||||
@@ -24,7 +23,7 @@ func initTest(t *testing.T) *PairsManager {
|
||||
}
|
||||
|
||||
spot := &PairStore{
|
||||
AssetEnabled: convert.BoolPtr(true),
|
||||
AssetEnabled: true,
|
||||
Available: spotAvailable,
|
||||
Enabled: spotEnabled,
|
||||
RequestFormat: &PairFormat{Uppercase: true},
|
||||
@@ -32,7 +31,7 @@ func initTest(t *testing.T) *PairsManager {
|
||||
}
|
||||
|
||||
futures := &PairStore{
|
||||
AssetEnabled: convert.BoolPtr(false),
|
||||
AssetEnabled: false,
|
||||
Available: spotAvailable,
|
||||
Enabled: spotEnabled,
|
||||
RequestFormat: &PairFormat{Uppercase: true},
|
||||
@@ -463,72 +462,54 @@ func TestEnablePair(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAssetEnabled_SetAssetEnabled(t *testing.T) {
|
||||
func TestAssetEnabled(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := initTest(t)
|
||||
|
||||
err := p.IsAssetEnabled(asset.Empty)
|
||||
if !errors.Is(err, asset.ErrNotSupported) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported)
|
||||
}
|
||||
assert.ErrorIs(t, err, asset.ErrNotSupported)
|
||||
|
||||
p.Pairs = nil
|
||||
|
||||
// Test enabling a pair when the pair manager is not initialised
|
||||
err = p.IsAssetEnabled(asset.Spot)
|
||||
if err == nil {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
assert.ErrorIs(t, err, ErrPairManagerNotInitialised)
|
||||
|
||||
err = p.SetAssetEnabled(asset.Spot, true)
|
||||
if err == nil {
|
||||
t.Fatal("unexpected result")
|
||||
}
|
||||
assert.ErrorIs(t, err, ErrPairManagerNotInitialised)
|
||||
|
||||
// Test asset type which doesn't exist
|
||||
p = initTest(t)
|
||||
|
||||
p.Pairs[asset.Spot].AssetEnabled = nil
|
||||
p.Pairs[asset.Spot].AssetEnabled = false
|
||||
|
||||
err = p.IsAssetEnabled(asset.Spot)
|
||||
if err == nil {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
assert.ErrorIs(t, err, asset.ErrNotEnabled)
|
||||
|
||||
err = p.SetAssetEnabled(asset.Spot, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = p.SetAssetEnabled(asset.Spot, false)
|
||||
if err == nil {
|
||||
t.Fatal("unexpected result")
|
||||
}
|
||||
assert.NoError(t, err, "Setting to disabled twice should not error")
|
||||
|
||||
err = p.IsAssetEnabled(asset.Spot)
|
||||
if err == nil {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
assert.ErrorIs(t, err, asset.ErrNotEnabled)
|
||||
|
||||
err = p.SetAssetEnabled(asset.Spot, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = p.SetAssetEnabled(asset.Spot, true)
|
||||
if err == nil {
|
||||
t.Fatal("unexpected result")
|
||||
}
|
||||
assert.NoError(t, err, "Setting to enabled twice should not error")
|
||||
|
||||
err = p.IsAssetEnabled(asset.Spot)
|
||||
if err != nil {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
assert.NoError(t, err, "IsAssetEnabled should not error")
|
||||
}
|
||||
|
||||
// TestFullStoreUnmarshalMarshal tests json Mashal and Unmarshal
|
||||
func TestFullStoreUnmarshalMarshal(t *testing.T) {
|
||||
t.Parallel()
|
||||
var um = make(FullStore)
|
||||
um[asset.Spot] = &PairStore{AssetEnabled: convert.BoolPtr(true)}
|
||||
um[asset.Spot] = &PairStore{AssetEnabled: true}
|
||||
|
||||
data, err := json.Marshal(um)
|
||||
if err != nil {
|
||||
@@ -591,7 +572,7 @@ func TestIsPairAvailable(t *testing.T) {
|
||||
|
||||
pm.Pairs[asset.PerpetualSwap] = &PairStore{}
|
||||
_, err = pm.IsPairAvailable(cp, asset.PerpetualSwap)
|
||||
assert.ErrorIs(t, err, ErrAssetIsNil, "Should error when store AssetEnabled is nil")
|
||||
require.NoError(t, err, "Must not error when store is empty")
|
||||
|
||||
_, err = pm.IsPairAvailable(EMPTYPAIR, asset.PerpetualSwap)
|
||||
assert.ErrorIs(t, err, ErrCurrencyPairEmpty, "Should error when currency pair is empty")
|
||||
@@ -602,62 +583,35 @@ func TestIsPairEnabled(t *testing.T) {
|
||||
pm := initTest(t)
|
||||
cp := NewPairWithDelimiter("BTC", "USD", "-")
|
||||
enabled, err := pm.IsPairEnabled(cp, asset.Spot)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
t.Fatal("expected pair to be enabled")
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.True(t, enabled, "IsPairEnabled should return true when pair is enabled")
|
||||
|
||||
enabled, err = pm.IsPairEnabled(NewPair(SAFE, MOONRISE), asset.Spot)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if enabled {
|
||||
t.Fatal("expected pair to be disabled")
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.False(t, enabled, "IsPairEnabled should return false when pair does not exist")
|
||||
|
||||
enabled, err = pm.IsPairEnabled(cp, asset.Futures)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if enabled {
|
||||
t.Fatal("expected pair to be disabled because asset type is not enabled")
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.False(t, enabled, "IsPairEnabled should return false when asset not enabled")
|
||||
|
||||
cp = NewPairWithDelimiter("XRP", "DOGE", "-")
|
||||
enabled, err = pm.IsPairEnabled(cp, asset.Spot)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if enabled {
|
||||
t.Fatal("expected pair to be disabled because pair not found in enabled list")
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.False(t, enabled, "IsPairEnabled should return false when pair not in enabled list")
|
||||
|
||||
_, err = pm.IsPairEnabled(cp, asset.PerpetualSwap)
|
||||
if !errors.Is(err, ErrAssetNotFound) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, ErrAssetNotFound)
|
||||
}
|
||||
assert.ErrorIs(t, err, ErrAssetNotFound)
|
||||
|
||||
_, err = pm.IsPairEnabled(cp, asset.Item(1337))
|
||||
if !errors.Is(err, asset.ErrNotSupported) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported)
|
||||
}
|
||||
assert.ErrorIs(t, err, asset.ErrNotSupported)
|
||||
|
||||
pm.Pairs[asset.PerpetualSwap] = &PairStore{}
|
||||
_, err = pm.IsPairEnabled(cp, asset.PerpetualSwap)
|
||||
if !errors.Is(err, ErrAssetIsNil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, ErrAssetIsNil)
|
||||
}
|
||||
enabled, err = pm.IsPairEnabled(cp, asset.PerpetualSwap)
|
||||
require.NoError(t, err, "Must not error when store is empty")
|
||||
assert.False(t, enabled, "Should return false when store is empty")
|
||||
|
||||
_, err = pm.IsPairEnabled(EMPTYPAIR, asset.PerpetualSwap)
|
||||
if !errors.Is(err, ErrCurrencyPairEmpty) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, ErrCurrencyPairEmpty)
|
||||
}
|
||||
assert.ErrorIs(t, err, ErrCurrencyPairEmpty)
|
||||
}
|
||||
|
||||
func TestEnsureOnePairEnabled(t *testing.T) {
|
||||
@@ -667,7 +621,7 @@ func TestEnsureOnePairEnabled(t *testing.T) {
|
||||
Pairs: map[asset.Item]*PairStore{
|
||||
asset.Futures: {},
|
||||
asset.Spot: {
|
||||
AssetEnabled: convert.BoolPtr(true),
|
||||
AssetEnabled: true,
|
||||
Available: []Pair{
|
||||
p,
|
||||
},
|
||||
@@ -700,13 +654,13 @@ func TestEnsureOnePairEnabled(t *testing.T) {
|
||||
pm = PairsManager{
|
||||
Pairs: map[asset.Item]*PairStore{
|
||||
asset.Futures: {
|
||||
AssetEnabled: convert.BoolPtr(true),
|
||||
AssetEnabled: true,
|
||||
Available: []Pair{
|
||||
NewPair(BTC, USDC),
|
||||
},
|
||||
},
|
||||
asset.Spot: {
|
||||
AssetEnabled: convert.BoolPtr(true),
|
||||
AssetEnabled: true,
|
||||
Enabled: []Pair{
|
||||
p,
|
||||
},
|
||||
@@ -733,11 +687,11 @@ func TestEnsureOnePairEnabled(t *testing.T) {
|
||||
pm = PairsManager{
|
||||
Pairs: map[asset.Item]*PairStore{
|
||||
asset.Futures: {
|
||||
AssetEnabled: convert.BoolPtr(true),
|
||||
AssetEnabled: true,
|
||||
Available: []Pair{p},
|
||||
},
|
||||
asset.Options: {
|
||||
AssetEnabled: convert.BoolPtr(true),
|
||||
AssetEnabled: true,
|
||||
Available: []Pair{},
|
||||
},
|
||||
},
|
||||
@@ -776,20 +730,20 @@ func TestLoad(t *testing.T) {
|
||||
RequestFormat: fmt2,
|
||||
Pairs: map[asset.Item]*PairStore{
|
||||
asset.Futures: {
|
||||
AssetEnabled: convert.BoolPtr(true),
|
||||
AssetEnabled: true,
|
||||
Available: []Pair{p},
|
||||
},
|
||||
asset.Options: {
|
||||
AssetEnabled: convert.BoolPtr(false),
|
||||
AssetEnabled: false,
|
||||
Available: []Pair{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
base.Load(&seed)
|
||||
assert.True(t, *base.Pairs[asset.Futures].AssetEnabled, "Futures AssetEnabled should be true")
|
||||
assert.True(t, base.Pairs[asset.Futures].AssetEnabled, "Futures AssetEnabled should be true")
|
||||
assert.True(t, base.Pairs[asset.Futures].Available.Contains(p, true), "Futures Available Pairs should contain BTCUSDT")
|
||||
assert.False(t, *base.Pairs[asset.Options].AssetEnabled, "Options AssetEnabled should be false")
|
||||
assert.False(t, base.Pairs[asset.Options].AssetEnabled, "Options AssetEnabled should be false")
|
||||
assert.Equal(t, tt, base.LastUpdated, "Last Updated should be correct")
|
||||
assert.Equal(t, fmt1.Uppercase, base.ConfigFormat.Uppercase, "ConfigFormat Uppercase should be correct")
|
||||
assert.Equal(t, fmt2.Delimiter, base.RequestFormat.Delimiter, "RequestFormat Delimiter should be correct")
|
||||
@@ -909,7 +863,7 @@ func TestIsAssetSupported(t *testing.T) {
|
||||
p := PairsManager{
|
||||
Pairs: FullStore{
|
||||
asset.Spot: {
|
||||
AssetEnabled: convert.BoolPtr(false),
|
||||
AssetEnabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ type FullStore map[asset.Item]*PairStore
|
||||
|
||||
// PairStore stores a currency pair store
|
||||
type PairStore struct {
|
||||
AssetEnabled *bool `json:"assetEnabled"`
|
||||
AssetEnabled bool `json:"assetEnabled"`
|
||||
Enabled Pairs `json:"enabled"`
|
||||
Available Pairs `json:"available"`
|
||||
RequestFormat *PairFormat `json:"requestFormat,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user