diff --git a/ccxt/async_support/mt5.py b/ccxt/async_support/mt5.py index 54ef9a3..c7e727c 100644 --- a/ccxt/async_support/mt5.py +++ b/ccxt/async_support/mt5.py @@ -177,28 +177,29 @@ class mt5(Exchange, ImplicitAPI): self.token = None async def fetch_markets(self, params={}): - """获取交易对列表 - 异步修复版本""" + """获取交易对列表 - 使用 SymbolParamsMany 接口""" if not hasattr(self, 'token') or not self.token: await self.get_token() request = { 'id': self.token, + 'limit': 10000, # 添加 limit 参数获取所有交易对 } try: - response = await self.private_get_symbols(self.extend(request, params)) + response = await self.private_get_symbolparamsmany(self.extend(request, params)) markets = [] - if isinstance(response, dict): - for symbol, info in response.items(): + if isinstance(response, list): + for symbol_data in response: try: - market = self.parse_market(info) + market = self.parse_market(symbol_data) if market and market.get('symbol'): markets.append(market) except Exception as e: - # 跳过解析失败的市场,继续处理其他市场 if self.verbose: - print(f"跳过交易对 {symbol}: {e}") + symbol_name = self.safe_string(symbol_data, 'symbol', 'unknown') + print(f"跳过交易对 {symbol_name}: {e}") continue # 设置市场数据 @@ -220,42 +221,72 @@ class mt5(Exchange, ImplicitAPI): except Exception as e: raise ExchangeError(f"获取市场数据失败: {e}") - def parse_market(self, info): - """解析市场信息 - 更健壮的版本""" + def parse_market(self, symbol_data): + """解析市场信息 - 根据新的数据格式""" try: - # 安全获取 symbol - if not isinstance(info, dict): + if not isinstance(symbol_data, dict): return None - symbol = self.safe_string(info, 'currency', '') + symbol = self.safe_string(symbol_data, 'symbol') if not symbol: return None - symbol = symbol.upper().strip() + symbol_info = self.safe_dict(symbol_data, 'symbolInfo', {}) + symbol_group = self.safe_dict(symbol_data, 'symbolGroup', {}) + + # 解析基础信息 + symbol_name = symbol.upper().strip() # 确保符号格式正确 (如 EURUSD) - if len(symbol) < 6: + if len(symbol_name) < 6: # 处理较短的符号 - base = symbol + base = symbol_name quote = 'USD' # 默认报价货币 else: - base = symbol[:3] - quote = symbol[3:] - - base = symbol[:3] - quote = symbol[3:] + base = symbol_name[:3] + quote = symbol_name[3:] - # 安全处理精度 - digits = self.safe_integer(info, 'digits', 5) - - # 确保 digits 是整数 + # 解析精度信息 + digits = self.safe_integer(symbol_info, 'digits', 5) if digits is not None: try: digits = int(digits) except (ValueError, TypeError): digits = 5 - market_id = symbol + # 解析合约大小 + contract_size = self.safe_number(symbol_info, 'contractSize', 100000) + + # 解析交易量限制 + min_volume = self.safe_number(symbol_group, 'minVolume', 0.01) + max_volume = self.safe_number(symbol_group, 'maxVolume', 100) + volume_step = self.safe_number(symbol_group, 'volumeStep', 0.01) + + # 处理最小交易量单位转换 + # 如果 minVolume 很大,可能是以合约单位表示,需要转换为手数 + if min_volume > 1000: # 假设大于1000的是合约单位 + min_volume = min_volume / contract_size if contract_size > 0 else 0.01 + + # 解析价格精度 + points = self.safe_number(symbol_info, 'points', 0.00001) + price_precision = digits + + # 解析保证金信息 + initial_margin = self.safe_number(symbol_group, 'initialMargin', 0) + maintenance_margin = self.safe_number(symbol_group, 'maintenanceMargin', 0) + + # 解析货币信息 + profit_currency = self.safe_string(symbol_info, 'profitCurrency', 'USD') + margin_currency = self.safe_string(symbol_info, 'marginCurrency', base) + + # 解析交易模式 + trade_mode = self.safe_string(symbol_group, 'tradeMode', 'Disabled') + active = trade_mode != 'Disabled' + + # 解析描述信息 + description = self.safe_string(symbol_info, 'description', '') + + market_id = symbol_name return { 'id': market_id, @@ -264,33 +295,61 @@ class mt5(Exchange, ImplicitAPI): 'quote': quote, 'baseId': base, 'quoteId': quote, - 'active': True, + 'active': active, 'type': 'spot', 'spot': True, 'margin': True, 'precision': { - 'price': digits, - 'amount': 2, + 'price': price_precision, + 'amount': 2, # 手数精度 + 'base': 2, + 'quote': price_precision, }, 'limits': { 'amount': { - 'min': self.safe_number(info, 'minVolume', 0.01), - 'max': self.safe_number(info, 'maxVolume'), + 'min': min_volume, + 'max': max_volume, }, 'price': { - 'min': None, + 'min': points, # 最小价格变动 'max': None, }, 'cost': { 'min': None, 'max': None, }, + 'leverage': { + 'min': 1.0, + 'max': self.safe_number(symbol_group, 'accountLeverage', 100.0), + } }, - 'info': info, + 'contractSize': contract_size, + 'expiry': None, + 'expiryDatetime': None, + 'strike': None, + 'optionType': None, + 'taker': self.safe_number(symbol_group, 'commission', 0), # 可能需要调整 + 'maker': self.safe_number(symbol_group, 'commission', 0), # 可能需要调整 + 'percentage': True, + 'tierBased': False, + 'feeSide': 'quote', + 'info': symbol_data, + 'margin': { + 'initial': initial_margin, + 'maintenance': maintenance_margin, + }, + 'swap': { + 'long': self.safe_number(symbol_group, 'swapLong', 0), + 'short': self.safe_number(symbol_group, 'swapShort', 0), + }, + 'lotSize': volume_step, + 'minNotional': None, + 'maxNotional': None, } except Exception as e: if self.verbose: - print(f"解析市场信息失败: {e}, info: {info}") + symbol_name = self.safe_string(symbol_data, 'symbol', 'unknown') + print(f"解析市场信息失败 {symbol_name}: {e}") return None async def server_timezone(self): diff --git a/ccxt/mt5.py b/ccxt/mt5.py index 9499899..799b9f4 100644 --- a/ccxt/mt5.py +++ b/ccxt/mt5.py @@ -209,25 +209,27 @@ class mt5(Exchange, ImplicitAPI): return self.timezone def fetch_markets(self, params={}): - """获取交易对列表 - 修复版本""" + """获取交易对列表 - 使用 SymbolParamsMany 接口""" self.load_token() request = { 'id': self.token, + 'limit': 10000, # 添加 limit 参数获取所有交易对 } try: - response = self.private_get_symbols(self.extend(request, params)) + response = self.private_get_symbolparamsmany(self.extend(request, params)) markets = [] - if isinstance(response, dict): - for symbol, info in response.items(): + if isinstance(response, list): + for symbol_data in response: try: - market = self.parse_market(info) + market = self.parse_market(symbol_data) if market and market.get('symbol'): markets.append(market) except Exception as e: if self.verbose: - print(f"跳过交易对 {symbol}: {e}") + symbol_name = self.safe_string(symbol_data, 'symbol', 'unknown') + print(f"跳过交易对 {symbol_name}: {e}") continue # 设置市场数据 @@ -249,34 +251,72 @@ class mt5(Exchange, ImplicitAPI): except Exception as e: raise ExchangeError(f"获取市场数据失败: {e}") - def parse_market(self, info): - """解析市场信息 - 更健壮的版本""" + def parse_market(self, symbol_data): + """解析市场信息 - 根据新的数据格式""" try: - if not isinstance(info, dict): + if not isinstance(symbol_data, dict): return None - symbol = self.safe_string(info, 'currency', '') + symbol = self.safe_string(symbol_data, 'symbol') if not symbol: return None - symbol = symbol.upper().strip() + symbol_info = self.safe_dict(symbol_data, 'symbolInfo', {}) + symbol_group = self.safe_dict(symbol_data, 'symbolGroup', {}) - # 处理符号格式 - if len(symbol) < 6: - base = symbol - quote = 'USD' + # 解析基础信息 + symbol_name = symbol.upper().strip() + + # 确保符号格式正确 (如 EURUSD) + if len(symbol_name) < 6: + # 处理较短的符号 + base = symbol_name + quote = 'USD' # 默认报价货币 else: - base = symbol[:3] - quote = symbol[3:] + base = symbol_name[:3] + quote = symbol_name[3:] - digits = self.safe_integer(info, 'digits', 5) + # 解析精度信息 + digits = self.safe_integer(symbol_info, 'digits', 5) if digits is not None: try: digits = int(digits) except (ValueError, TypeError): digits = 5 - market_id = symbol + # 解析合约大小 + contract_size = self.safe_number(symbol_info, 'contractSize', 100000) + + # 解析交易量限制 + min_volume = self.safe_number(symbol_group, 'minVolume', 0.01) + max_volume = self.safe_number(symbol_group, 'maxVolume', 100) + volume_step = self.safe_number(symbol_group, 'volumeStep', 0.01) + + # 处理最小交易量单位转换 + # 如果 minVolume 很大,可能是以合约单位表示,需要转换为手数 + if min_volume > 1000: # 假设大于1000的是合约单位 + min_volume = min_volume / contract_size if contract_size > 0 else 0.01 + + # 解析价格精度 + points = self.safe_number(symbol_info, 'points', 0.00001) + price_precision = digits + + # 解析保证金信息 + initial_margin = self.safe_number(symbol_group, 'initialMargin', 0) + maintenance_margin = self.safe_number(symbol_group, 'maintenanceMargin', 0) + + # 解析货币信息 + profit_currency = self.safe_string(symbol_info, 'profitCurrency', 'USD') + margin_currency = self.safe_string(symbol_info, 'marginCurrency', base) + + # 解析交易模式 + trade_mode = self.safe_string(symbol_group, 'tradeMode', 'Disabled') + active = trade_mode != 'Disabled' + + # 解析描述信息 + description = self.safe_string(symbol_info, 'description', '') + + market_id = symbol_name return { 'id': market_id, @@ -285,35 +325,63 @@ class mt5(Exchange, ImplicitAPI): 'quote': quote, 'baseId': base, 'quoteId': quote, - 'active': True, + 'active': active, 'type': 'spot', 'spot': True, 'margin': True, 'precision': { - 'price': digits, - 'amount': 2, + 'price': price_precision, + 'amount': 2, # 手数精度 + 'base': 2, + 'quote': price_precision, }, 'limits': { 'amount': { - 'min': self.safe_number(info, 'minVolume', 0.01), - 'max': self.safe_number(info, 'maxVolume'), + 'min': min_volume, + 'max': max_volume, }, 'price': { - 'min': None, + 'min': points, # 最小价格变动 'max': None, }, 'cost': { 'min': None, 'max': None, }, + 'leverage': { + 'min': 1.0, + 'max': self.safe_number(symbol_group, 'accountLeverage', 100.0), + } }, - 'info': info, + 'contractSize': contract_size, + 'expiry': None, + 'expiryDatetime': None, + 'strike': None, + 'optionType': None, + 'taker': self.safe_number(symbol_group, 'commission', 0), # 可能需要调整 + 'maker': self.safe_number(symbol_group, 'commission', 0), # 可能需要调整 + 'percentage': True, + 'tierBased': False, + 'feeSide': 'quote', + 'info': symbol_data, + 'margin': { + 'initial': initial_margin, + 'maintenance': maintenance_margin, + }, + 'swap': { + 'long': self.safe_number(symbol_group, 'swapLong', 0), + 'short': self.safe_number(symbol_group, 'swapShort', 0), + }, + 'lotSize': volume_step, + 'minNotional': None, + 'maxNotional': None, } except Exception as e: if self.verbose: - print(f"解析市场信息失败: {e}, info: {info}") + symbol_name = self.safe_string(symbol_data, 'symbol', 'unknown') + print(f"解析市场信息失败 {symbol_name}: {e}") return None - + def fetch_ticker(self, symbol, params={}): """获取行情数据""" self.load_token()