enhancedMarketManager.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. import { MarketDataManager, MarketData, Ticker24hr, KlineData, DepthData } from './marketDataManager'
  2. import { MarketDataCache, CacheConfig } from './marketDataCache'
  3. import { EventEmitter } from 'events'
  4. /**
  5. * 增强行情管理器配置
  6. */
  7. export interface EnhancedMarketManagerConfig {
  8. wsUrl?: string
  9. reconnectInterval?: number
  10. maxReconnectAttempts?: number
  11. cacheConfig?: Partial<CacheConfig>
  12. enableCache?: boolean
  13. enableAutoReconnect?: boolean
  14. enablePingPong?: boolean
  15. }
  16. /**
  17. * 增强的行情管理器
  18. * 整合 WebSocket 连接和缓存管理功能
  19. */
  20. export class EnhancedMarketManager extends EventEmitter {
  21. private marketDataManager: MarketDataManager
  22. private marketDataCache: MarketDataCache
  23. private config: EnhancedMarketManagerConfig
  24. private isInitialized: boolean = false
  25. constructor(config: EnhancedMarketManagerConfig = {}) {
  26. super()
  27. this.config = {
  28. wsUrl: 'wss://fstream.binance.com/ws',
  29. reconnectInterval: 5000,
  30. maxReconnectAttempts: 10,
  31. enableCache: true,
  32. enableAutoReconnect: true,
  33. enablePingPong: true,
  34. ...config,
  35. }
  36. // 初始化行情数据管理器
  37. this.marketDataManager = new MarketDataManager(
  38. this.config.wsUrl,
  39. this.config.reconnectInterval,
  40. this.config.maxReconnectAttempts,
  41. )
  42. // 初始化缓存管理器
  43. this.marketDataCache = new MarketDataCache(this.config.cacheConfig)
  44. // 绑定事件
  45. this.bindEvents()
  46. }
  47. /**
  48. * 初始化连接
  49. */
  50. public async initialize(): Promise<void> {
  51. if (this.isInitialized) {
  52. return
  53. }
  54. try {
  55. await this.marketDataManager.connect()
  56. this.isInitialized = true
  57. console.log('✅ 增强行情管理器初始化成功')
  58. } catch (error) {
  59. console.error('❌ 增强行情管理器初始化失败:', error)
  60. throw error
  61. }
  62. }
  63. /**
  64. * 订阅行情数据
  65. * @param symbols 交易对列表
  66. * @param intervals K线间隔列表
  67. */
  68. public subscribeMarketData(symbols: string[], intervals: string[] = []): void {
  69. if (!this.isInitialized) {
  70. throw new Error('请先调用 initialize() 方法初始化连接')
  71. }
  72. this.marketDataManager.subscribeMarketData(symbols, intervals)
  73. }
  74. /**
  75. * 取消订阅
  76. * @param symbols 交易对列表
  77. */
  78. public unsubscribeMarketData(symbols: string[]): void {
  79. this.marketDataManager.unsubscribeMarketData(symbols)
  80. }
  81. /**
  82. * 获取行情数据(优先从缓存获取)
  83. */
  84. public getMarketData(symbol: string): MarketData | null {
  85. if (this.config.enableCache) {
  86. return this.marketDataCache.getMarketData(symbol)
  87. }
  88. return this.marketDataManager.getMarketData(symbol)
  89. }
  90. /**
  91. * 获取所有行情数据
  92. */
  93. public getAllMarketData(): Map<string, MarketData> {
  94. if (this.config.enableCache) {
  95. return this.marketDataCache.getAllMarketData()
  96. }
  97. return this.marketDataManager.getAllMarketData()
  98. }
  99. /**
  100. * 获取24小时行情数据
  101. */
  102. public getTicker24hr(symbol: string): Ticker24hr | null {
  103. if (this.config.enableCache) {
  104. return this.marketDataCache.getTicker24hr(symbol)
  105. }
  106. return this.marketDataManager.getTicker24hr(symbol)
  107. }
  108. /**
  109. * 获取所有24小时行情数据
  110. */
  111. public getAllTicker24hr(): Map<string, Ticker24hr> {
  112. if (this.config.enableCache) {
  113. return this.marketDataCache.getAllTicker24hr()
  114. }
  115. return this.marketDataManager.getAllTicker24hr()
  116. }
  117. /**
  118. * 获取K线数据
  119. */
  120. public getKlineData(symbol: string, interval: string, limit: number = 100): KlineData[] {
  121. if (this.config.enableCache) {
  122. return this.marketDataCache.getKlineData(symbol, interval, limit)
  123. }
  124. return this.marketDataManager.getKlineData(symbol, interval, limit)
  125. }
  126. /**
  127. * 获取深度数据
  128. */
  129. public getDepthData(symbol: string): DepthData | null {
  130. if (this.config.enableCache) {
  131. return this.marketDataCache.getDepthData(symbol)
  132. }
  133. return this.marketDataManager.getDepthData(symbol)
  134. }
  135. /**
  136. * 获取订阅的符号列表
  137. */
  138. public getSubscribedSymbols(): string[] {
  139. return this.marketDataManager.getSubscribedSymbols()
  140. }
  141. /**
  142. * 检查是否已连接
  143. */
  144. public isConnected(): boolean {
  145. return this.marketDataManager.isConnectedToWebSocket()
  146. }
  147. /**
  148. * 获取缓存统计信息
  149. */
  150. public getCacheStats() {
  151. if (!this.config.enableCache) {
  152. return null
  153. }
  154. return this.marketDataCache.getCacheStats()
  155. }
  156. /**
  157. * 获取指定交易对的所有数据
  158. */
  159. public getSymbolData(symbol: string) {
  160. if (this.config.enableCache) {
  161. return this.marketDataCache.getSymbolData(symbol)
  162. }
  163. return {
  164. marketData: this.marketDataManager.getMarketData(symbol),
  165. ticker24hr: this.marketDataManager.getTicker24hr(symbol),
  166. klineData: new Map(), // 需要从 manager 获取
  167. depthData: this.marketDataManager.getDepthData(symbol),
  168. }
  169. }
  170. /**
  171. * 检查数据是否过期
  172. */
  173. public isDataExpired(symbol: string, type: 'market' | 'ticker' | 'kline' | 'depth', interval?: string): boolean {
  174. if (!this.config.enableCache) {
  175. return true // 没有缓存时认为数据已过期
  176. }
  177. return this.marketDataCache.isDataExpired(symbol, type, interval)
  178. }
  179. /**
  180. * 获取数据更新时间
  181. */
  182. public getDataTimestamp(
  183. symbol: string,
  184. type: 'market' | 'ticker' | 'kline' | 'depth',
  185. interval?: string,
  186. ): number | null {
  187. if (!this.config.enableCache) {
  188. return null
  189. }
  190. return this.marketDataCache.getDataTimestamp(symbol, type, interval)
  191. }
  192. /**
  193. * 清理缓存
  194. */
  195. public cleanupCache(): void {
  196. if (this.config.enableCache) {
  197. this.marketDataCache.cleanup()
  198. }
  199. }
  200. /**
  201. * 清空缓存
  202. */
  203. public clearCache(): void {
  204. if (this.config.enableCache) {
  205. this.marketDataCache.clear()
  206. }
  207. }
  208. /**
  209. * 断开连接
  210. */
  211. public disconnect(): void {
  212. this.marketDataManager.disconnect()
  213. this.isInitialized = false
  214. }
  215. /**
  216. * 销毁管理器
  217. */
  218. public destroy(): void {
  219. this.disconnect()
  220. if (this.config.enableCache) {
  221. this.marketDataCache.destroy()
  222. }
  223. this.removeAllListeners()
  224. }
  225. /**
  226. * 绑定事件
  227. */
  228. private bindEvents(): void {
  229. // 连接事件
  230. this.marketDataManager.on('connected', () => {
  231. this.emit('connected')
  232. })
  233. this.marketDataManager.on('disconnected', (code: number, reason: string) => {
  234. this.emit('disconnected', code, reason)
  235. })
  236. this.marketDataManager.on('error', (error: Error) => {
  237. this.emit('error', error)
  238. })
  239. this.marketDataManager.on('maxReconnectAttemptsReached', () => {
  240. this.emit('maxReconnectAttemptsReached')
  241. })
  242. // 数据事件
  243. this.marketDataManager.on('marketData', (data: MarketData) => {
  244. if (this.config.enableCache) {
  245. this.marketDataCache.updateMarketData(data)
  246. }
  247. this.emit('marketData', data)
  248. })
  249. this.marketDataManager.on('ticker24hr', (data: Ticker24hr) => {
  250. if (this.config.enableCache) {
  251. this.marketDataCache.updateTicker24hr(data)
  252. }
  253. this.emit('ticker24hr', data)
  254. })
  255. this.marketDataManager.on('kline', (data: KlineData) => {
  256. if (this.config.enableCache) {
  257. this.marketDataCache.updateKlineData(data)
  258. }
  259. this.emit('kline', data)
  260. })
  261. this.marketDataManager.on('depth', (data: DepthData) => {
  262. if (this.config.enableCache) {
  263. this.marketDataCache.updateDepthData(data)
  264. }
  265. this.emit('depth', data)
  266. })
  267. }
  268. /**
  269. * 获取实时价格
  270. */
  271. public getCurrentPrice(symbol: string): number | null {
  272. const marketData = this.getMarketData(symbol)
  273. return marketData ? marketData.price : null
  274. }
  275. /**
  276. * 获取价格变化百分比
  277. */
  278. public getPriceChangePercent(symbol: string): number | null {
  279. const ticker = this.getTicker24hr(symbol)
  280. return ticker ? parseFloat(ticker.priceChangePercent) : null
  281. }
  282. /**
  283. * 获取24小时最高价
  284. */
  285. public get24hrHigh(symbol: string): number | null {
  286. const ticker = this.getTicker24hr(symbol)
  287. return ticker ? parseFloat(ticker.highPrice) : null
  288. }
  289. /**
  290. * 获取24小时最低价
  291. */
  292. public get24hrLow(symbol: string): number | null {
  293. const ticker = this.getTicker24hr(symbol)
  294. return ticker ? parseFloat(ticker.lowPrice) : null
  295. }
  296. /**
  297. * 获取24小时成交量
  298. */
  299. public get24hrVolume(symbol: string): number | null {
  300. const ticker = this.getTicker24hr(symbol)
  301. return ticker ? parseFloat(ticker.volume) : null
  302. }
  303. /**
  304. * 获取买卖价差
  305. */
  306. public getSpread(symbol: string): number | null {
  307. const marketData = this.getMarketData(symbol)
  308. if (!marketData) return null
  309. return marketData.ask - marketData.bid
  310. }
  311. /**
  312. * 获取买卖价差百分比
  313. */
  314. public getSpreadPercent(symbol: string): number | null {
  315. const marketData = this.getMarketData(symbol)
  316. if (!marketData) return null
  317. const spread = marketData.ask - marketData.bid
  318. return (spread / marketData.price) * 100
  319. }
  320. /**
  321. * 检查价格是否上涨
  322. */
  323. public isPriceUp(symbol: string): boolean | null {
  324. const changePercent = this.getPriceChangePercent(symbol)
  325. return changePercent !== null ? changePercent > 0 : null
  326. }
  327. /**
  328. * 检查价格是否下跌
  329. */
  330. public isPriceDown(symbol: string): boolean | null {
  331. const changePercent = this.getPriceChangePercent(symbol)
  332. return changePercent !== null ? changePercent < 0 : null
  333. }
  334. /**
  335. * 获取所有活跃交易对的价格
  336. */
  337. public getAllPrices(): Map<string, number> {
  338. const prices = new Map<string, number>()
  339. const allMarketData = this.getAllMarketData()
  340. for (const [symbol, data] of allMarketData) {
  341. prices.set(symbol, data.price)
  342. }
  343. return prices
  344. }
  345. /**
  346. * 获取价格排序的交易对列表
  347. */
  348. public getSymbolsByPrice(sortOrder: 'asc' | 'desc' = 'desc'): string[] {
  349. const allMarketData = this.getAllMarketData()
  350. const symbols = Array.from(allMarketData.entries())
  351. .sort(([, a], [, b]) => {
  352. return sortOrder === 'asc' ? a.price - b.price : b.price - a.price
  353. })
  354. .map(([symbol]) => symbol)
  355. return symbols
  356. }
  357. /**
  358. * 获取价格变化排序的交易对列表
  359. */
  360. public getSymbolsByChange(sortOrder: 'asc' | 'desc' = 'desc'): string[] {
  361. const symbols: Array<{ symbol: string; change: number }> = []
  362. for (const symbol of this.getSubscribedSymbols()) {
  363. const changePercent = this.getPriceChangePercent(symbol)
  364. if (changePercent !== null) {
  365. symbols.push({ symbol, change: changePercent })
  366. }
  367. }
  368. return symbols
  369. .sort((a, b) => {
  370. return sortOrder === 'asc' ? a.change - b.change : b.change - a.change
  371. })
  372. .map(item => item.symbol)
  373. }
  374. }