213 lines
7.3 KiB
Python
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}") |