# -*- coding: utf-8 -*- # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code from ccxt.async_support.base.exchange import Exchange from ccxt.abstract.mt5 import ImplicitAPI import asyncio import hashlib from ccxt.base.types import Any, Balances, DepositAddress, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, OrderBooks, Trade, TradingFees, Transaction from typing import List from ccxt.base.errors import ExchangeError from ccxt.base.errors import AuthenticationError from ccxt.base.errors import ArgumentsRequired from ccxt.base.errors import InsufficientFunds from ccxt.base.errors import InvalidOrder from ccxt.base.errors import OrderNotFound from ccxt.base.errors import DDoSProtection from ccxt.base.errors import RateLimitExceeded from ccxt.base.errors import ExchangeNotAvailable from ccxt.base.errors import InvalidNonce from ccxt.base.decimal_to_precision import TICK_SIZE from ccxt.base.precise import Precise class mt5(Exchange, ImplicitAPI): def describe(self) -> Any: return self.deep_extend(super(mt5, self).describe(), { 'id': 'mt5', 'name': 'MT5', 'countries': ['US'], 'version': 'v2025.02.05-05.23', 'rateLimit': 1000, 'hostname': '43.167.188.220:5000', 'pro': True, 'options': { 'host': '18.163.85.196', 'port': 443, }, 'has': { 'CORS': True, 'spot': True, 'margin': True, 'swap': True, 'future': True, 'option': True, 'borrowCrossMargin': True, 'cancelAllOrders': True, 'cancelAllOrdersAfter': True, 'cancelOrder': True, 'cancelOrders': True, 'cancelOrdersForSymbols': True, 'closeAllPositions': False, 'closePosition': False, 'createConvertTrade': True, 'createMarketBuyOrderWithCost': True, 'createMarketSellOrderWithCost': True, 'createOrder': True, 'createOrders': True, 'createOrderWithTakeProfitAndStopLoss': True, 'createPostOnlyOrder': True, 'createReduceOnlyOrder': True, 'createStopLimitOrder': True, 'createStopLossOrder': True, 'createStopMarketOrder': True, 'createStopOrder': True, 'createTakeProfitOrder': True, 'createTrailingAmountOrder': True, 'createTriggerOrder': True, 'editOrder': True, 'editOrders': True, 'fetchAllGreeks': True, 'fetchBalance': True, 'fetchBidsAsks': 'emulated', 'fetchBorrowInterest': False, # temporarily disabled, doesn't work 'fetchBorrowRateHistories': False, 'fetchBorrowRateHistory': False, 'fetchCanceledAndClosedOrders': True, 'fetchCanceledOrders': True, 'fetchClosedOrder': True, 'fetchClosedOrders': True, 'fetchConvertCurrencies': True, 'fetchConvertQuote': True, 'fetchConvertTrade': True, 'fetchConvertTradeHistory': True, 'fetchCrossBorrowRate': True, 'fetchCrossBorrowRates': False, 'fetchCurrencies': True, 'fetchDeposit': False, 'fetchDepositAddress': True, 'fetchDepositAddresses': False, 'fetchDepositAddressesByNetwork': True, 'fetchDeposits': True, 'fetchDepositWithdrawFee': 'emulated', 'fetchDepositWithdrawFees': True, 'fetchFundingHistory': True, 'fetchFundingRate': 'emulated', # emulated in exchange 'fetchFundingRateHistory': True, 'fetchFundingRates': True, 'fetchGreeks': True, 'fetchIndexOHLCV': True, 'fetchIsolatedBorrowRate': False, 'fetchIsolatedBorrowRates': False, 'fetchLedger': True, 'fetchLeverage': True, 'fetchLeverageTiers': True, 'fetchLongShortRatio': False, 'fetchLongShortRatioHistory': True, 'fetchMarginAdjustmentHistory': False, 'fetchMarketLeverageTiers': True, 'fetchMarkets': True, 'fetchMarkOHLCV': True, 'fetchMyLiquidations': True, 'fetchMySettlementHistory': True, 'fetchMyTrades': True, 'fetchOHLCV': True, 'fetchOpenInterest': True, 'fetchOpenInterestHistory': True, 'fetchOpenOrder': True, 'fetchOpenOrders': True, 'fetchOption': True, 'fetchOptionChain': True, 'fetchOrder': True, 'fetchOrderBook': True, 'fetchOrders': False, 'fetchOrderTrades': True, 'fetchPosition': True, 'fetchPositionHistory': 'emulated', 'fetchPositions': True, 'fetchPositionsHistory': True, 'fetchPremiumIndexOHLCV': True, 'fetchSettlementHistory': True, 'fetchTicker': True, 'fetchTickers': True, 'fetchTime': True, 'fetchTrades': True, 'fetchTradingFee': True, 'fetchTradingFees': True, 'fetchTransactions': False, 'fetchTransfers': True, 'fetchUnderlyingAssets': False, 'fetchVolatilityHistory': True, 'fetchWithdrawals': True, 'repayCrossMargin': True, 'sandbox': True, 'setLeverage': True, 'setMarginMode': True, 'setPositionMode': True, 'transfer': True, 'withdraw': True, }, 'timeframes': { '1m': 1, '5m': 5, '15m': 15, '30m': 30, '1h': 60, '4h': 240, '1d': 1440, '1w': 10080, '1M': 43200, }, 'urls': { 'logo': '', 'api': { 'public': 'http://{hostname}', 'private': 'http://{hostname}', }, 'www': 'http://{hostname}', 'doc': ['http://{hostname}/index.html'], }, 'api': { 'public': { 'get': { 'Ping': 1 }, }, 'private': { 'get': { 'Connect': 10, 'CheckConnect': 1, 'Disconnect': 1, 'Symbols': 1, 'ServerTimezone':1, 'AccountSummary': 1, 'AccountDetails': 1, 'SymbolList': 1, 'GetQuote': 1, 'GetQuoteMany': 1, 'MarketWatchMany': 1, 'OpenedOrders': 1, 'ClosedOrders': 1, 'OpenedOrder': 1, 'OrderHistory': 1, 'PriceHistory': 1, 'OrderSend': 1, 'OrderModify': 1, 'OrderClose': 1, }, }, 'wsEndpoint': { 'order': "OnOrderUpdate", 'quote': "OnQuote", 'orderbook': "OnOrderBook", } }, 'requiredCredentials': { 'apiKey': True, 'secret': True, 'hostname': True, }, 'commonCurrencies': {}, 'exceptions': { 'exact': { 'Invalid token': AuthenticationError, 'Connection failed': ExchangeError, 'Invalid symbol': ExchangeError, 'Invalid order': InvalidOrder, 'Order not found': OrderNotFound, }, }, }) async def get_token(self): """获取或刷新 token - 异步版本""" if self.token and self.token_checked: try: await self.check_connect() return self.token except Exception: # Token 无效,重新连接 pass # 重新连接获取 token return await self.connect() async def connect(self): """连接到 MT5 账户并获取 token - 异步版本""" request = { 'user': self.apiKey, 'password': self.secret, 'host': self.options['host'], 'port': self.options['port'], 'connectTimeoutSeconds': 30, } print(f"🔧 Debug: Connect request params: {request}") response = await self.private_get_connect(request) print(f"🔧 Debug: Connect response: {response}") self.token = response self.token_checked = True return self.token async def check_connect(self): """检查连接状态 - 异步版本""" request = { 'id': await self.get_token(), } return await self.private_get_check_connect(request) async def fetch_markets(self, params={}): """获取交易对列表""" if not self.token: await self.get_token() request = { 'id': self.token, } response = await self.private_get_symbols(self.deep_extend(request, params)) markets = [] if isinstance(response, dict): for symbol, info in response.items(): market = self.parse_market(info) if market: markets.append(market) return markets def parse_market(self, info): """解析市场信息 - 根据 SymbolInfo 结构修正""" symbol = info.get('currency', '').upper() if not symbol: return None return { 'id': symbol, 'symbol': symbol, 'base': symbol[:3] if len(symbol) >= 6 else symbol, 'quote': symbol[3:] if len(symbol) >= 6 else 'USD', 'active': True, 'precision': { 'price': info.get('digits', 5), 'amount': 2, }, 'limits': { 'amount': { 'min': 0.01, 'max': None, }, 'price': { 'min': None, 'max': None, }, 'cost': { 'min': None, 'max': None, }, }, 'info': info, }