add
This commit is contained in:
213
ccxt/pro/mt5.py
Normal file
213
ccxt/pro/mt5.py
Normal file
@@ -0,0 +1,213 @@
|
||||
# -*- 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}")
|
||||
Reference in New Issue
Block a user