1
This commit is contained in:
@@ -107,6 +107,10 @@ class mt5(Exchange, ImplicitAPI):
|
||||
'OrderSend': 1,
|
||||
'OrderModify': 1,
|
||||
'OrderClose': 1,
|
||||
'SubscribeOhlc': 1, # K线订阅
|
||||
'UnsubscribeOhlc': 1, # K线取消订阅
|
||||
'Subscribe': 1, # 行情订阅
|
||||
'UnSubscribe': 1, # 行情取消订阅
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -297,11 +301,13 @@ class mt5(Exchange, ImplicitAPI):
|
||||
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)
|
||||
equity = self.safe_number(response, 'equity', 0.0)
|
||||
|
||||
result[currency] = {
|
||||
'free': free_margin,
|
||||
'used': margin,
|
||||
'total': balance,
|
||||
'equity': equity,
|
||||
}
|
||||
|
||||
return self.safe_balance(result)
|
||||
@@ -532,6 +538,7 @@ class mt5(Exchange, ImplicitAPI):
|
||||
'datetime': self.iso8601(timestamp),
|
||||
'timestamp': timestamp,
|
||||
'lastTradeTimestamp': last_trade_timestamp,
|
||||
'lastUpdateTimestamp': last_trade_timestamp,
|
||||
'status': status,
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
@@ -539,7 +546,9 @@ class mt5(Exchange, ImplicitAPI):
|
||||
'postOnly': None,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'stopPrice': None,
|
||||
'stopLossPrice': self.safe_number(order, 'stopLoss'),
|
||||
'takeProfitPrice': self.safe_number(order, 'takeProfit'),
|
||||
'reduceOnly':None,
|
||||
'triggerPrice': None,
|
||||
'amount': amount,
|
||||
'filled': filled,
|
||||
@@ -547,7 +556,7 @@ class mt5(Exchange, ImplicitAPI):
|
||||
'cost': cost,
|
||||
'trades': None,
|
||||
'fee': fee,
|
||||
'info': order,
|
||||
# 'info': order,
|
||||
'average': None,
|
||||
})
|
||||
except Exception as e:
|
||||
@@ -589,6 +598,68 @@ class mt5(Exchange, ImplicitAPI):
|
||||
}
|
||||
return self.safe_string(types, type, type)
|
||||
|
||||
def parse_position(self, order_data, market: Market = None):
|
||||
"""从订单数据解析持仓"""
|
||||
# 只有状态为 Filled 的订单才是持仓
|
||||
state = self.safe_string(order_data, 'state')
|
||||
if state != 'Filled':
|
||||
return None
|
||||
|
||||
symbol = self.safe_string(order_data, 'symbol')
|
||||
if symbol and len(symbol) >= 6:
|
||||
base = symbol[:3]
|
||||
quote = symbol[3:]
|
||||
symbol = base + '/' + quote
|
||||
|
||||
timestamp = self.parse8601(self.safe_string(order_data, 'openTime'))
|
||||
open_timestamp_utc = self.safe_integer(order_data, 'openTimestampUTC')
|
||||
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':
|
||||
percentage = (current_price - entry_price) / entry_price
|
||||
else:
|
||||
percentage = (entry_price - current_price) / entry_price
|
||||
|
||||
return {
|
||||
'id': self.safe_string(order_data, 'ticket'),
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': self.iso8601(timestamp),
|
||||
'side': side,
|
||||
'contracts': contracts,
|
||||
'contractSize': self.safe_number(order_data, 'contractSize', 1.0),
|
||||
'entryPrice': entry_price,
|
||||
'markPrice': current_price, # 使用当前价格作为标记价格
|
||||
'notional': notional,
|
||||
'leverage': 1, # MT5 可能需要从账户信息获取
|
||||
'unrealizedPnl': self.safe_number(order_data, 'profit', 0),
|
||||
'realizedPnl': 0, # 对于持仓,已实现盈亏为0
|
||||
'liquidationPrice': None, # MT5 可能不提供
|
||||
'marginMode': 'cross',
|
||||
'percentage': percentage,
|
||||
'marginRatio': None,
|
||||
'collateral': None,
|
||||
'initialMargin': None, # 可能需要计算
|
||||
'initialMarginPercentage': None,
|
||||
'maintenanceMargin': None,
|
||||
'maintenanceMarginPercentage': None,
|
||||
# 'info': order_data,
|
||||
}
|
||||
|
||||
async def create_order(self, symbol, type, side, amount, price=None, params={}):
|
||||
"""创建订单"""
|
||||
await self.load_markets()
|
||||
@@ -650,6 +721,62 @@ class mt5(Exchange, ImplicitAPI):
|
||||
response = await self.private_get_orderclose(self.extend(request, params))
|
||||
return self.parse_order(response)
|
||||
|
||||
async def private_get(self, endpoint, params={}):
|
||||
"""发送私有 GET 请求"""
|
||||
return await self.fetch_private(endpoint, 'GET', params)
|
||||
|
||||
async def fetch_private(self, path, method='GET', params={}, headers=None, body=None):
|
||||
"""发送私有 API 请求"""
|
||||
url = self.urls['api']['private'] + '/' + path
|
||||
query = self.omit(params, self.extract_params(path))
|
||||
|
||||
if method == 'GET' and query:
|
||||
url += '?' + self.urlencode(query)
|
||||
|
||||
if self.verbose:
|
||||
print(f"🔧 发送请求: {url}")
|
||||
|
||||
import aiohttp
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as response:
|
||||
if response.status == 200:
|
||||
content = await response.text()
|
||||
# 直接返回内容,让 parse_json 处理
|
||||
return self.parse_json(content)
|
||||
else:
|
||||
error_text = await response.text()
|
||||
raise ExchangeError(f"HTTP {response.status}: {error_text}")
|
||||
except Exception as e:
|
||||
raise ExchangeError(f"请求失败: {e}")
|
||||
|
||||
def parse_json(self, response):
|
||||
"""解析响应,支持多种格式"""
|
||||
if not response:
|
||||
return response
|
||||
|
||||
response = response.strip()
|
||||
|
||||
# 处理常见的成功响应
|
||||
if response in ['OK', 'SUCCESS', 'True', 'true']:
|
||||
return True
|
||||
|
||||
# 处理常见的失败响应
|
||||
if response in ['FAIL', 'ERROR', 'False', 'false']:
|
||||
return False
|
||||
|
||||
# 尝试解析 JSON
|
||||
try:
|
||||
import json
|
||||
return json.loads(response)
|
||||
except json.JSONDecodeError:
|
||||
# 不是 JSON,返回原始响应
|
||||
return response
|
||||
except Exception as e:
|
||||
if self.verbose:
|
||||
print(f"响应解析失败: {e}, 响应: {response}")
|
||||
return response
|
||||
|
||||
def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
|
||||
"""签名请求"""
|
||||
base_url = self.urls['api'][api]
|
||||
|
||||
Reference in New Issue
Block a user