Reduce the request frequency to the Binance API and add backend caching.

This commit is contained in:
SkywalkerJi
2025-10-30 14:01:06 +08:00
parent 8dc32e61d0
commit 1171a4643c
6 changed files with 89 additions and 21 deletions

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"log"
"strconv"
"sync"
"time"
"github.com/adshao/go-binance/v2/futures"
@@ -13,19 +14,44 @@ import (
// FuturesTrader 币安合约交易器
type FuturesTrader struct {
client *futures.Client
// 余额缓存
cachedBalance map[string]interface{}
balanceCacheTime time.Time
balanceCacheMutex sync.RWMutex
// 持仓缓存
cachedPositions []map[string]interface{}
positionsCacheTime time.Time
positionsCacheMutex sync.RWMutex
// 缓存有效期15秒
cacheDuration time.Duration
}
// NewFuturesTrader 创建合约交易器
func NewFuturesTrader(apiKey, secretKey string) *FuturesTrader {
client := futures.NewClient(apiKey, secretKey)
return &FuturesTrader{
client: client,
client: client,
cacheDuration: 15 * time.Second, // 15秒缓存
}
}
// GetBalance 获取账户余额
// GetBalance 获取账户余额(带缓存)
func (t *FuturesTrader) GetBalance() (map[string]interface{}, error) {
log.Printf("🔄 正在调用币安API获取账户余额...")
// 先检查缓存是否有效
t.balanceCacheMutex.RLock()
if t.cachedBalance != nil && time.Since(t.balanceCacheTime) < t.cacheDuration {
cacheAge := time.Since(t.balanceCacheTime)
t.balanceCacheMutex.RUnlock()
log.Printf("✓ 使用缓存的账户余额(缓存时间: %.1f秒前)", cacheAge.Seconds())
return t.cachedBalance, nil
}
t.balanceCacheMutex.RUnlock()
// 缓存过期或不存在调用API
log.Printf("🔄 缓存过期正在调用币安API获取账户余额...")
account, err := t.client.NewGetAccountService().Do(context.Background())
if err != nil {
log.Printf("❌ 币安API调用失败: %v", err)
@@ -42,11 +68,29 @@ func (t *FuturesTrader) GetBalance() (map[string]interface{}, error) {
account.AvailableBalance,
account.TotalUnrealizedProfit)
// 更新缓存
t.balanceCacheMutex.Lock()
t.cachedBalance = result
t.balanceCacheTime = time.Now()
t.balanceCacheMutex.Unlock()
return result, nil
}
// GetPositions 获取所有持仓
// GetPositions 获取所有持仓(带缓存)
func (t *FuturesTrader) GetPositions() ([]map[string]interface{}, error) {
// 先检查缓存是否有效
t.positionsCacheMutex.RLock()
if t.cachedPositions != nil && time.Since(t.positionsCacheTime) < t.cacheDuration {
cacheAge := time.Since(t.positionsCacheTime)
t.positionsCacheMutex.RUnlock()
log.Printf("✓ 使用缓存的持仓信息(缓存时间: %.1f秒前)", cacheAge.Seconds())
return t.cachedPositions, nil
}
t.positionsCacheMutex.RUnlock()
// 缓存过期或不存在调用API
log.Printf("🔄 缓存过期正在调用币安API获取持仓信息...")
positions, err := t.client.NewGetPositionRiskService().Do(context.Background())
if err != nil {
return nil, fmt.Errorf("获取持仓失败: %w", err)
@@ -78,6 +122,12 @@ func (t *FuturesTrader) GetPositions() ([]map[string]interface{}, error) {
result = append(result, posMap)
}
// 更新缓存
t.positionsCacheMutex.Lock()
t.cachedPositions = result
t.positionsCacheTime = time.Now()
t.positionsCacheMutex.Unlock()
return result, nil
}

View File

@@ -42,9 +42,9 @@ function App() {
: null,
() => api.getStatus(selectedTraderId),
{
refreshInterval: 5000,
revalidateOnFocus: true,
dedupingInterval: 0,
refreshInterval: 15000, // 15秒刷新配合后端15秒缓存
revalidateOnFocus: false, // 禁用聚焦时重新验证,减少请求
dedupingInterval: 10000, // 10秒去重防止短时间内重复请求
}
);
@@ -54,9 +54,9 @@ function App() {
: null,
() => api.getAccount(selectedTraderId),
{
refreshInterval: 5000,
revalidateOnFocus: true,
dedupingInterval: 0,
refreshInterval: 15000, // 15秒刷新配合后端15秒缓存
revalidateOnFocus: false, // 禁用聚焦时重新验证,减少请求
dedupingInterval: 10000, // 10秒去重防止短时间内重复请求
}
);
@@ -66,9 +66,9 @@ function App() {
: null,
() => api.getPositions(selectedTraderId),
{
refreshInterval: 5000,
revalidateOnFocus: true,
dedupingInterval: 0,
refreshInterval: 15000, // 15秒刷新配合后端15秒缓存
revalidateOnFocus: false, // 禁用聚焦时重新验证,减少请求
dedupingInterval: 10000, // 10秒去重防止短时间内重复请求
}
);
@@ -77,7 +77,11 @@ function App() {
? `decisions/latest-${selectedTraderId}`
: null,
() => api.getLatestDecisions(selectedTraderId),
{ refreshInterval: 10000 }
{
refreshInterval: 30000, // 30秒刷新决策更新频率较低
revalidateOnFocus: false,
dedupingInterval: 20000,
}
);
const { data: stats } = useSWR<Statistics>(
@@ -85,7 +89,11 @@ function App() {
? `statistics-${selectedTraderId}`
: null,
() => api.getStatistics(selectedTraderId),
{ refreshInterval: 10000 }
{
refreshInterval: 30000, // 30秒刷新统计数据更新频率较低
revalidateOnFocus: false,
dedupingInterval: 20000,
}
);
useEffect(() => {

View File

@@ -50,7 +50,11 @@ export default function AILearning({ traderId }: AILearningProps) {
const { data: performance, error } = useSWR<PerformanceAnalysis>(
traderId ? `performance-${traderId}` : 'performance',
() => api.getPerformance(traderId),
{ refreshInterval: 10000 }
{
refreshInterval: 30000, // 30秒刷新AI学习分析数据更新频率较低
revalidateOnFocus: false,
dedupingInterval: 20000,
}
);
if (error) {

View File

@@ -33,8 +33,9 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
return Promise.all(promises);
},
{
refreshInterval: 10000,
refreshInterval: 30000, // 30秒刷新对比图表数据更新频率较低
revalidateOnFocus: false,
dedupingInterval: 20000,
}
);

View File

@@ -11,8 +11,9 @@ export function CompetitionPage() {
'competition',
api.getCompetition,
{
refreshInterval: 5000,
revalidateOnFocus: true,
refreshInterval: 15000, // 15秒刷新竞赛数据不需要太频繁更新
revalidateOnFocus: false,
dedupingInterval: 10000,
}
);

View File

@@ -34,7 +34,9 @@ export function EquityChart({ traderId }: EquityChartProps) {
traderId ? `equity-history-${traderId}` : 'equity-history',
() => api.getEquityHistory(traderId),
{
refreshInterval: 10000, // 每10秒刷新
refreshInterval: 30000, // 30秒刷新(历史数据更新频率较低)
revalidateOnFocus: false,
dedupingInterval: 20000,
}
);
@@ -42,7 +44,9 @@ export function EquityChart({ traderId }: EquityChartProps) {
traderId ? `account-${traderId}` : 'account',
() => api.getAccount(traderId),
{
refreshInterval: 5000,
refreshInterval: 15000, // 15秒刷新配合后端缓存
revalidateOnFocus: false,
dedupingInterval: 10000,
}
);