Files
ccxt_with_mt5/ccxt/pro/mt5.py
lz_db 0fab423a18 add
2025-11-16 12:31:03 +08:00

213 lines
7.3 KiB
Python

# -*- 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}")