82b0f2f1a8
Keep alpha token existence from list cache and fetch live alpha price via symbol ticker endpoint for richer token response accuracy. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
157 lines
4.4 KiB
Go
157 lines
4.4 KiB
Go
package commands
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"me.thuanle/bbot/internal/data"
|
|
"me.thuanle/bbot/internal/data/market"
|
|
)
|
|
|
|
func TestBuildRichTokenMessageInput_AllSources(t *testing.T) {
|
|
in := buildRichTokenMessageInput(buildRichTokenMessageArgs{
|
|
Token: "ETH",
|
|
SpotPrice: 3245,
|
|
HasSpot: true,
|
|
FuturePrice: 3251,
|
|
FundingRate: 0.000123,
|
|
FundingTimeMs: 1740000000000,
|
|
HasFuture: true,
|
|
AlphaPrice: 3248,
|
|
HasAlpha: true,
|
|
Alpha24h: -3.21,
|
|
HasAlpha24h: true,
|
|
MarginAPRPercent: 7.665,
|
|
HasMarginAPR: true,
|
|
})
|
|
|
|
if !in.HasSpot || !in.HasFuture || !in.HasAlpha || !in.HasAlpha24h || !in.HasMarginAPR {
|
|
t.Fatalf("expected all source flags true: %+v", in)
|
|
}
|
|
}
|
|
|
|
func TestBuildRichTokenMessageInput_OnlyAlpha(t *testing.T) {
|
|
in := buildRichTokenMessageInput(buildRichTokenMessageArgs{
|
|
Token: "ABC",
|
|
HasAlpha: true,
|
|
AlphaPrice: 0.1234,
|
|
HasAlpha24h: true,
|
|
Alpha24h: 12.4,
|
|
})
|
|
|
|
if !in.HasAlpha || !in.HasAlpha24h {
|
|
t.Fatalf("expected alpha flags true: %+v", in)
|
|
}
|
|
if in.HasSpot || in.HasFuture {
|
|
t.Fatalf("did not expect spot/future flags true: %+v", in)
|
|
}
|
|
}
|
|
|
|
type marketStub struct {
|
|
spotPairs map[string]bool
|
|
futuresPairs map[string]bool
|
|
spotPrices map[string]float64
|
|
futurePrices map[string]struct {
|
|
price float64
|
|
rate float64
|
|
time int64
|
|
}
|
|
alphaTokens map[string]market.AlphaTokenInfo
|
|
alphaPrices map[string]float64
|
|
marginRates map[string]float64
|
|
}
|
|
|
|
func (m *marketStub) GetFuturePrice(symbol string) (float64, float64, int64, bool) {
|
|
v, ok := m.futurePrices[symbol]
|
|
if !ok {
|
|
return 0, 0, 0, false
|
|
}
|
|
return v.price, v.rate, v.time, true
|
|
}
|
|
func (m *marketStub) GetAllPremiumIndex() (map[string]market.PremiumIndex, error) { return nil, nil }
|
|
func (m *marketStub) GetAllFundRate() (map[string]float64, map[string]int64) { return nil, nil }
|
|
func (m *marketStub) GetSpotPrice(symbol string) (float64, bool) {
|
|
v, ok := m.spotPrices[symbol]
|
|
return v, ok
|
|
}
|
|
func (m *marketStub) GetMarginInterestRates() map[string]float64 { return m.marginRates }
|
|
func (m *marketStub) IsAlphaToken(symbol string) bool {
|
|
_, ok := m.alphaTokens[symbol]
|
|
return ok
|
|
}
|
|
func (m *marketStub) GetAlphaToken(symbol string) (market.AlphaTokenInfo, bool) {
|
|
v, ok := m.alphaTokens[symbol]
|
|
return v, ok
|
|
}
|
|
func (m *marketStub) GetAlphaPrice(symbol string) (float64, bool) {
|
|
v, ok := m.alphaPrices[symbol]
|
|
return v, ok
|
|
}
|
|
func (m *marketStub) IsSpotPair(symbol string) bool { return m.spotPairs[symbol] }
|
|
func (m *marketStub) IsFuturesPair(symbol string) bool { return m.futuresPairs[symbol] }
|
|
func (m *marketStub) RefreshTradingPairCache() error { return nil }
|
|
|
|
func TestCollectRichTokenData_SpotOnlyReachable(t *testing.T) {
|
|
orig := data.Market
|
|
defer func() { data.Market = orig }()
|
|
|
|
data.Market = &marketStub{
|
|
spotPairs: map[string]bool{"ABCUSDT": true},
|
|
spotPrices: map[string]float64{"ABCUSDT": 1.23},
|
|
}
|
|
|
|
args := collectRichTokenData("ABC")
|
|
if !args.HasSpot {
|
|
t.Fatalf("expected spot to be reachable for spot-only token: %+v", args)
|
|
}
|
|
if args.HasFuture || args.HasAlpha {
|
|
t.Fatalf("did not expect future/alpha for spot-only token: %+v", args)
|
|
}
|
|
}
|
|
|
|
func TestCollectRichTokenData_FutureFailureStillKeepsSpot(t *testing.T) {
|
|
orig := data.Market
|
|
defer func() { data.Market = orig }()
|
|
|
|
data.Market = &marketStub{
|
|
spotPairs: map[string]bool{"ETHUSDT": true},
|
|
futuresPairs: map[string]bool{"ETHUSDT": true},
|
|
spotPrices: map[string]float64{"ETHUSDT": 3245},
|
|
futurePrices: map[string]struct {
|
|
price float64
|
|
rate float64
|
|
time int64
|
|
}{},
|
|
}
|
|
|
|
args := collectRichTokenData("ETH")
|
|
if !args.HasSpot {
|
|
t.Fatalf("expected spot to remain available when future lookup fails: %+v", args)
|
|
}
|
|
if args.HasFuture {
|
|
t.Fatalf("did not expect future when future lookup fails: %+v", args)
|
|
}
|
|
}
|
|
|
|
func TestCollectRichTokenData_AlphaUsesSymbolPrice(t *testing.T) {
|
|
orig := data.Market
|
|
defer func() { data.Market = orig }()
|
|
|
|
data.Market = &marketStub{
|
|
alphaTokens: map[string]market.AlphaTokenInfo{
|
|
"ABC": {Symbol: "ABC", PercentChange24h: "12.4", Price: "0.1"},
|
|
},
|
|
alphaPrices: map[string]float64{"ABCUSDT": 0.1234},
|
|
}
|
|
|
|
args := collectRichTokenData("ABC")
|
|
if !args.HasAlpha {
|
|
t.Fatalf("expected alpha to be available: %+v", args)
|
|
}
|
|
if !args.HasAlpha24h {
|
|
t.Fatalf("expected alpha 24h to be available: %+v", args)
|
|
}
|
|
if args.AlphaPrice != 0.1234 {
|
|
t.Fatalf("expected alpha price from symbol lookup, got %.4f", args.AlphaPrice)
|
|
}
|
|
}
|