add
This commit is contained in:
673
ccxt/async_support/mt5.py
Normal file
673
ccxt/async_support/mt5.py
Normal file
@@ -0,0 +1,673 @@
|
||||
# -*- 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 typing import List, Optional # 添加 Optional 导入
|
||||
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,
|
||||
'connectTimeoutSeconds': 30,
|
||||
},
|
||||
'has': {
|
||||
'CORS': True,
|
||||
'spot': True,
|
||||
'margin': True,
|
||||
'swap': False,
|
||||
'future': False,
|
||||
'option': False,
|
||||
'cancelOrder': True,
|
||||
'createOrder': True,
|
||||
'fetchBalance': True,
|
||||
'fetchClosedOrders': True,
|
||||
'fetchMarkets': True,
|
||||
'fetchMyTrades': True,
|
||||
'fetchOHLCV': True,
|
||||
'fetchOpenOrders': True,
|
||||
'fetchOrder': True,
|
||||
'fetchOrderBook': True,
|
||||
'fetchTicker': True,
|
||||
'fetchTickers': True, # 添加这个字段
|
||||
'fetchTickers': 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://43.167.188.220:5000', # 直接使用具体地址
|
||||
'private': 'http://43.167.188.220:5000',
|
||||
},
|
||||
'www': 'http://43.167.188.220:5000',
|
||||
'doc': ['http://43.167.188.220:5000/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,
|
||||
},
|
||||
},
|
||||
},
|
||||
'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 hasattr(self, 'token') and self.token:
|
||||
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': self.options['connectTimeoutSeconds'],
|
||||
}
|
||||
|
||||
response = await self.private_get_connect(request)
|
||||
|
||||
self.token = response
|
||||
return self.token
|
||||
|
||||
async def check_connect(self):
|
||||
"""检查连接状态 - 异步版本"""
|
||||
request = {
|
||||
'id': await self.get_token(),
|
||||
}
|
||||
return await self.private_get_checkconnect(request)
|
||||
|
||||
async def fetch_markets(self, params={}):
|
||||
"""获取交易对列表 - 异步修复版本"""
|
||||
if not hasattr(self, 'token') or not self.token:
|
||||
await self.get_token()
|
||||
|
||||
request = {
|
||||
'id': self.token,
|
||||
}
|
||||
|
||||
try:
|
||||
response = await self.private_get_symbols(self.extend(request, params))
|
||||
|
||||
markets = []
|
||||
if isinstance(response, dict):
|
||||
for symbol, info in response.items():
|
||||
try:
|
||||
market = self.parse_market(info)
|
||||
if market and market.get('symbol'):
|
||||
markets.append(market)
|
||||
except Exception as e:
|
||||
# 跳过解析失败的市场,继续处理其他市场
|
||||
if self.verbose:
|
||||
print(f"跳过交易对 {symbol}: {e}")
|
||||
continue
|
||||
|
||||
# 设置市场数据
|
||||
if markets:
|
||||
self.markets = {}
|
||||
self.symbols = []
|
||||
for market in markets:
|
||||
id = market['id']
|
||||
symbol = market['symbol']
|
||||
self.markets[id] = market
|
||||
self.markets[symbol] = market
|
||||
self.symbols.append(symbol)
|
||||
|
||||
self.symbols = sorted(self.symbols)
|
||||
self.ids = sorted(self.markets.keys())
|
||||
|
||||
return markets
|
||||
|
||||
except Exception as e:
|
||||
raise ExchangeError(f"获取市场数据失败: {e}")
|
||||
|
||||
def parse_market(self, info):
|
||||
"""解析市场信息 - 更健壮的版本"""
|
||||
try:
|
||||
# 安全获取 symbol
|
||||
if not isinstance(info, dict):
|
||||
return None
|
||||
|
||||
symbol = self.safe_string(info, 'currency', '')
|
||||
if not symbol:
|
||||
return None
|
||||
|
||||
symbol = symbol.upper().strip()
|
||||
|
||||
# 确保符号格式正确 (如 EURUSD)
|
||||
if len(symbol) < 6:
|
||||
return None
|
||||
|
||||
base = symbol[:3]
|
||||
quote = symbol[3:]
|
||||
|
||||
# 安全处理精度
|
||||
digits = self.safe_integer(info, 'digits', 5)
|
||||
|
||||
# 确保 digits 是整数
|
||||
if digits is not None:
|
||||
try:
|
||||
digits = int(digits)
|
||||
except (ValueError, TypeError):
|
||||
digits = 5
|
||||
|
||||
market_id = symbol
|
||||
|
||||
return {
|
||||
'id': market_id,
|
||||
'symbol': base + '/' + quote,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'baseId': base,
|
||||
'quoteId': quote,
|
||||
'active': True,
|
||||
'type': 'spot',
|
||||
'spot': True,
|
||||
'margin': True,
|
||||
'precision': {
|
||||
'price': digits,
|
||||
'amount': 2,
|
||||
},
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': self.safe_number(info, 'minVolume', 0.01),
|
||||
'max': self.safe_number(info, 'maxVolume'),
|
||||
},
|
||||
'price': {
|
||||
'min': None,
|
||||
'max': None,
|
||||
},
|
||||
'cost': {
|
||||
'min': None,
|
||||
'max': None,
|
||||
},
|
||||
},
|
||||
'info': info,
|
||||
}
|
||||
except Exception as e:
|
||||
if self.verbose:
|
||||
print(f"解析市场信息失败: {e}, info: {info}")
|
||||
return None
|
||||
|
||||
async def fetch_balance(self, params={}):
|
||||
"""获取账户余额"""
|
||||
if not hasattr(self, 'token') or not self.token:
|
||||
await self.get_token()
|
||||
|
||||
request = {
|
||||
'id': self.token,
|
||||
}
|
||||
response = await self.private_get_accountsummary(self.extend(request, params))
|
||||
return self.parse_balance(response)
|
||||
|
||||
def parse_balance(self, response):
|
||||
"""解析余额信息"""
|
||||
result = {
|
||||
'info': response,
|
||||
'timestamp': None,
|
||||
'datetime': None,
|
||||
}
|
||||
|
||||
currency = 'USDT'
|
||||
balance = self.safe_number(response, 'balance', 0.0)
|
||||
margin = self.safe_number(response, 'margin', 0.0)
|
||||
free_margin = self.safe_number(response, 'freeMargin', 0.0)
|
||||
|
||||
result[currency] = {
|
||||
'free': free_margin,
|
||||
'used': margin,
|
||||
'total': balance,
|
||||
}
|
||||
|
||||
return self.safe_balance(result)
|
||||
|
||||
async def fetch_ticker(self, symbol, params={}):
|
||||
"""获取行情数据"""
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
|
||||
if not hasattr(self, 'token') or not self.token:
|
||||
await self.get_token()
|
||||
|
||||
request = {
|
||||
'id': self.token,
|
||||
'symbol': market['id'],
|
||||
}
|
||||
|
||||
response = await self.private_get_getquote(self.extend(request, params))
|
||||
return self.parse_ticker(response, market)
|
||||
|
||||
def parse_ticker(self, ticker, market=None):
|
||||
"""解析行情数据"""
|
||||
symbol = market['symbol'] if market else None
|
||||
timestamp = None
|
||||
if ticker.get('time'):
|
||||
try:
|
||||
timestamp = self.parse8601(ticker.get('time'))
|
||||
except:
|
||||
timestamp = None
|
||||
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': self.iso8601(timestamp) if timestamp else None,
|
||||
'high': None,
|
||||
'low': None,
|
||||
'bid': self.safe_number(ticker, 'bid'),
|
||||
'bidVolume': None,
|
||||
'ask': self.safe_number(ticker, 'ask'),
|
||||
'askVolume': None,
|
||||
'vwap': None,
|
||||
'open': None,
|
||||
'close': None,
|
||||
'last': self.safe_number(ticker, 'last'),
|
||||
'previousClose': None,
|
||||
'change': None,
|
||||
'percentage': None,
|
||||
'average': None,
|
||||
'baseVolume': self.safe_number(ticker, 'volume'),
|
||||
'quoteVolume': None,
|
||||
'info': ticker,
|
||||
}
|
||||
|
||||
async def fetch_tickers(self, symbols: Optional[List[str]] = None, params={}):
|
||||
"""异步获取多个交易对的行情数据"""
|
||||
if not hasattr(self, 'token') or not self.token:
|
||||
await self.get_token()
|
||||
|
||||
request = {
|
||||
'id': self.token,
|
||||
}
|
||||
|
||||
# 如果指定了特定的交易对
|
||||
if symbols is not None:
|
||||
# 将符号列表转换为 MT5 格式
|
||||
mt5_symbols = []
|
||||
for symbol in symbols:
|
||||
market = self.market(symbol)
|
||||
mt5_symbols.append(market['id'])
|
||||
request['symbols'] = mt5_symbols
|
||||
|
||||
try:
|
||||
response = await self.private_get_getquotemany(self.extend(request, params))
|
||||
return self.parse_tickers(response, symbols)
|
||||
except Exception as e:
|
||||
# 如果批量获取失败,回退到逐个获取
|
||||
if symbols is not None:
|
||||
return await self.fetch_tickers_fallback(symbols, params)
|
||||
else:
|
||||
raise ExchangeError(f"获取批量行情失败: {e}")
|
||||
|
||||
async def fetch_tickers_fallback(self, symbols, params={}):
|
||||
"""异步回退方法:逐个获取交易对行情"""
|
||||
tickers = {}
|
||||
for symbol in symbols:
|
||||
try:
|
||||
ticker = await self.fetch_ticker(symbol, params)
|
||||
tickers[symbol] = ticker
|
||||
except Exception as e:
|
||||
if self.verbose:
|
||||
print(f"获取 {symbol} 行情失败: {e}")
|
||||
continue
|
||||
return tickers
|
||||
|
||||
def parse_tickers(self, response, symbols=None):
|
||||
"""解析批量行情数据(与同步版本相同)"""
|
||||
tickers = {}
|
||||
|
||||
if isinstance(response, list):
|
||||
# 如果响应是数组
|
||||
for ticker_data in response:
|
||||
try:
|
||||
ticker = self.parse_ticker(ticker_data)
|
||||
if ticker and ticker.get('symbol'):
|
||||
tickers[ticker['symbol']] = ticker
|
||||
except Exception as e:
|
||||
if self.verbose:
|
||||
print(f"解析行情数据失败: {e}")
|
||||
continue
|
||||
elif isinstance(response, dict):
|
||||
# 如果响应是字典
|
||||
for symbol_key, ticker_data in response.items():
|
||||
try:
|
||||
ticker = self.parse_ticker(ticker_data)
|
||||
if ticker and ticker.get('symbol'):
|
||||
tickers[ticker['symbol']] = ticker
|
||||
except Exception as e:
|
||||
if self.verbose:
|
||||
print(f"解析行情数据失败 {symbol_key}: {e}")
|
||||
continue
|
||||
|
||||
# 如果指定了特定的交易对,确保返回的顺序一致
|
||||
if symbols is not None:
|
||||
ordered_tickers = {}
|
||||
for symbol in symbols:
|
||||
if symbol in tickers:
|
||||
ordered_tickers[symbol] = tickers[symbol]
|
||||
return ordered_tickers
|
||||
|
||||
return tickers
|
||||
|
||||
async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
|
||||
"""异步获取未平仓订单 - 修复版本"""
|
||||
if not hasattr(self, 'token') or not self.token:
|
||||
await self.get_token()
|
||||
|
||||
request = {
|
||||
'id': self.token,
|
||||
}
|
||||
|
||||
response = await self.private_get_openedorders(self.extend(request, params))
|
||||
|
||||
# 如果指定了特定交易对,进行过滤
|
||||
if symbol is not None:
|
||||
market = self.market(symbol)
|
||||
filtered_orders = []
|
||||
for order in response:
|
||||
if isinstance(order, dict) and order.get('symbol') == market['id']:
|
||||
filtered_orders.append(order)
|
||||
return self.parse_orders(filtered_orders, market, since, limit)
|
||||
else:
|
||||
return self.parse_orders(response, None, since, limit)
|
||||
|
||||
async def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
|
||||
"""异步获取已平仓订单 - 修复版本"""
|
||||
if not hasattr(self, 'token') or not self.token:
|
||||
await self.get_token()
|
||||
|
||||
request = {
|
||||
'id': self.token,
|
||||
}
|
||||
|
||||
response = await self.private_get_closedorders(self.extend(request, params))
|
||||
|
||||
# 如果指定了特定交易对,进行过滤
|
||||
if symbol is not None:
|
||||
market = self.market(symbol)
|
||||
filtered_orders = []
|
||||
for order in response:
|
||||
if isinstance(order, dict) and order.get('symbol') == market['id']:
|
||||
filtered_orders.append(order)
|
||||
return self.parse_orders(filtered_orders, market, since, limit)
|
||||
else:
|
||||
return self.parse_orders(response, None, since, limit)
|
||||
|
||||
def parse_order(self, order, market=None):
|
||||
"""解析订单信息 - 修复市场符号问题"""
|
||||
try:
|
||||
id = self.safe_string(order, 'ticket')
|
||||
market_id = self.safe_string(order, 'symbol')
|
||||
|
||||
# 安全地解析市场符号
|
||||
symbol = None
|
||||
if market is not None:
|
||||
symbol = market['symbol']
|
||||
elif market_id is not None:
|
||||
# 修复:提供更多参数来正确解析符号
|
||||
# 假设 MT5 的符号格式是 BASEQUOTE(如 EURUSD, BTCUSD)
|
||||
if len(market_id) >= 6:
|
||||
# 尝试解析为 3+3 格式(如 EURUSD, GBPUSD)
|
||||
base = market_id[:3]
|
||||
quote = market_id[3:]
|
||||
symbol = base + '/' + quote
|
||||
else:
|
||||
# 如果无法解析,使用原始 market_id
|
||||
symbol = market_id
|
||||
|
||||
timestamp = self.parse8601(self.safe_string(order, 'openTime'))
|
||||
last_trade_timestamp = self.parse8601(self.safe_string(order, 'closeTime'))
|
||||
|
||||
status = self.parse_order_status(self.safe_string(order, 'state'))
|
||||
side = self.parse_order_side(self.safe_string(order, 'orderType'))
|
||||
type = self.parse_order_type(self.safe_string(order, 'orderType'))
|
||||
|
||||
price = self.safe_number(order, 'openPrice')
|
||||
amount = self.safe_number(order, 'lots')
|
||||
filled = self.safe_number(order, 'closeLots', 0)
|
||||
|
||||
remaining = None
|
||||
if amount is not None and filled is not None:
|
||||
remaining = amount - filled
|
||||
|
||||
cost = None
|
||||
if price is not None and filled is not None:
|
||||
cost = price * filled
|
||||
|
||||
fee = None
|
||||
fee_cost = self.safe_number(order, 'commission', 0)
|
||||
if fee_cost != 0:
|
||||
fee = {
|
||||
'cost': fee_cost,
|
||||
'currency': None,
|
||||
}
|
||||
|
||||
return self.safe_order({
|
||||
'id': id,
|
||||
'clientOrderId': None,
|
||||
'datetime': self.iso8601(timestamp),
|
||||
'timestamp': timestamp,
|
||||
'lastTradeTimestamp': last_trade_timestamp,
|
||||
'status': status,
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
'timeInForce': None,
|
||||
'postOnly': None,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'stopPrice': None,
|
||||
'triggerPrice': None,
|
||||
'amount': amount,
|
||||
'filled': filled,
|
||||
'remaining': remaining,
|
||||
'cost': cost,
|
||||
'trades': None,
|
||||
'fee': fee,
|
||||
'info': order,
|
||||
'average': None,
|
||||
})
|
||||
except Exception as e:
|
||||
if self.verbose:
|
||||
print(f"解析订单失败: {e}, order: {order}")
|
||||
raise e
|
||||
|
||||
def parse_order_status(self, status):
|
||||
statuses = {
|
||||
'Started': 'open',
|
||||
'Placed': 'open',
|
||||
'Cancelled': 'canceled',
|
||||
'Partial': 'open',
|
||||
'Filled': 'closed',
|
||||
'Rejected': 'rejected',
|
||||
'Expired': 'expired',
|
||||
}
|
||||
return self.safe_string(statuses, status, status)
|
||||
|
||||
def parse_order_side(self, side):
|
||||
sides = {
|
||||
'Buy': 'buy',
|
||||
'Sell': 'sell',
|
||||
'BuyLimit': 'buy',
|
||||
'SellLimit': 'sell',
|
||||
'BuyStop': 'buy',
|
||||
'SellStop': 'sell',
|
||||
}
|
||||
return self.safe_string(sides, side, side)
|
||||
|
||||
def parse_order_type(self, type):
|
||||
types = {
|
||||
'Buy': 'market',
|
||||
'Sell': 'market',
|
||||
'BuyLimit': 'limit',
|
||||
'SellLimit': 'limit',
|
||||
'BuyStop': 'stop',
|
||||
'SellStop': 'stop',
|
||||
}
|
||||
return self.safe_string(types, type, type)
|
||||
|
||||
async def create_order(self, symbol, type, side, amount, price=None, params={}):
|
||||
"""创建订单"""
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
|
||||
if not hasattr(self, 'token') or not self.token:
|
||||
await self.get_token()
|
||||
|
||||
request = {
|
||||
'id': self.token,
|
||||
'symbol': market['id'],
|
||||
'volume': amount,
|
||||
}
|
||||
|
||||
# 映射订单类型
|
||||
operation_map = {
|
||||
'market': {
|
||||
'buy': 'Buy',
|
||||
'sell': 'Sell',
|
||||
},
|
||||
'limit': {
|
||||
'buy': 'BuyLimit',
|
||||
'sell': 'SellLimit',
|
||||
},
|
||||
'stop': {
|
||||
'buy': 'BuyStop',
|
||||
'sell': 'SellStop',
|
||||
},
|
||||
}
|
||||
|
||||
if type in operation_map and side in operation_map[type]:
|
||||
request['operation'] = operation_map[type][side]
|
||||
else:
|
||||
raise InvalidOrder(self.id + ' createOrder does not support order type ' + type + ' and side ' + side)
|
||||
|
||||
if type in ['limit', 'stop'] and price is not None:
|
||||
request['price'] = price
|
||||
|
||||
# 处理止损止盈
|
||||
stop_loss = self.safe_number(params, 'stopLoss')
|
||||
take_profit = self.safe_number(params, 'takeProfit')
|
||||
if stop_loss is not None:
|
||||
request['stoploss'] = stop_loss
|
||||
if take_profit is not None:
|
||||
request['takeprofit'] = take_profit
|
||||
|
||||
response = await self.private_get_ordersend(self.extend(request, params))
|
||||
return self.parse_order(response, market)
|
||||
|
||||
async def cancel_order(self, id, symbol=None, params={}):
|
||||
"""取消订单"""
|
||||
if not hasattr(self, 'token') or not self.token:
|
||||
await self.get_token()
|
||||
|
||||
request = {
|
||||
'id': self.token,
|
||||
'ticket': int(id),
|
||||
}
|
||||
response = await self.private_get_orderclose(self.extend(request, params))
|
||||
return self.parse_order(response)
|
||||
|
||||
def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
|
||||
"""签名请求"""
|
||||
base_url = self.urls['api'][api]
|
||||
url = base_url + '/' + path
|
||||
query = self.omit(params, self.extract_params(path))
|
||||
|
||||
if method == 'GET' and query:
|
||||
url += '?' + self.urlencode(query)
|
||||
|
||||
# 调试信息
|
||||
if self.verbose:
|
||||
print(f"🔧 Debug: Final URL: {url}")
|
||||
print(f"🔧 Debug: Method: {method}")
|
||||
print(f"🔧 Debug: Params: {params}")
|
||||
|
||||
return {
|
||||
'url': url,
|
||||
'method': method,
|
||||
'body': body,
|
||||
'headers': headers
|
||||
}
|
||||
Reference in New Issue
Block a user