87e69ec8d0
Build Docker Image / build (amd64) (push) Successful in 2m21s
Alpha token prices are now looked up directly from Alpha data instead of being cached into spotPrice. Remove unused ensureAlphaCacheLoaded and shouldRefreshAlphaCache functions. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
153 lines
4.9 KiB
Go
153 lines
4.9 KiB
Go
package market
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
// AlphaTokenInfo represents the response from Binance Alpha token API
|
|
type AlphaTokenInfo struct {
|
|
TokenID string `json:"tokenId"`
|
|
ChainID string `json:"chainId"`
|
|
ChainIconURL string `json:"chainIconUrl"`
|
|
ChainName string `json:"chainName"`
|
|
ContractAddress string `json:"contractAddress"`
|
|
Name string `json:"name"`
|
|
Symbol string `json:"symbol"`
|
|
IconURL string `json:"iconUrl"`
|
|
Price string `json:"price"`
|
|
PercentChange24h string `json:"percentChange24h"`
|
|
Volume24h string `json:"volume24h"`
|
|
MarketCap string `json:"marketCap"`
|
|
FDV string `json:"fdv"`
|
|
Liquidity string `json:"liquidity"`
|
|
TotalSupply string `json:"totalSupply"`
|
|
CirculatingSupply string `json:"circulatingSupply"`
|
|
Holders string `json:"holders"`
|
|
Decimals int `json:"decimals"`
|
|
ListingCex bool `json:"listingCex"`
|
|
HotTag bool `json:"hotTag"`
|
|
CexCoinName string `json:"cexCoinName"`
|
|
CanTransfer bool `json:"canTransfer"`
|
|
Denomination int `json:"denomination"`
|
|
Offline bool `json:"offline"`
|
|
TradeDecimal int `json:"tradeDecimal"`
|
|
AlphaID string `json:"alphaId"`
|
|
Offsell bool `json:"offsell"`
|
|
PriceHigh24h string `json:"priceHigh24h"`
|
|
PriceLow24h string `json:"priceLow24h"`
|
|
Count24h string `json:"count24h"`
|
|
OnlineTge bool `json:"onlineTge"`
|
|
OnlineAirdrop bool `json:"onlineAirdrop"`
|
|
Score int `json:"score"`
|
|
CexOffDisplay bool `json:"cexOffDisplay"`
|
|
StockState bool `json:"stockState"`
|
|
ListingTime int64 `json:"listingTime"`
|
|
MulPoint int `json:"mulPoint"`
|
|
BnExclusiveState bool `json:"bnExclusiveState"`
|
|
}
|
|
|
|
// AlphaTokenResponse represents the API response structure
|
|
type AlphaTokenResponse struct {
|
|
Code string `json:"code"`
|
|
Message *string `json:"message"`
|
|
MessageDetail *string `json:"messageDetail"`
|
|
Data []AlphaTokenInfo `json:"data"`
|
|
}
|
|
|
|
// GetPrice returns the price as float64
|
|
func (a *AlphaTokenInfo) GetPrice() float64 {
|
|
price, err := strconv.ParseFloat(a.Price, 64)
|
|
if err != nil {
|
|
log.Error().Err(err).Str("symbol", a.Symbol).Msg("Failed to parse Alpha token price")
|
|
return 0
|
|
}
|
|
return price
|
|
}
|
|
|
|
// GetPercentChange24h returns the 24h percentage change as float64
|
|
func (a *AlphaTokenInfo) GetPercentChange24h() float64 {
|
|
change, err := strconv.ParseFloat(a.PercentChange24h, 64)
|
|
if err != nil {
|
|
log.Error().Err(err).Str("symbol", a.Symbol).Msg("Failed to parse Alpha token 24h change")
|
|
return 0
|
|
}
|
|
return change
|
|
}
|
|
|
|
// fetchAlphaTokens fetches Alpha tokens from Binance API
|
|
func (ms *MarketData) fetchAlphaTokens() ([]AlphaTokenInfo, error) {
|
|
const alphaTokenURL = "https://www.binance.com/bapi/defi/v1/public/wallet-direct/buw/wallet/cex/alpha/all/token/list"
|
|
|
|
client := &http.Client{
|
|
Timeout: 10 * time.Second,
|
|
}
|
|
|
|
resp, err := client.Get(alphaTokenURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to fetch Alpha tokens: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("Alpha token API returned status: %d", resp.StatusCode)
|
|
}
|
|
|
|
var tokenResponse AlphaTokenResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&tokenResponse); err != nil {
|
|
return nil, fmt.Errorf("failed to decode Alpha token response: %w", err)
|
|
}
|
|
|
|
if tokenResponse.Code != "000000" {
|
|
return nil, fmt.Errorf("Alpha token API returned error code: %s", tokenResponse.Code)
|
|
}
|
|
|
|
return tokenResponse.Data, nil
|
|
}
|
|
|
|
// refreshAlphaTokenCache refreshes the Alpha token cache
|
|
func (ms *MarketData) refreshAlphaTokenCache() {
|
|
ms.alphaCacheMutex.Lock()
|
|
defer ms.alphaCacheMutex.Unlock()
|
|
|
|
log.Info().Msg("Refreshing Alpha token cache")
|
|
|
|
tokens, err := ms.fetchAlphaTokens()
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to refresh Alpha token cache")
|
|
return
|
|
}
|
|
|
|
// Clear existing cache
|
|
ms.alphaTokens = make(map[string]AlphaTokenInfo)
|
|
|
|
// Populate cache with new data
|
|
for _, token := range tokens {
|
|
symbol := token.Symbol // Already uppercase from API
|
|
ms.alphaTokens[symbol] = token
|
|
}
|
|
|
|
ms.lastAlphaCacheUpdate = time.Now()
|
|
log.Info().Int("count", len(tokens)).Msg("Alpha token cache refreshed successfully")
|
|
}
|
|
|
|
// GetAlphaToken returns Alpha token info by symbol
|
|
func (ms *MarketData) GetAlphaToken(symbol string) (AlphaTokenInfo, bool) {
|
|
ms.alphaCacheMutex.RLock()
|
|
defer ms.alphaCacheMutex.RUnlock()
|
|
|
|
token, exists := ms.alphaTokens[symbol]
|
|
return token, exists
|
|
}
|
|
|
|
// IsAlphaToken checks if a symbol is an Alpha token
|
|
func (ms *MarketData) IsAlphaToken(symbol string) bool {
|
|
_, exists := ms.GetAlphaToken(symbol)
|
|
return exists
|
|
}
|