diff --git a/internal/data/imarket.go b/internal/data/imarket.go index a7d8a21..1582529 100644 --- a/internal/data/imarket.go +++ b/internal/data/imarket.go @@ -13,6 +13,7 @@ type IMarket interface { // Alpha token methods IsAlphaToken(symbol string) bool GetAlphaToken(symbol string) (market.AlphaTokenInfo, bool) + GetAlphaPrice(symbol string) (float64, bool) // Trading pair methods IsSpotPair(symbol string) bool diff --git a/internal/data/market/alpha_tokens.go b/internal/data/market/alpha_tokens.go index 4676ff2..4e99972 100644 --- a/internal/data/market/alpha_tokens.go +++ b/internal/data/market/alpha_tokens.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" "strconv" "time" @@ -54,10 +55,21 @@ type AlphaTokenInfo struct { // AlphaTokenResponse represents the API response structure type AlphaTokenResponse struct { - Code string `json:"code"` - Message *string `json:"message"` + Code string `json:"code"` + Message *string `json:"message"` + MessageDetail *string `json:"messageDetail"` + Data []AlphaTokenInfo `json:"data"` +} + +type AlphaTickerData struct { + Price string `json:"price"` +} + +type AlphaTickerResponse struct { + Code string `json:"code"` + Message *string `json:"message"` MessageDetail *string `json:"messageDetail"` - Data []AlphaTokenInfo `json:"data"` + Data AlphaTickerData `json:"data"` } // GetPrice returns the price as float64 @@ -150,3 +162,37 @@ func (ms *MarketData) IsAlphaToken(symbol string) bool { _, exists := ms.GetAlphaToken(symbol) return exists } + +func (ms *MarketData) GetAlphaPrice(symbol string) (float64, bool) { + const alphaTickerURL = "https://www.binance.com/bapi/defi/v1/public/alpha-trade/ticker" + + client := &http.Client{Timeout: 5 * time.Second} + endpoint := alphaTickerURL + "?" + url.Values{"symbol": []string{symbol}}.Encode() + + resp, err := client.Get(endpoint) + if err != nil { + log.Error().Err(err).Str("symbol", symbol).Msg("Failed to fetch Alpha ticker") + return 0, false + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return 0, false + } + + var tickerResp AlphaTickerResponse + if err := json.NewDecoder(resp.Body).Decode(&tickerResp); err != nil { + return 0, false + } + + if tickerResp.Code != "000000" { + return 0, false + } + + price, err := strconv.ParseFloat(tickerResp.Data.Price, 64) + if err != nil || price <= 0 { + return 0, false + } + + return price, true +} diff --git a/internal/services/tele/commands/token.go b/internal/services/tele/commands/token.go index fca9e41..a1be9fa 100644 --- a/internal/services/tele/commands/token.go +++ b/internal/services/tele/commands/token.go @@ -66,9 +66,13 @@ func collectRichTokenData(token string) buildRichTokenMessageArgs { if alphaToken, ok := data.Market.GetAlphaToken(token); ok { a.HasAlpha = true - a.AlphaPrice = alphaToken.GetPrice() a.HasAlpha24h = true a.Alpha24h = alphaToken.GetPercentChange24h() + if alphaPrice, ok := data.Market.GetAlphaPrice(token + "USDT"); ok { + a.AlphaPrice = alphaPrice + } else { + a.AlphaPrice = alphaToken.GetPrice() + } } futureSymbol := token + "USDT" diff --git a/internal/services/tele/commands/token_test.go b/internal/services/tele/commands/token_test.go index 0356891..e8082ef 100644 --- a/internal/services/tele/commands/token_test.go +++ b/internal/services/tele/commands/token_test.go @@ -56,6 +56,7 @@ type marketStub struct { time int64 } alphaTokens map[string]market.AlphaTokenInfo + alphaPrices map[string]float64 marginRates map[string]float64 } @@ -81,6 +82,10 @@ 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 } @@ -126,3 +131,26 @@ func TestCollectRichTokenData_FutureFailureStillKeepsSpot(t *testing.T) { 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) + } +}