From c7128ff5168de6ac1c0a57c25d61c858ea2997ba Mon Sep 17 00:00:00 2001 From: thuanle Date: Sun, 26 Apr 2026 15:40:00 +0700 Subject: [PATCH] Address PR #15 review: batch API calls and admin-guard /refresh 1. Add GetAllPremiumIndex() to fetch all futures data in one call, used by GetTopPrices instead of per-symbol sequential calls. 2. Add ADMIN_CHAT_ID env check to /refresh command to restrict access to authorized users only. Co-Authored-By: Claude Opus 4.7 --- internal/configs/key/env.go | 1 + internal/data/imarket.go | 1 + internal/data/market/future_price.go | 39 ++++++++++++++++++----- internal/services/controllers/market.go | 12 ++++--- internal/services/tele/commands/market.go | 8 +++++ 5 files changed, 49 insertions(+), 12 deletions(-) diff --git a/internal/configs/key/env.go b/internal/configs/key/env.go index d9bd0be..e8f5457 100644 --- a/internal/configs/key/env.go +++ b/internal/configs/key/env.go @@ -4,4 +4,5 @@ const ( LogEnv = "LOG_ENV" TelegramToken = "TELEGRAM_TOKEN" + AdminChatID = "ADMIN_CHAT_ID" ) diff --git a/internal/data/imarket.go b/internal/data/imarket.go index 9e45a5f..708a77d 100644 --- a/internal/data/imarket.go +++ b/internal/data/imarket.go @@ -4,6 +4,7 @@ import "me.thuanle/bbot/internal/data/market" type IMarket interface { GetFuturePrice(symbol string) (float64, float64, int64, bool) + GetAllPremiumIndex() (map[string]market.PremiumIndex, error) GetAllFundRate() (map[string]float64, map[string]int64) GetSpotPrice(symbol string) (float64, bool) diff --git a/internal/data/market/future_price.go b/internal/data/market/future_price.go index 9b6427e..d414cdf 100644 --- a/internal/data/market/future_price.go +++ b/internal/data/market/future_price.go @@ -36,27 +36,50 @@ func (ms *MarketData) GetFuturePrice(symbol string) (float64, float64, int64, bo return markPrice, fundingRate, p.NextFundingTime, true } -func (ms *MarketData) GetAllFundRate() (map[string]float64, map[string]int64) { +type PremiumIndex struct { + MarkPrice float64 + FundingRate float64 + NextFundingTime int64 +} + +func (ms *MarketData) GetAllPremiumIndex() (map[string]PremiumIndex, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() premiums, err := ms.futuresClient.NewPremiumIndexService().Do(ctx) if err != nil { log.Error().Err(err).Msg("Failed to fetch all futures premium index") - return make(map[string]float64), make(map[string]int64) + return nil, err } - rates := make(map[string]float64, len(premiums)) - times := make(map[string]int64, len(premiums)) - + result := make(map[string]PremiumIndex, len(premiums)) for _, p := range premiums { - rate, err := strconv.ParseFloat(p.LastFundingRate, 64) + markPrice, err := strconv.ParseFloat(p.MarkPrice, 64) if err != nil { continue } - rates[p.Symbol] = rate - times[p.Symbol] = p.NextFundingTime + rate, _ := strconv.ParseFloat(p.LastFundingRate, 64) + result[p.Symbol] = PremiumIndex{ + MarkPrice: markPrice, + FundingRate: rate, + NextFundingTime: p.NextFundingTime, + } } + return result, nil +} + +func (ms *MarketData) GetAllFundRate() (map[string]float64, map[string]int64) { + all, err := ms.GetAllPremiumIndex() + if err != nil { + return make(map[string]float64), make(map[string]int64) + } + + rates := make(map[string]float64, len(all)) + times := make(map[string]int64, len(all)) + for sym, p := range all { + rates[sym] = p.FundingRate + times[sym] = p.NextFundingTime + } return rates, times } diff --git a/internal/services/controllers/market.go b/internal/services/controllers/market.go index bf30653..0f1adbe 100644 --- a/internal/services/controllers/market.go +++ b/internal/services/controllers/market.go @@ -13,15 +13,19 @@ func GetTopPrices() ([]string, []float64, []float64) { topPrice := make([]float64, n) topRate := make([]float64, n) + all, err := data.Market.GetAllPremiumIndex() + if err != nil { + return topSym, topPrice, topRate + } + for i, sym := range strategy.TopPriceSymbols { - price, rate, _, ok := data.Market.GetFuturePrice(sym) + p, ok := all[sym] if !ok { continue } topSym[i] = sym - topPrice[i] = price - topRate[i] = rate - + topPrice[i] = p.MarkPrice + topRate[i] = p.FundingRate } return topSym, topPrice, topRate } diff --git a/internal/services/tele/commands/market.go b/internal/services/tele/commands/market.go index c73c7ab..9e8e1b9 100644 --- a/internal/services/tele/commands/market.go +++ b/internal/services/tele/commands/market.go @@ -1,7 +1,11 @@ package commands import ( + "os" + "strconv" + "gopkg.in/telebot.v3" + "me.thuanle/bbot/internal/configs/key" "me.thuanle/bbot/internal/data" "me.thuanle/bbot/internal/services/controllers" "me.thuanle/bbot/internal/services/tele/chat" @@ -19,6 +23,10 @@ func OnGetTopFundingFee(context telebot.Context) error { } func OnRefreshPairCache(context telebot.Context) error { + adminID, _ := strconv.ParseInt(os.Getenv(key.AdminChatID), 10, 64) + if adminID != 0 && context.Sender().ID != adminID { + return nil + } data.Market.RefreshTradingPairCache() return chat.ReplyMessage(context, "Trading pair cache refreshed") }