PacificaDataClient.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /**
  2. * Pacifica数据接口客户端
  3. *
  4. * 负责处理Pacifica DEX的所有数据查询接口
  5. * 包括价格、订单簿、K线、交易历史等
  6. */
  7. import { UniversalHttpClient } from '@/utils/universalHttpClient'
  8. import { HttpClientRequest, HttpClientResponse } from '@/types/httpClient'
  9. import {
  10. PacificaPriceData,
  11. PacificaTicker,
  12. PacificaOrderBook,
  13. PacificaKline,
  14. PacificaTrade,
  15. PacificaAggTrade,
  16. PacificaSymbol,
  17. PacificaMarketDataParams,
  18. PacificaApiResponse,
  19. PacificaListResponse,
  20. } from '@/types/pacifica'
  21. export class PacificaDataClient {
  22. private httpClient: UniversalHttpClient
  23. private accountId: string
  24. constructor(httpClient: UniversalHttpClient, accountId: string) {
  25. this.httpClient = httpClient
  26. this.accountId = accountId
  27. }
  28. // ======================== 市场数据接口 ========================
  29. /**
  30. * 获取交易对信息
  31. */
  32. async getExchangeInfo(): Promise<PacificaApiResponse<{ symbols: PacificaSymbol[] }>> {
  33. const request: HttpClientRequest = {
  34. platform: 'pacifica',
  35. accountId: this.accountId,
  36. method: 'GET',
  37. url: '/api/v3/exchangeInfo',
  38. headers: {
  39. 'Content-Type': 'application/json',
  40. },
  41. }
  42. const response = await this.httpClient.request<{ symbols: PacificaSymbol[] }>(request)
  43. return this.wrapResponse(response)
  44. }
  45. /**
  46. * 获取单个交易对最新价格
  47. */
  48. async getPrice(symbol: string): Promise<PacificaApiResponse<PacificaPriceData>> {
  49. const request: HttpClientRequest = {
  50. platform: 'pacifica',
  51. accountId: this.accountId,
  52. method: 'GET',
  53. url: `/api/v3/ticker/price?symbol=${symbol}`,
  54. headers: {
  55. 'Content-Type': 'application/json',
  56. },
  57. }
  58. const response = await this.httpClient.request<PacificaPriceData>(request)
  59. return this.wrapResponse(response)
  60. }
  61. /**
  62. * 获取所有交易对价格
  63. */
  64. async getAllPrices(): Promise<PacificaApiResponse<PacificaPriceData[]>> {
  65. const request: HttpClientRequest = {
  66. platform: 'pacifica',
  67. accountId: this.accountId,
  68. method: 'GET',
  69. url: '/api/v3/ticker/price',
  70. headers: {
  71. 'Content-Type': 'application/json',
  72. },
  73. }
  74. const response = await this.httpClient.request<PacificaPriceData[]>(request)
  75. return this.wrapResponse(response)
  76. }
  77. /**
  78. * 获取24hr价格变动统计
  79. */
  80. async getTicker24hr(symbol?: string): Promise<PacificaApiResponse<PacificaTicker | PacificaTicker[]>> {
  81. const url = symbol ? `/api/v3/ticker/24hr?symbol=${symbol}` : '/api/v3/ticker/24hr'
  82. const request: HttpClientRequest = {
  83. platform: 'pacifica',
  84. accountId: this.accountId,
  85. method: 'GET',
  86. url,
  87. headers: {
  88. 'Content-Type': 'application/json',
  89. },
  90. }
  91. const response = await this.httpClient.request<PacificaTicker | PacificaTicker[]>(request)
  92. return this.wrapResponse(response)
  93. }
  94. /**
  95. * 获取订单簿深度
  96. */
  97. async getOrderBook(symbol: string, limit: number = 100): Promise<PacificaApiResponse<PacificaOrderBook>> {
  98. const request: HttpClientRequest = {
  99. platform: 'pacifica',
  100. accountId: this.accountId,
  101. method: 'GET',
  102. url: `/api/v3/depth?symbol=${symbol}&limit=${limit}`,
  103. headers: {
  104. 'Content-Type': 'application/json',
  105. },
  106. }
  107. const response = await this.httpClient.request<PacificaOrderBook>(request)
  108. return this.wrapResponse(response)
  109. }
  110. /**
  111. * 获取最近成交记录
  112. */
  113. async getRecentTrades(symbol: string, limit: number = 500): Promise<PacificaApiResponse<PacificaTrade[]>> {
  114. const request: HttpClientRequest = {
  115. platform: 'pacifica',
  116. accountId: this.accountId,
  117. method: 'GET',
  118. url: `/api/v3/trades?symbol=${symbol}&limit=${limit}`,
  119. headers: {
  120. 'Content-Type': 'application/json',
  121. },
  122. }
  123. const response = await this.httpClient.request<PacificaTrade[]>(request)
  124. return this.wrapResponse(response)
  125. }
  126. /**
  127. * 获取历史成交记录
  128. */
  129. async getHistoricalTrades(
  130. symbol: string,
  131. params: PacificaMarketDataParams = {},
  132. ): Promise<PacificaApiResponse<PacificaTrade[]>> {
  133. const queryParams = new URLSearchParams()
  134. queryParams.append('symbol', symbol)
  135. if (params.limit) queryParams.append('limit', params.limit.toString())
  136. if (params.fromId) queryParams.append('fromId', params.fromId.toString())
  137. const request: HttpClientRequest = {
  138. platform: 'pacifica',
  139. accountId: this.accountId,
  140. method: 'GET',
  141. url: `/api/v3/historicalTrades?${queryParams.toString()}`,
  142. headers: {
  143. 'Content-Type': 'application/json',
  144. },
  145. }
  146. const response = await this.httpClient.request<PacificaTrade[]>(request)
  147. return this.wrapResponse(response)
  148. }
  149. /**
  150. * 获取聚合交易记录
  151. */
  152. async getAggTrades(
  153. symbol: string,
  154. params: PacificaMarketDataParams = {},
  155. ): Promise<PacificaApiResponse<PacificaAggTrade[]>> {
  156. const queryParams = new URLSearchParams()
  157. queryParams.append('symbol', symbol)
  158. if (params.limit) queryParams.append('limit', params.limit.toString())
  159. if (params.fromId) queryParams.append('fromId', params.fromId.toString())
  160. if (params.startTime) queryParams.append('startTime', params.startTime.toString())
  161. if (params.endTime) queryParams.append('endTime', params.endTime.toString())
  162. const request: HttpClientRequest = {
  163. platform: 'pacifica',
  164. accountId: this.accountId,
  165. method: 'GET',
  166. url: `/api/v3/aggTrades?${queryParams.toString()}`,
  167. headers: {
  168. 'Content-Type': 'application/json',
  169. },
  170. }
  171. const response = await this.httpClient.request<PacificaAggTrade[]>(request)
  172. return this.wrapResponse(response)
  173. }
  174. /**
  175. * 获取K线数据
  176. */
  177. async getKlines(
  178. symbol: string,
  179. interval: string,
  180. params: PacificaMarketDataParams = {},
  181. ): Promise<PacificaApiResponse<PacificaKline[]>> {
  182. const queryParams = new URLSearchParams()
  183. queryParams.append('symbol', symbol)
  184. queryParams.append('interval', interval)
  185. if (params.limit) queryParams.append('limit', params.limit.toString())
  186. if (params.startTime) queryParams.append('startTime', params.startTime.toString())
  187. if (params.endTime) queryParams.append('endTime', params.endTime.toString())
  188. const request: HttpClientRequest = {
  189. platform: 'pacifica',
  190. accountId: this.accountId,
  191. method: 'GET',
  192. url: `/api/v3/klines?${queryParams.toString()}`,
  193. headers: {
  194. 'Content-Type': 'application/json',
  195. },
  196. }
  197. const response = await this.httpClient.request<any[]>(request)
  198. // 转换原始K线数据为结构化对象
  199. const klines: PacificaKline[] = response.data.map((kline: any[]) => ({
  200. symbol,
  201. openTime: kline[0],
  202. open: kline[1],
  203. high: kline[2],
  204. low: kline[3],
  205. close: kline[4],
  206. volume: kline[5],
  207. closeTime: kline[6],
  208. quoteVolume: kline[7],
  209. trades: kline[8],
  210. takerBuyBaseVolume: kline[9],
  211. takerBuyQuoteVolume: kline[10],
  212. interval,
  213. firstTradeId: 0,
  214. lastTradeId: 0,
  215. }))
  216. return this.wrapResponse({
  217. ...response,
  218. data: klines,
  219. })
  220. }
  221. /**
  222. * 获取平均价格
  223. */
  224. async getAvgPrice(symbol: string): Promise<PacificaApiResponse<{ mins: number; price: string }>> {
  225. const request: HttpClientRequest = {
  226. platform: 'pacifica',
  227. accountId: this.accountId,
  228. method: 'GET',
  229. url: `/api/v3/avgPrice?symbol=${symbol}`,
  230. headers: {
  231. 'Content-Type': 'application/json',
  232. },
  233. }
  234. const response = await this.httpClient.request<{ mins: number; price: string }>(request)
  235. return this.wrapResponse(response)
  236. }
  237. /**
  238. * 获取服务器时间
  239. */
  240. async getServerTime(): Promise<PacificaApiResponse<{ serverTime: number }>> {
  241. const request: HttpClientRequest = {
  242. platform: 'pacifica',
  243. accountId: this.accountId,
  244. method: 'GET',
  245. url: '/api/v3/time',
  246. headers: {
  247. 'Content-Type': 'application/json',
  248. },
  249. }
  250. const response = await this.httpClient.request<{ serverTime: number }>(request)
  251. return this.wrapResponse(response)
  252. }
  253. /**
  254. * 测试连接
  255. */
  256. async ping(): Promise<PacificaApiResponse<{}>> {
  257. const request: HttpClientRequest = {
  258. platform: 'pacifica',
  259. accountId: this.accountId,
  260. method: 'GET',
  261. url: '/api/v3/ping',
  262. headers: {
  263. 'Content-Type': 'application/json',
  264. },
  265. }
  266. const response = await this.httpClient.request<{}>(request)
  267. return this.wrapResponse(response)
  268. }
  269. // ======================== 批量查询接口 ========================
  270. /**
  271. * 批量获取多个交易对价格
  272. */
  273. async getBatchPrices(symbols: string[]): Promise<PacificaApiResponse<PacificaPriceData[]>> {
  274. const symbolsParam = JSON.stringify(symbols)
  275. const request: HttpClientRequest = {
  276. platform: 'pacifica',
  277. accountId: this.accountId,
  278. method: 'GET',
  279. url: `/api/v3/ticker/price?symbols=${encodeURIComponent(symbolsParam)}`,
  280. headers: {
  281. 'Content-Type': 'application/json',
  282. },
  283. }
  284. const response = await this.httpClient.request<PacificaPriceData[]>(request)
  285. return this.wrapResponse(response)
  286. }
  287. /**
  288. * 批量获取多个交易对24hr统计
  289. */
  290. async getBatch24hrTickers(symbols: string[]): Promise<PacificaApiResponse<PacificaTicker[]>> {
  291. const symbolsParam = JSON.stringify(symbols)
  292. const request: HttpClientRequest = {
  293. platform: 'pacifica',
  294. accountId: this.accountId,
  295. method: 'GET',
  296. url: `/api/v3/ticker/24hr?symbols=${encodeURIComponent(symbolsParam)}`,
  297. headers: {
  298. 'Content-Type': 'application/json',
  299. },
  300. }
  301. const response = await this.httpClient.request<PacificaTicker[]>(request)
  302. return this.wrapResponse(response)
  303. }
  304. // ======================== 实时数据流接口 ========================
  305. /**
  306. * 获取WebSocket连接的监听密钥
  307. */
  308. async getListenKey(): Promise<PacificaApiResponse<{ listenKey: string }>> {
  309. const request: HttpClientRequest = {
  310. platform: 'pacifica',
  311. accountId: this.accountId,
  312. method: 'POST',
  313. url: '/api/v3/userDataStream',
  314. headers: {
  315. 'Content-Type': 'application/json',
  316. },
  317. }
  318. const response = await this.httpClient.request<{ listenKey: string }>(request)
  319. return this.wrapResponse(response)
  320. }
  321. /**
  322. * 延长监听密钥有效期
  323. */
  324. async keepAliveListenKey(listenKey: string): Promise<PacificaApiResponse<{}>> {
  325. const request: HttpClientRequest = {
  326. platform: 'pacifica',
  327. accountId: this.accountId,
  328. method: 'PUT',
  329. url: `/api/v3/userDataStream?listenKey=${listenKey}`,
  330. headers: {
  331. 'Content-Type': 'application/json',
  332. },
  333. }
  334. const response = await this.httpClient.request<{}>(request)
  335. return this.wrapResponse(response)
  336. }
  337. /**
  338. * 关闭监听密钥
  339. */
  340. async closeListenKey(listenKey: string): Promise<PacificaApiResponse<{}>> {
  341. const request: HttpClientRequest = {
  342. platform: 'pacifica',
  343. accountId: this.accountId,
  344. method: 'DELETE',
  345. url: `/api/v3/userDataStream?listenKey=${listenKey}`,
  346. headers: {
  347. 'Content-Type': 'application/json',
  348. },
  349. }
  350. const response = await this.httpClient.request<{}>(request)
  351. return this.wrapResponse(response)
  352. }
  353. // ======================== 辅助方法 ========================
  354. /**
  355. * 包装响应数据为统一格式
  356. */
  357. private wrapResponse<T>(response: HttpClientResponse<T>): PacificaApiResponse<T> {
  358. return {
  359. data: response.data,
  360. success: response.status >= 200 && response.status < 300,
  361. code: response.status,
  362. message: response.statusText,
  363. timestamp: Date.now(),
  364. }
  365. }
  366. /**
  367. * 构建查询参数字符串
  368. */
  369. private buildQueryString(params: Record<string, any>): string {
  370. const queryParams = new URLSearchParams()
  371. Object.entries(params).forEach(([key, value]) => {
  372. if (value !== undefined && value !== null) {
  373. queryParams.append(key, value.toString())
  374. }
  375. })
  376. return queryParams.toString()
  377. }
  378. /**
  379. * 格式化符号参数
  380. */
  381. private formatSymbol(symbol: string): string {
  382. return symbol.toUpperCase()
  383. }
  384. /**
  385. * 验证时间间隔参数
  386. */
  387. private validateInterval(interval: string): boolean {
  388. const validIntervals = ['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w', '1M']
  389. return validIntervals.includes(interval)
  390. }
  391. /**
  392. * 获取客户端配置
  393. */
  394. getClientConfig() {
  395. return {
  396. accountId: this.accountId,
  397. platform: 'pacifica',
  398. endpoints: {
  399. exchangeInfo: '/api/v3/exchangeInfo',
  400. price: '/api/v3/ticker/price',
  401. ticker24hr: '/api/v3/ticker/24hr',
  402. orderBook: '/api/v3/depth',
  403. trades: '/api/v3/trades',
  404. klines: '/api/v3/klines',
  405. ping: '/api/v3/ping',
  406. time: '/api/v3/time',
  407. },
  408. }
  409. }
  410. }