refactor: use canonical token-to-symbol maps for market lookup

Build canonical spot/future token maps with quote priority, unify cache refresh scheduling, and switch resolver/token tests to map-based token lookups.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 04:44:30 +07:00
parent 06c40ef30f
commit 711721c1ee
9 changed files with 296 additions and 108 deletions
+28 -27
View File
@@ -1,7 +1,7 @@
package binancex
import (
"reflect"
"strings"
"testing"
"me.thuanle/bbot/internal/configs/binance"
@@ -31,7 +31,25 @@ func (m *resolverMarketStub) GetAlphaToken(symbol string) (market.AlphaTokenInfo
func (m *resolverMarketStub) GetAlphaPrice(symbol string) (float64, bool) { return 0, false }
func (m *resolverMarketStub) IsSpotPair(symbol string) bool { return m.spotPairs[symbol] }
func (m *resolverMarketStub) IsFuturesPair(symbol string) bool { return m.futuresPairs[symbol] }
func (m *resolverMarketStub) RefreshTradingPairCache() error { return nil }
func (m *resolverMarketStub) GetSpotSymbolByToken(token string) (string, bool) {
token = strings.ToUpper(token)
for sym := range m.spotPairs {
if strings.ToUpper(Symbol2Token(sym)) == token {
return sym, true
}
}
return "", false
}
func (m *resolverMarketStub) GetFutureSymbolByToken(token string) (string, bool) {
token = strings.ToUpper(token)
for sym := range m.futuresPairs {
if strings.ToUpper(Symbol2Token(sym)) == token {
return sym, true
}
}
return "", false
}
func (m *resolverMarketStub) RefreshTradingPairCache() error { return nil }
func withResolverMarketStub(t *testing.T, marketStub *resolverMarketStub) {
t.Helper()
@@ -40,7 +58,7 @@ func withResolverMarketStub(t *testing.T, marketStub *resolverMarketStub) {
t.Cleanup(func() { data.Market = orig })
}
func TestToken2FutureSymbols_ResolvesPrefixAndSuffix(t *testing.T) {
func TestToken2FutureSymbols_ReturnsCanonicalFutureSymbol(t *testing.T) {
withResolverMarketStub(t, &resolverMarketStub{
futuresPairs: map[string]bool{
"1000PEPEUSDT": true,
@@ -49,27 +67,12 @@ func TestToken2FutureSymbols_ResolvesPrefixAndSuffix(t *testing.T) {
})
syms := Token2FutureSymbols("pepe")
if len(syms) == 0 {
t.Fatalf("expected future symbols for PEPE")
}
foundUSDT := false
foundUSDC := false
for _, sym := range syms {
if sym == "1000PEPEUSDT" {
foundUSDT = true
}
if sym == "1000PEPEUSDC" {
foundUSDC = true
}
}
if !foundUSDT || !foundUSDC {
t.Fatalf("expected both 1000PEPEUSDT and 1000PEPEUSDC, got %+v", syms)
if len(syms) != 1 || syms[0] != "1000PEPEUSDT" {
t.Fatalf("expected canonical [1000PEPEUSDT], got %+v", syms)
}
}
func TestToken2FutureSymbols_UsesDeterministicCandidateOrder(t *testing.T) {
func TestToken2FutureSymbols_UsesCanonicalQuotePriority(t *testing.T) {
withResolverMarketStub(t, &resolverMarketStub{
futuresPairs: map[string]bool{
"1000PEPEUSDT": true,
@@ -80,9 +83,8 @@ func TestToken2FutureSymbols_UsesDeterministicCandidateOrder(t *testing.T) {
})
syms := Token2FutureSymbols("pepe")
expected := []string{"1000PEPEUSDT", "1000PEPEUSDC", "PEPEUSDT", "PEPEUSDC"}
if !reflect.DeepEqual(syms, expected) {
t.Fatalf("expected deterministic order %+v, got %+v", expected, syms)
if len(syms) != 1 || syms[0] != "1000PEPEUSDT" {
t.Fatalf("expected canonical [1000PEPEUSDT], got %+v", syms)
}
}
@@ -92,9 +94,8 @@ func TestToken2FutureSymbols_ResolvesUSDCAbbreviation(t *testing.T) {
})
syms := Token2FutureSymbols("pepec")
expected := []string{"PEPEUSDC"}
if !reflect.DeepEqual(syms, expected) {
t.Fatalf("expected USDC abbreviation resolution %+v, got %+v", expected, syms)
if len(syms) != 1 || syms[0] != "PEPEUSDC" {
t.Fatalf("expected [PEPEUSDC], got %+v", syms)
}
}