# -*- coding: utf-8 -*- from ccxt.async_support.mt5 import mt5 as mt5Parent from ccxt.base.errors import ExchangeError, ArgumentsRequired from ccxt.async_support.base.ws.client import Client import asyncio from typing import Optional, Dict, Any, List class mt5(mt5Parent): def describe(self): return self.deep_extend(super(mt5, self).describe(), { 'has': { 'ws': True, 'watchBalance': True, 'watchOrders': True, 'watchTicker': True, 'watchOHLCV': True, }, 'urls': { 'api': { 'ws': 'ws://43.167.188.220:5000', }, }, 'options': { 'tradesLimit': 1000, 'ordersLimit': 1000, 'OHLCVLimit': 1000, }, 'streaming': { 'ping': True, 'maxPingPongMisses': 2, }, }) async def watch_balance(self, params={}): """ 监听余额变化 """ if not hasattr(self, 'token') or not self.token: await self.get_token() url = self.urls['api']['ws'] + '/OnOrderProfit' request = { 'id': self.token, } message_hash = 'balance' return await self.watch(url, message_hash, request, params) async def watch_orders(self, symbol: Optional[str] = None, since: Optional[int] = None, limit: Optional[int] = None, params={}): """ 监听订单变化 """ if not hasattr(self, 'token') or not self.token: await self.get_token() url = self.urls['api']['ws'] + '/OnOrderUpdate' request = { 'id': self.token, } message_hash = 'orders' if symbol is not None: symbol = self.symbol(symbol) message_hash += ':' + symbol orders = await self.watch(url, message_hash, request, params) if self.newUpdates: limit = orders.getLimit(symbol, limit) return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True) async def watch_ticker(self, symbol: str, params={}): """ 监听行情变化 """ await self.load_markets() market = self.market(symbol) if not hasattr(self, 'token') or not self.token: await self.get_token() url = self.urls['api']['ws'] + '/OnQuote' request = { 'id': self.token, 'symbol': market['id'], } message_hash = 'ticker:' + market['symbol'] return await self.watch(url, message_hash, request, params) async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Optional[int] = None, limit: Optional[int] = None, params={}): """ 监听K线数据 """ await self.load_markets() market = self.market(symbol) if not hasattr(self, 'token') or not self.token: await self.get_token() url = self.urls['api']['ws'] + '/OnOhlc' request = { 'id': self.token, 'symbol': market['id'], 'timeframe': self.timeframes[timeframe], } message_hash = 'ohlcv:' + market['symbol'] + ':' + timeframe ohlcv = await self.watch(url, message_hash, request, params) if self.newUpdates: limit = ohlcv.getLimit(symbol, limit) return self.filter_by_since_limit(ohlcv, since, limit, 0, True) def handle_balance(self, client: Client, message): """处理余额更新""" message_hash = 'balance' balance = self.parse_balance(message) self.balance = balance client.resolve(balance, message_hash) def handle_orders(self, client: Client, message): """处理订单更新""" message_hash = 'orders' if self.orders is None: self.orders = ArrayCacheBySymbolById() orders = self.parse_ws_orders(message) for order in orders: self.orders.append(order) client.resolve(self.orders, message_hash) def handle_ticker(self, client: Client, message): """处理行情更新""" ticker = self.parse_ws_ticker(message) symbol = ticker['symbol'] message_hash = 'ticker:' + symbol self.tickers[symbol] = ticker client.resolve(ticker, message_hash) def handle_ohlcv(self, client: Client, message): """处理K线更新""" symbol = self.safe_string(message, 'symbol') timeframe = self.safe_string(message, 'timeframe', '1m') message_hash = 'ohlcv:' + symbol + ':' + timeframe if self.ohlcvs is None: self.ohlcvs = {} if symbol not in self.ohlcvs: self.ohlcvs[symbol] = {} stored = self.ohlcvs[symbol][timeframe] if stored is None: limit = self.safe_integer(self.options, 'OHLCVLimit', 1000) stored = ArrayCacheByTimestamp(limit) self.ohlcvs[symbol][timeframe] = stored ohlcv = self.parse_ws_ohlcv(message) stored.append(ohlcv) client.resolve(stored, message_hash) def parse_ws_orders(self, message): """解析WebSocket订单数据""" orders = self.safe_value(message, 'data', []) result = [] for order_data in orders: order = self.parse_ws_order(order_data) result.append(order) return result def parse_ws_order(self, order_data): """解析单个WebSocket订单""" return self.parse_order(order_data) def parse_ws_ticker(self, message): """解析WebSocket行情数据""" return self.parse_ticker(message) def parse_ws_ohlcv(self, message): """解析WebSocket K线数据""" return [ self.parse8601(self.safe_string(message, 'time')), self.safe_number(message, 'open'), self.safe_number(message, 'high'), self.safe_number(message, 'low'), self.safe_number(message, 'close'), self.safe_number(message, 'volume', 0), ] def handle_message(self, client: Client, message): """处理所有WebSocket消息""" error_code = self.safe_string(message, 'errorCode') if error_code is not None: self.handle_error_message(client, message) return # 根据消息类型路由处理 message_type = self.safe_string(message, 'type') if message_type == 'OnOrderProfit': self.handle_balance(client, message) elif message_type == 'OnOrderUpdate': self.handle_orders(client, message) elif message_type == 'OnQuote': self.handle_ticker(client, message) elif message_type == 'OnOhlc': self.handle_ohlcv(client, message) elif 'OpenedOrders' in message: self.handle_orders(client, message) def handle_error_message(self, client: Client, message): """处理错误消息""" error_code = self.safe_string(message, 'errorCode') error_message = self.safe_string(message, 'message') raise ExchangeError(f"MT5 WebSocket error {error_code}: {error_message}")