317 lines
11 KiB
Python
317 lines
11 KiB
Python
# -*- 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,
|
|
}
|