| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- import { MarketData, Ticker24hr, KlineData, DepthData } from './marketDataManager'
- /**
- * 缓存配置
- */
- export interface CacheConfig {
- maxKlineRecords: number // 每个交易对每个时间间隔最大K线记录数
- maxDepthLevels: number // 深度数据最大档位数
- cleanupInterval: number // 清理间隔(毫秒)
- maxAge: number // 数据最大保存时间(毫秒)
- }
- /**
- * 行情数据缓存管理器
- * 用于优化内存使用和数据管理
- */
- export class MarketDataCache {
- private config: CacheConfig
- private cleanupTimer: NodeJS.Timeout | null = null
- // 缓存数据
- private marketDataCache: Map<string, MarketData> = new Map()
- private ticker24hrCache: Map<string, Ticker24hr> = new Map()
- private klineDataCache: Map<string, Map<string, KlineData[]>> = new Map()
- private depthDataCache: Map<string, DepthData> = new Map()
- // 数据时间戳
- private dataTimestamps: Map<string, number> = new Map()
- constructor(config: Partial<CacheConfig> = {}) {
- this.config = {
- maxKlineRecords: 1000,
- maxDepthLevels: 20,
- cleanupInterval: 60000, // 1分钟
- maxAge: 300000, // 5分钟
- ...config,
- }
- this.startCleanupTimer()
- }
- /**
- * 更新行情数据
- */
- public updateMarketData(data: MarketData): void {
- this.marketDataCache.set(data.symbol, data)
- this.dataTimestamps.set(`market_${data.symbol}`, Date.now())
- }
- /**
- * 更新24小时行情数据
- */
- public updateTicker24hr(data: Ticker24hr): void {
- this.ticker24hrCache.set(data.symbol, data)
- this.dataTimestamps.set(`ticker_${data.symbol}`, Date.now())
- }
- /**
- * 更新K线数据
- */
- public updateKlineData(data: KlineData): void {
- const symbol = data.symbol
- const interval = data.interval
- if (!this.klineDataCache.has(symbol)) {
- this.klineDataCache.set(symbol, new Map())
- }
- const symbolKlines = this.klineDataCache.get(symbol)!
- if (!symbolKlines.has(interval)) {
- symbolKlines.set(interval, [])
- }
- const klines = symbolKlines.get(interval)!
- // 查找是否已存在相同时间戳的K线
- const existingIndex = klines.findIndex(k => k.openTime === data.openTime)
- if (existingIndex >= 0) {
- klines[existingIndex] = data
- } else {
- klines.push(data)
- // 保持最大记录数限制
- if (klines.length > this.config.maxKlineRecords) {
- klines.splice(0, klines.length - this.config.maxKlineRecords)
- }
- }
- this.dataTimestamps.set(`kline_${symbol}_${interval}`, Date.now())
- }
- /**
- * 更新深度数据
- */
- public updateDepthData(data: DepthData): void {
- // 限制深度档位数
- const limitedData: DepthData = {
- symbol: data.symbol,
- bids: data.bids.slice(0, this.config.maxDepthLevels),
- asks: data.asks.slice(0, this.config.maxDepthLevels),
- lastUpdateId: data.lastUpdateId,
- }
- this.depthDataCache.set(data.symbol, limitedData)
- this.dataTimestamps.set(`depth_${data.symbol}`, Date.now())
- }
- /**
- * 获取行情数据
- */
- public getMarketData(symbol: string): MarketData | null {
- return this.marketDataCache.get(symbol) || null
- }
- /**
- * 获取所有行情数据
- */
- public getAllMarketData(): Map<string, MarketData> {
- return new Map(this.marketDataCache)
- }
- /**
- * 获取24小时行情数据
- */
- public getTicker24hr(symbol: string): Ticker24hr | null {
- return this.ticker24hrCache.get(symbol) || null
- }
- /**
- * 获取所有24小时行情数据
- */
- public getAllTicker24hr(): Map<string, Ticker24hr> {
- return new Map(this.ticker24hrCache)
- }
- /**
- * 获取K线数据
- */
- public getKlineData(symbol: string, interval: string, limit?: number): KlineData[] {
- const symbolKlines = this.klineDataCache.get(symbol)
- if (!symbolKlines) return []
- const klines = symbolKlines.get(interval)
- if (!klines) return []
- if (limit) {
- return klines.slice(-limit)
- }
- return [...klines]
- }
- /**
- * 获取深度数据
- */
- public getDepthData(symbol: string): DepthData | null {
- return this.depthDataCache.get(symbol) || null
- }
- /**
- * 获取缓存统计信息
- */
- public getCacheStats(): {
- marketDataCount: number
- ticker24hrCount: number
- klineDataCount: number
- depthDataCount: number
- totalMemoryUsage: number
- } {
- let klineDataCount = 0
- for (const symbolKlines of this.klineDataCache.values()) {
- for (const klines of symbolKlines.values()) {
- klineDataCount += klines.length
- }
- }
- return {
- marketDataCount: this.marketDataCache.size,
- ticker24hrCount: this.ticker24hrCache.size,
- klineDataCount,
- depthDataCount: this.depthDataCache.size,
- totalMemoryUsage: this.estimateMemoryUsage(),
- }
- }
- /**
- * 清理过期数据
- */
- public cleanup(): void {
- const now = Date.now()
- const expiredKeys: string[] = []
- // 检查过期数据
- for (const [key, timestamp] of this.dataTimestamps.entries()) {
- if (now - timestamp > this.config.maxAge) {
- expiredKeys.push(key)
- }
- }
- // 删除过期数据
- expiredKeys.forEach(key => {
- const [type, symbol, interval] = key.split('_')
- switch (type) {
- case 'market':
- this.marketDataCache.delete(symbol)
- break
- case 'ticker':
- this.ticker24hrCache.delete(symbol)
- break
- case 'kline':
- const symbolKlines = this.klineDataCache.get(symbol)
- if (symbolKlines) {
- symbolKlines.delete(interval)
- if (symbolKlines.size === 0) {
- this.klineDataCache.delete(symbol)
- }
- }
- break
- case 'depth':
- this.depthDataCache.delete(symbol)
- break
- }
- this.dataTimestamps.delete(key)
- })
- if (expiredKeys.length > 0) {
- console.log(`🧹 清理了 ${expiredKeys.length} 条过期数据`)
- }
- }
- /**
- * 清空所有缓存
- */
- public clear(): void {
- this.marketDataCache.clear()
- this.ticker24hrCache.clear()
- this.klineDataCache.clear()
- this.depthDataCache.clear()
- this.dataTimestamps.clear()
- console.log('🗑️ 已清空所有缓存数据')
- }
- /**
- * 获取指定交易对的所有数据
- */
- public getSymbolData(symbol: string): {
- marketData: MarketData | null
- ticker24hr: Ticker24hr | null
- klineData: Map<string, KlineData[]>
- depthData: DepthData | null
- } {
- return {
- marketData: this.getMarketData(symbol),
- ticker24hr: this.getTicker24hr(symbol),
- klineData: this.klineDataCache.get(symbol) || new Map(),
- depthData: this.getDepthData(symbol),
- }
- }
- /**
- * 检查数据是否过期
- */
- public isDataExpired(symbol: string, type: 'market' | 'ticker' | 'kline' | 'depth', interval?: string): boolean {
- const key = interval ? `${type}_${symbol}_${interval}` : `${type}_${symbol}`
- const timestamp = this.dataTimestamps.get(key)
- if (!timestamp) return true
- return Date.now() - timestamp > this.config.maxAge
- }
- /**
- * 获取数据更新时间
- */
- public getDataTimestamp(
- symbol: string,
- type: 'market' | 'ticker' | 'kline' | 'depth',
- interval?: string,
- ): number | null {
- const key = interval ? `${type}_${symbol}_${interval}` : `${type}_${symbol}`
- return this.dataTimestamps.get(key) || null
- }
- /**
- * 启动清理定时器
- */
- private startCleanupTimer(): void {
- this.cleanupTimer = setInterval(() => {
- this.cleanup()
- }, this.config.cleanupInterval)
- }
- /**
- * 停止清理定时器
- */
- public stopCleanupTimer(): void {
- if (this.cleanupTimer) {
- clearInterval(this.cleanupTimer)
- this.cleanupTimer = null
- }
- }
- /**
- * 估算内存使用量
- */
- private estimateMemoryUsage(): number {
- let totalSize = 0
- // 估算 Map 开销
- totalSize += this.marketDataCache.size * 200 // 每个 MarketData 约 200 字节
- totalSize += this.ticker24hrCache.size * 300 // 每个 Ticker24hr 约 300 字节
- totalSize += this.depthDataCache.size * 400 // 每个 DepthData 约 400 字节
- // 估算 K线数据
- for (const symbolKlines of this.klineDataCache.values()) {
- for (const klines of symbolKlines.values()) {
- totalSize += klines.length * 150 // 每个 KlineData 约 150 字节
- }
- }
- // 估算时间戳 Map
- totalSize += this.dataTimestamps.size * 50 // 每个时间戳约 50 字节
- return totalSize
- }
- /**
- * 销毁缓存管理器
- */
- public destroy(): void {
- this.stopCleanupTimer()
- this.clear()
- }
- }
|