mt5添加获取持仓方法
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
ccxt/__pycache__/
|
ccxt/__pycache__/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
.vscode/settings.json
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ class mt5(Exchange, ImplicitAPI):
|
|||||||
'fetchOHLCV': True,
|
'fetchOHLCV': True,
|
||||||
'fetchOpenOrders': True,
|
'fetchOpenOrders': True,
|
||||||
'fetchOrder': True,
|
'fetchOrder': True,
|
||||||
|
'fetchPositions': True,
|
||||||
'fetchOrderBook': True,
|
'fetchOrderBook': True,
|
||||||
'fetchTicker': True,
|
'fetchTicker': True,
|
||||||
'fetchTickers': True,
|
'fetchTickers': True,
|
||||||
@@ -561,6 +562,132 @@ class mt5(Exchange, ImplicitAPI):
|
|||||||
|
|
||||||
return tickers
|
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={}):
|
async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
|
||||||
"""异步获取未平仓订单 - 修复版本"""
|
"""异步获取未平仓订单 - 修复版本"""
|
||||||
if not hasattr(self, 'token') or not self.token:
|
if not hasattr(self, 'token') or not self.token:
|
||||||
@@ -731,68 +858,6 @@ class mt5(Exchange, ImplicitAPI):
|
|||||||
}
|
}
|
||||||
return self.safe_string(types, type, type)
|
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={}):
|
async def create_order(self, symbol, type, side, amount, price=None, params={}):
|
||||||
"""创建订单"""
|
"""创建订单"""
|
||||||
await self.load_markets()
|
await self.load_markets()
|
||||||
|
|||||||
185
ccxt/mt5.py
185
ccxt/mt5.py
@@ -58,6 +58,7 @@ class mt5(Exchange, ImplicitAPI):
|
|||||||
'fetchOHLCV': True,
|
'fetchOHLCV': True,
|
||||||
'fetchOpenOrders': True,
|
'fetchOpenOrders': True,
|
||||||
'fetchOrder': True,
|
'fetchOrder': True,
|
||||||
|
'fetchPositions': True,
|
||||||
'fetchOrderBook': True,
|
'fetchOrderBook': True,
|
||||||
'fetchTicker': True,
|
'fetchTicker': True,
|
||||||
'fetchTickers': True,
|
'fetchTickers': True,
|
||||||
@@ -582,6 +583,132 @@ class mt5(Exchange, ImplicitAPI):
|
|||||||
'isInvestor': self.safe_value(response, 'isInvestor', False),
|
'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={}):
|
def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
|
||||||
"""获取未平仓订单"""
|
"""获取未平仓订单"""
|
||||||
self.load_token()
|
self.load_token()
|
||||||
@@ -730,64 +857,6 @@ class mt5(Exchange, ImplicitAPI):
|
|||||||
}
|
}
|
||||||
return self.safe_string(types, type, type)
|
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={}):
|
def create_order(self, symbol, type, side, amount, price=None, params={}):
|
||||||
"""创建订单"""
|
"""创建订单"""
|
||||||
self.load_token()
|
self.load_token()
|
||||||
|
|||||||
Reference in New Issue
Block a user