This commit is contained in:
lz_db
2025-11-17 19:36:28 +08:00
parent 42a0391eeb
commit da459da0f3
4 changed files with 324 additions and 188 deletions

View File

@@ -1,8 +1,5 @@
# -*- 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.base.exchange import Exchange
from ccxt.abstract.mt5 import ImplicitAPI
from ccxt.base.types import Any, Balances, BorrowInterest, Conversion, CrossBorrowRate, Currencies, Currency, DepositAddress, FundingHistory, Greeks, Int, LedgerEntry, Leverage, LeverageTier, LeverageTiers, Liquidation, LongShortRatio, Market, Num, Option, OptionChain, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFeeInterface, TradingFees, Transaction, MarketInterface, TransferEntry
@@ -79,7 +76,7 @@ class mt5(Exchange, ImplicitAPI):
'urls': {
'logo': '',
'api': {
'public': 'http://43.167.188.220:5000', # 直接使用具体地址,不使用 {hostname}
'public': 'http://43.167.188.220:5000',
'private': 'http://43.167.188.220:5000',
},
'www': 'http://43.167.188.220:5000',
@@ -112,7 +109,10 @@ class mt5(Exchange, ImplicitAPI):
'OrderSend': 1,
'OrderModify': 1,
'OrderClose': 1,
'SubscribeOhlc': 1, # 添加订阅端点
'SubscribeOhlc': 1,
'UnsubscribeOhlc': 1,
'Subscribe': 1,
'UnSubscribe': 1,
},
},
},
@@ -157,10 +157,10 @@ class mt5(Exchange, ImplicitAPI):
def connect(self):
"""连接到 MT5 账户并获取 token"""
params = {
'user': self.apiKey, # apiKey 作为 user
'password': self.secret, # secret 作为 password
'host': self.options['host'], # 账号所分配在的MT5服务器IP
'port': self.options['port'], # 账号所分配在的MT5服务器端口
'user': self.apiKey,
'password': self.secret,
'host': self.options['host'],
'port': self.options['port'],
'connectTimeoutSeconds': self.options['connectTimeoutSeconds'],
}
@@ -225,7 +225,6 @@ class mt5(Exchange, ImplicitAPI):
if market and market.get('symbol'):
markets.append(market)
except Exception as e:
# 跳过解析失败的市场,继续处理其他市场
if self.verbose:
print(f"跳过交易对 {symbol}: {e}")
continue
@@ -252,7 +251,6 @@ class mt5(Exchange, ImplicitAPI):
def parse_market(self, info):
"""解析市场信息 - 更健壮的版本"""
try:
# 安全获取 symbol
if not isinstance(info, dict):
return None
@@ -262,21 +260,15 @@ class mt5(Exchange, ImplicitAPI):
symbol = symbol.upper().strip()
# 确保符号格式正确 (如 EURUSD, BTCUSD)
# 处理符号格式
if len(symbol) < 6:
# 处理较短的符号(如黄金 XAUUSD 是6位但可能有其他情况
# 对于无法确定格式的符号,直接使用原始符号
base = symbol
quote = 'USD' # 默认报价货币
quote = 'USD'
else:
# 假设标准格式是3位基础货币+3位报价货币
base = symbol[:3]
quote = symbol[3:]
# 安全处理精度
digits = self.safe_integer(info, 'digits', 5)
# 确保 digits 是整数
if digits is not None:
try:
digits = int(digits)
@@ -374,9 +366,7 @@ class mt5(Exchange, ImplicitAPI):
'id': self.token,
}
# 如果指定了特定的交易对
if symbols is not None:
# 将符号列表转换为 MT5 格式(如 ['EUR/USD', 'GBP/USD'] -> ['EURUSD', 'GBPUSD']
mt5_symbols = []
for symbol in symbols:
market = self.market(symbol)
@@ -387,7 +377,6 @@ class mt5(Exchange, ImplicitAPI):
response = self.private_get_getquotemany(self.extend(request, params))
return self.parse_tickers(response, symbols)
except Exception as e:
# 如果批量获取失败,回退到逐个获取
if symbols is not None:
return self.fetch_tickers_fallback(symbols, params)
else:
@@ -411,7 +400,6 @@ class mt5(Exchange, ImplicitAPI):
tickers = {}
if isinstance(response, list):
# 如果响应是数组
for ticker_data in response:
try:
ticker = self.parse_ticker(ticker_data)
@@ -422,7 +410,6 @@ class mt5(Exchange, ImplicitAPI):
print(f"解析行情数据失败: {e}")
continue
elif isinstance(response, dict):
# 如果响应是字典
for symbol_key, ticker_data in response.items():
try:
ticker = self.parse_ticker(ticker_data)
@@ -433,7 +420,6 @@ class mt5(Exchange, ImplicitAPI):
print(f"解析行情数据失败 {symbol_key}: {e}")
continue
# 如果指定了特定的交易对,确保返回的顺序一致
if symbols is not None:
ordered_tickers = {}
for symbol in symbols:
@@ -453,8 +439,6 @@ class mt5(Exchange, ImplicitAPI):
}
response = self.private_get_getquote(self.extend(request, params))
# MT5 的 GetQuote 返回的是最新报价,不是完整的订单簿
# 这里模拟一个简单的订单簿
bid = self.safe_number(response, 'bid')
ask = self.safe_number(response, 'ask')
@@ -485,7 +469,7 @@ class mt5(Exchange, ImplicitAPI):
'datetime': None,
}
currency = 'USDT' # 强制设定为 USDT
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)
@@ -530,7 +514,7 @@ class mt5(Exchange, ImplicitAPI):
}
def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
"""获取未平仓订单 - 修复版本"""
"""获取未平仓订单"""
self.load_token()
request = {
'id': self.token,
@@ -538,7 +522,6 @@ class mt5(Exchange, ImplicitAPI):
response = self.private_get_openedorders(self.extend(request, params))
# 如果指定了特定交易对,进行过滤
if symbol is not None:
market = self.market(symbol)
filtered_orders = []
@@ -550,15 +533,14 @@ class mt5(Exchange, ImplicitAPI):
return self.parse_orders(response, None, since, limit)
def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
"""获取已平仓订单 - 修复版本"""
"""获取已平仓订单"""
self.load_token()
request = {
'id': self.token,
}
response = self.private_get_closedorders(self.extend(request, params))
# print(response)
# 如果指定了特定交易对,进行过滤
if symbol is not None:
market = self.market(symbol)
filtered_orders = []
@@ -570,25 +552,20 @@ class mt5(Exchange, ImplicitAPI):
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'))
@@ -634,7 +611,7 @@ class mt5(Exchange, ImplicitAPI):
'price': price,
'stopLossPrice': self.safe_number(order, 'stopLoss'),
'takeProfitPrice': self.safe_number(order, 'takeProfit'),
'reduceOnly':None,
'reduceOnly': None,
'triggerPrice': None,
'amount': amount,
'filled': filled,
@@ -686,7 +663,6 @@ class mt5(Exchange, ImplicitAPI):
def parse_position(self, order_data, market: Market = None):
"""从订单数据解析持仓"""
# 只有状态为 Filled 的订单才是持仓
state = self.safe_string(order_data, 'state')
if state != 'Filled':
return None
@@ -702,17 +678,14 @@ class mt5(Exchange, ImplicitAPI):
if open_timestamp_utc:
timestamp = open_timestamp_utc
# 确定持仓方向
order_type = self.safe_string(order_data, 'orderType')
side = 'long' if order_type == 'Buy' else 'short'
# 计算持仓价值
contracts = self.safe_number(order_data, 'lots', 0)
entry_price = self.safe_number(order_data, 'openPrice', 0)
current_price = self.safe_number(order_data, 'closePrice', entry_price)
notional = contracts * entry_price if contracts and entry_price else None
# 计算盈亏百分比
percentage = None
if entry_price and current_price and entry_price != 0:
if side == 'long':
@@ -729,17 +702,17 @@ class mt5(Exchange, ImplicitAPI):
'contracts': contracts,
'contractSize': self.safe_number(order_data, 'contractSize', 1.0),
'entryPrice': entry_price,
'markPrice': current_price, # 使用当前价格作为标记价格
'markPrice': current_price,
'notional': notional,
'leverage': 1, # MT5 可能需要从账户信息获取
'leverage': 1,
'unrealizedPnl': self.safe_number(order_data, 'profit', 0),
'realizedPnl': 0, # 对于持仓已实现盈亏为0
'liquidationPrice': None, # MT5 可能不提供
'realizedPnl': 0,
'liquidationPrice': None,
'marginMode': 'cross',
'percentage': percentage,
'marginRatio': None,
'collateral': None,
'initialMargin': None, # 可能需要计算
'initialMargin': None,
'initialMarginPercentage': None,
'maintenanceMargin': None,
'maintenanceMarginPercentage': None,
@@ -757,7 +730,6 @@ class mt5(Exchange, ImplicitAPI):
'volume': amount,
}
# 映射订单类型
operation_map = {
'market': {
'buy': 'Buy',
@@ -781,7 +753,6 @@ class mt5(Exchange, ImplicitAPI):
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: