mt5添加获取持仓方法
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@ build/
|
||||
dist/
|
||||
ccxt/__pycache__/
|
||||
__pycache__/
|
||||
.vscode/settings.json
|
||||
|
||||
@@ -52,6 +52,7 @@ class mt5(Exchange, ImplicitAPI):
|
||||
'fetchOHLCV': True,
|
||||
'fetchOpenOrders': True,
|
||||
'fetchOrder': True,
|
||||
'fetchPositions': True,
|
||||
'fetchOrderBook': True,
|
||||
'fetchTicker': True,
|
||||
'fetchTickers': True,
|
||||
@@ -561,6 +562,132 @@ class mt5(Exchange, ImplicitAPI):
|
||||
|
||||
return tickers
|
||||
|
||||
async def fetch_positions(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))
|
||||
|
||||
# 使用基类的 parse_positions 方法,让 parse_position 自己判断是否为有效持仓
|
||||
result = self.parse_positions(response, [symbol] if symbol else None, params)
|
||||
|
||||
# 过滤掉 None 值(无效持仓)
|
||||
result = [position for position in result if position is not None]
|
||||
|
||||
# 应用限制
|
||||
if limit is not None:
|
||||
result = result[:limit]
|
||||
|
||||
return result
|
||||
|
||||
def parse_position(self, position, market: Market = None):
|
||||
"""解析持仓信息 - 根据真实数据调整"""
|
||||
# 获取市场信息
|
||||
market_id = self.safe_string(position, 'symbol')
|
||||
symbol = self.safe_symbol(market_id, market, '/')
|
||||
|
||||
# 检查是否为有效持仓
|
||||
state = self.safe_string(position, 'state')
|
||||
lots = self.safe_number(position, 'lots', 0)
|
||||
close_lots = self.safe_number(position, 'closeLots', 0)
|
||||
|
||||
# 只有状态为已成交且有未平仓数量的才是有效持仓
|
||||
# 根据你的业务逻辑调整这个判断条件
|
||||
if state != 'Filled' or lots <= close_lots:
|
||||
return None
|
||||
|
||||
# 解析时间戳
|
||||
timestamp = self.parse8601(self.safe_string(position, 'openTime'))
|
||||
open_timestamp_utc = self.safe_integer(position, 'openTimestampUTC')
|
||||
if open_timestamp_utc:
|
||||
timestamp = open_timestamp_utc
|
||||
|
||||
# 确定持仓方向
|
||||
order_type = self.safe_string(position, 'orderType')
|
||||
side = 'long' if order_type == 'Buy' else 'short'
|
||||
|
||||
# 获取持仓数量 (未平仓数量)
|
||||
contracts = lots - close_lots
|
||||
|
||||
# 获取价格信息
|
||||
entry_price = self.safe_number(position, 'openPrice', 0)
|
||||
current_price = self.safe_number(position, 'closePrice', entry_price)
|
||||
mark_price = current_price
|
||||
|
||||
# 计算持仓价值
|
||||
contract_size = self.safe_number(position, 'contractSize', 1.0)
|
||||
notional = contracts * entry_price * contract_size if contracts and entry_price and contract_size else None
|
||||
|
||||
# 计算盈亏 - 使用 profit 字段
|
||||
unrealized_pnl = self.safe_number(position, 'profit', 0)
|
||||
|
||||
# 计算保证金信息
|
||||
initial_margin = None
|
||||
initial_margin_percentage = None
|
||||
|
||||
# 如果有持仓价值,计算保证金
|
||||
if notional is not None and notional != 0:
|
||||
leverage = self.safe_number(position, 'leverage', 100)
|
||||
initial_margin = notional / leverage
|
||||
initial_margin_percentage = 1 / leverage
|
||||
|
||||
# 计算强平价格 (简化计算)
|
||||
liquidation_price = None
|
||||
if entry_price is not None:
|
||||
if side == 'long':
|
||||
liquidation_price = entry_price * 0.95 # 假设 5% 强平线
|
||||
else:
|
||||
liquidation_price = entry_price * 1.05 # 假设 5% 强平线
|
||||
|
||||
# 计算百分比盈亏
|
||||
percentage = None
|
||||
if entry_price is not None and mark_price is not None and entry_price != 0:
|
||||
if side == 'long':
|
||||
percentage = (mark_price - entry_price) / entry_price
|
||||
else:
|
||||
percentage = (entry_price - mark_price) / entry_price
|
||||
|
||||
# 获取止损止盈价格
|
||||
stop_loss_price = self.safe_number(position, 'stopLoss')
|
||||
take_profit_price = self.safe_number(position, 'takeProfit')
|
||||
|
||||
# 返回标准化的持仓信息
|
||||
return {
|
||||
'info': position,
|
||||
'id': self.safe_string(position, 'ticket'),
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': self.iso8601(timestamp) if timestamp else None,
|
||||
'lastUpdateTimestamp': timestamp,
|
||||
'initialMargin': initial_margin,
|
||||
'initialMarginPercentage': initial_margin_percentage,
|
||||
'maintenanceMargin': None,
|
||||
'maintenanceMarginPercentage': None,
|
||||
'entryPrice': entry_price,
|
||||
'notional': notional,
|
||||
'leverage': self.safe_number(position, 'leverage', 100),
|
||||
'unrealizedPnl': unrealized_pnl,
|
||||
'realizedPnl': self.safe_number(position, 'realizedPnl', 0),
|
||||
'contracts': contracts,
|
||||
'contractSize': contract_size,
|
||||
'marginRatio': None,
|
||||
'liquidationPrice': liquidation_price,
|
||||
'markPrice': mark_price,
|
||||
'lastPrice': mark_price,
|
||||
'collateral': initial_margin,
|
||||
'marginMode': 'cross',
|
||||
'side': side,
|
||||
'percentage': percentage,
|
||||
'stopLossPrice': stop_loss_price,
|
||||
'takeProfitPrice': take_profit_price,
|
||||
'hedged': None,
|
||||
}
|
||||
|
||||
async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
|
||||
"""异步获取未平仓订单 - 修复版本"""
|
||||
if not hasattr(self, 'token') or not self.token:
|
||||
@@ -731,68 +858,6 @@ 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()
|
||||
|
||||
185
ccxt/mt5.py
185
ccxt/mt5.py
@@ -58,6 +58,7 @@ class mt5(Exchange, ImplicitAPI):
|
||||
'fetchOHLCV': True,
|
||||
'fetchOpenOrders': True,
|
||||
'fetchOrder': True,
|
||||
'fetchPositions': True,
|
||||
'fetchOrderBook': True,
|
||||
'fetchTicker': True,
|
||||
'fetchTickers': True,
|
||||
@@ -582,6 +583,132 @@ class mt5(Exchange, ImplicitAPI):
|
||||
'isInvestor': self.safe_value(response, 'isInvestor', False),
|
||||
}
|
||||
|
||||
def fetch_positions(self, symbol=None, since=None, limit=None, params={}):
|
||||
"""异步获取持仓信息"""
|
||||
if not hasattr(self, 'token') or not self.token:
|
||||
self.get_token()
|
||||
|
||||
request = {
|
||||
'id': self.token,
|
||||
}
|
||||
|
||||
response = self.private_get_openedorders(self.extend(request, params))
|
||||
|
||||
# 使用基类的 parse_positions 方法,让 parse_position 自己判断是否为有效持仓
|
||||
result = self.parse_positions(response, [symbol] if symbol else None, params)
|
||||
|
||||
# 过滤掉 None 值(无效持仓)
|
||||
result = [position for position in result if position is not None]
|
||||
|
||||
# 应用限制
|
||||
if limit is not None:
|
||||
result = result[:limit]
|
||||
|
||||
return result
|
||||
|
||||
def parse_position(self, position, market: Market = None):
|
||||
"""解析持仓信息 - 根据真实数据调整"""
|
||||
# 获取市场信息
|
||||
market_id = self.safe_string(position, 'symbol')
|
||||
symbol = self.safe_symbol(market_id, market, '/')
|
||||
|
||||
# 检查是否为有效持仓
|
||||
state = self.safe_string(position, 'state')
|
||||
lots = self.safe_number(position, 'lots', 0)
|
||||
close_lots = self.safe_number(position, 'closeLots', 0)
|
||||
|
||||
# 只有状态为已成交且有未平仓数量的才是有效持仓
|
||||
# 根据你的业务逻辑调整这个判断条件
|
||||
if state != 'Filled' or lots <= close_lots:
|
||||
return None
|
||||
|
||||
# 解析时间戳
|
||||
timestamp = self.parse8601(self.safe_string(position, 'openTime'))
|
||||
open_timestamp_utc = self.safe_integer(position, 'openTimestampUTC')
|
||||
if open_timestamp_utc:
|
||||
timestamp = open_timestamp_utc
|
||||
|
||||
# 确定持仓方向
|
||||
order_type = self.safe_string(position, 'orderType')
|
||||
side = 'long' if order_type == 'Buy' else 'short'
|
||||
|
||||
# 获取持仓数量 (未平仓数量)
|
||||
contracts = lots - close_lots
|
||||
|
||||
# 获取价格信息
|
||||
entry_price = self.safe_number(position, 'openPrice', 0)
|
||||
current_price = self.safe_number(position, 'closePrice', entry_price)
|
||||
mark_price = current_price
|
||||
|
||||
# 计算持仓价值
|
||||
contract_size = self.safe_number(position, 'contractSize', 1.0)
|
||||
notional = contracts * entry_price * contract_size if contracts and entry_price and contract_size else None
|
||||
|
||||
# 计算盈亏 - 使用 profit 字段
|
||||
unrealized_pnl = self.safe_number(position, 'profit', 0)
|
||||
|
||||
# 计算保证金信息
|
||||
initial_margin = None
|
||||
initial_margin_percentage = None
|
||||
|
||||
# 如果有持仓价值,计算保证金
|
||||
if notional is not None and notional != 0:
|
||||
leverage = self.safe_number(position, 'leverage', 100)
|
||||
initial_margin = notional / leverage
|
||||
initial_margin_percentage = 1 / leverage
|
||||
|
||||
# 计算强平价格 (简化计算)
|
||||
liquidation_price = None
|
||||
if entry_price is not None:
|
||||
if side == 'long':
|
||||
liquidation_price = entry_price * 0.95 # 假设 5% 强平线
|
||||
else:
|
||||
liquidation_price = entry_price * 1.05 # 假设 5% 强平线
|
||||
|
||||
# 计算百分比盈亏
|
||||
percentage = None
|
||||
if entry_price is not None and mark_price is not None and entry_price != 0:
|
||||
if side == 'long':
|
||||
percentage = (mark_price - entry_price) / entry_price
|
||||
else:
|
||||
percentage = (entry_price - mark_price) / entry_price
|
||||
|
||||
# 获取止损止盈价格
|
||||
stop_loss_price = self.safe_number(position, 'stopLoss')
|
||||
take_profit_price = self.safe_number(position, 'takeProfit')
|
||||
|
||||
# 返回标准化的持仓信息
|
||||
return {
|
||||
'info': position,
|
||||
'id': self.safe_string(position, 'ticket'),
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': self.iso8601(timestamp) if timestamp else None,
|
||||
'lastUpdateTimestamp': timestamp,
|
||||
'initialMargin': initial_margin,
|
||||
'initialMarginPercentage': initial_margin_percentage,
|
||||
'maintenanceMargin': None,
|
||||
'maintenanceMarginPercentage': None,
|
||||
'entryPrice': entry_price,
|
||||
'notional': notional,
|
||||
'leverage': self.safe_number(position, 'leverage', 100),
|
||||
'unrealizedPnl': unrealized_pnl,
|
||||
'realizedPnl': self.safe_number(position, 'realizedPnl', 0),
|
||||
'contracts': contracts,
|
||||
'contractSize': contract_size,
|
||||
'marginRatio': None,
|
||||
'liquidationPrice': liquidation_price,
|
||||
'markPrice': mark_price,
|
||||
'lastPrice': mark_price,
|
||||
'collateral': initial_margin,
|
||||
'marginMode': 'cross',
|
||||
'side': side,
|
||||
'percentage': percentage,
|
||||
'stopLossPrice': stop_loss_price,
|
||||
'takeProfitPrice': take_profit_price,
|
||||
'hedged': None,
|
||||
}
|
||||
|
||||
def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
|
||||
"""获取未平仓订单"""
|
||||
self.load_token()
|
||||
@@ -730,64 +857,6 @@ class mt5(Exchange, ImplicitAPI):
|
||||
}
|
||||
return self.safe_string(types, type, type)
|
||||
|
||||
def parse_position(self, order_data, market: Market = None):
|
||||
"""从订单数据解析持仓"""
|
||||
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,
|
||||
'unrealizedPnl': self.safe_number(order_data, 'profit', 0),
|
||||
'realizedPnl': 0,
|
||||
'liquidationPrice': None,
|
||||
'marginMode': 'cross',
|
||||
'percentage': percentage,
|
||||
'marginRatio': None,
|
||||
'collateral': None,
|
||||
'initialMargin': None,
|
||||
'initialMarginPercentage': None,
|
||||
'maintenanceMargin': None,
|
||||
'maintenanceMarginPercentage': None,
|
||||
'info': order_data,
|
||||
}
|
||||
|
||||
def create_order(self, symbol, type, side, amount, price=None, params={}):
|
||||
"""创建订单"""
|
||||
self.load_token()
|
||||
|
||||
Reference in New Issue
Block a user