forked from kodorvan/stcs
Added the ability to get a list of open trades and limit orders, as well as their closures (previously it was possible only for the selected pair)
This commit is contained in:
@@ -14,6 +14,8 @@ import app.telegram.Keyboards.inline_keyboards as inline_markup
|
||||
logging.config.dictConfig(LOGGING_CONFIG)
|
||||
logger = logging.getLogger("futures")
|
||||
|
||||
processed_trade_ids = set()
|
||||
|
||||
|
||||
def safe_float(val) -> float:
|
||||
"""
|
||||
@@ -100,6 +102,13 @@ async def handle_execution_message(message, msg: dict) -> None:
|
||||
"""
|
||||
# logger.info(f"Исполнена сделка:\n{json.dumps(msg, indent=4, ensure_ascii=False)}")
|
||||
|
||||
trade_id = msg.get('data', [{}])[0].get('orderId')
|
||||
if trade_id in processed_trade_ids:
|
||||
logger.info(f"Уже обработана сделка {trade_id}, дублирующее уведомление игнорируется")
|
||||
return
|
||||
|
||||
processed_trade_ids.add(trade_id)
|
||||
|
||||
pnl = parse_pnl_from_msg(msg)
|
||||
tg_id = message.from_user.id
|
||||
|
||||
@@ -475,7 +484,7 @@ async def set_take_profit_stop_loss(tg_id: int, message, take_profit_price: floa
|
||||
|
||||
async def cancel_all_tp_sl_orders(tg_id, symbol):
|
||||
"""
|
||||
Отменяет все открытые ордера TP/SL для указанного символа.
|
||||
Отменяет лимитные ордера для указанного символа.
|
||||
"""
|
||||
api_key = await rq.get_bybit_api_key(tg_id)
|
||||
secret_key = await rq.get_bybit_secret_key(tg_id)
|
||||
@@ -487,26 +496,49 @@ async def cancel_all_tp_sl_orders(tg_id, symbol):
|
||||
|
||||
for order in orders:
|
||||
order_id = order.get('orderId')
|
||||
order_symbol = order.get('symbol')
|
||||
cancel_resp = client.cancel_order(category='linear', symbol=symbol, orderId=order_id)
|
||||
last_response = cancel_resp
|
||||
if cancel_resp.get('retCode') != 0:
|
||||
logger.warning(f"Не удалось отменить ордер {order_id}: {cancel_resp.get('retMsg')}")
|
||||
else:
|
||||
last_response = order_symbol
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при отмене ордеров TP/SL: {e}")
|
||||
logger.error(f"Ошибка при отмене ордера: {e}")
|
||||
|
||||
return last_response
|
||||
|
||||
|
||||
async def get_active_positions_by_symbol(tg_id, message):
|
||||
async def get_active_positions(tg_id, message):
|
||||
"""
|
||||
Показывает активные позиции пользователя.
|
||||
"""
|
||||
api_key = await rq.get_bybit_api_key(tg_id)
|
||||
secret_key = await rq.get_bybit_secret_key(tg_id)
|
||||
client = HTTP(api_key=api_key, api_secret=secret_key)
|
||||
|
||||
active_positions = client.get_positions(category='linear', settleCoin='USDT')
|
||||
|
||||
positions = active_positions.get('result', {}).get('list', [])
|
||||
active_symbols = [pos.get('symbol') for pos in positions if float(pos.get('size', 0)) > 0]
|
||||
|
||||
if active_symbols:
|
||||
await message.answer("📈 Ваши активные позиции:",
|
||||
reply_markup=inline_markup.create_trades_inline_keyboard(active_symbols))
|
||||
else:
|
||||
await message.answer("❗️ У вас нет активных позиций.", reply_markup=inline_markup.back_to_main)
|
||||
return
|
||||
|
||||
|
||||
async def get_active_positions_by_symbol(tg_id, symbol, message):
|
||||
"""
|
||||
Показывает активные позиции пользователя по символу.
|
||||
"""
|
||||
api_key = await rq.get_bybit_api_key(tg_id)
|
||||
secret_key = await rq.get_bybit_secret_key(tg_id)
|
||||
client = HTTP(api_key=api_key, api_secret=secret_key)
|
||||
symbol = await rq.get_symbol(tg_id)
|
||||
|
||||
active_positions = client.get_positions(category='linear', symbol=symbol)
|
||||
|
||||
positions = active_positions.get('result', {}).get('list', [])
|
||||
pos = positions[0] if positions else None
|
||||
|
||||
@@ -526,14 +558,33 @@ async def get_active_positions_by_symbol(tg_id, message):
|
||||
|
||||
await message.answer(text, reply_markup=inline_markup.create_close_deal_markup(symbol))
|
||||
|
||||
async def get_active_orders(tg_id, message):
|
||||
"""
|
||||
Показывает активные лимитные ордера пользователя.
|
||||
"""
|
||||
api_key = await rq.get_bybit_api_key(tg_id)
|
||||
secret_key = await rq.get_bybit_secret_key(tg_id)
|
||||
client = HTTP(api_key=api_key, api_secret=secret_key)
|
||||
|
||||
async def get_active_orders_by_symbol(tg_id, message):
|
||||
response = client.get_open_orders(category='linear', settleCoin='USDT', orderType='Limit')
|
||||
orders = response.get('result', {}).get('list', [])
|
||||
limit_orders = [order for order in orders if order.get('orderType') == 'Limit']
|
||||
|
||||
if limit_orders:
|
||||
symbols = [order['symbol'] for order in limit_orders]
|
||||
await message.answer("📈 Ваши активные лимитные ордера:",
|
||||
reply_markup=inline_markup.create_trades_inline_keyboard_limits(symbols))
|
||||
else:
|
||||
await message.answer("❗️ У вас нет активных лимитных ордеров.", reply_markup=inline_markup.back_to_main)
|
||||
return
|
||||
|
||||
|
||||
async def get_active_orders_by_symbol(tg_id, symbol, message):
|
||||
"""
|
||||
Показывает активные лимитные ордера пользователя по символу.
|
||||
"""
|
||||
api_key = await rq.get_bybit_api_key(tg_id)
|
||||
secret_key = await rq.get_bybit_secret_key(tg_id)
|
||||
symbol = await rq.get_symbol(tg_id)
|
||||
client = HTTP(api_key=api_key, api_secret=secret_key)
|
||||
|
||||
active_orders = client.get_open_orders(category='linear', symbol=symbol)
|
||||
@@ -561,7 +612,7 @@ async def get_active_orders_by_symbol(tg_id, message):
|
||||
)
|
||||
texts.append(text)
|
||||
|
||||
await message.answer("\n\n".join(texts), reply_markup=inline_markup.create_close_deal_markup(symbol))
|
||||
await message.answer("\n\n".join(texts), reply_markup=inline_markup.create_close_limit_markup(symbol))
|
||||
|
||||
|
||||
async def close_user_trade(tg_id: int, symbol: str, message):
|
||||
@@ -574,7 +625,6 @@ async def close_user_trade(tg_id: int, symbol: str, message):
|
||||
secret_key = await rq.get_bybit_secret_key(tg_id)
|
||||
data_risk_stgs = await rq.get_user_risk_management_settings(tg_id)
|
||||
|
||||
limit_price = await rq.get_limit_price(tg_id)
|
||||
include_fee = data_risk_stgs.get('commission_fee', 'Нет') == 'Да'
|
||||
client = HTTP(api_key=api_key, api_secret=secret_key)
|
||||
|
||||
@@ -589,66 +639,29 @@ async def close_user_trade(tg_id: int, symbol: str, message):
|
||||
position = positions_list[0]
|
||||
qty = abs(safe_float(position.get('size')))
|
||||
side = position.get('side')
|
||||
entry_price = safe_float(position.get('avgPrice'))
|
||||
if qty == 0:
|
||||
return False
|
||||
|
||||
orders = client.get_open_orders(category='linear', symbol=symbol)
|
||||
cancel_resp = await cancel_all_tp_sl_orders(tg_id, symbol)
|
||||
open_orders_list = orders.get('result', {}).get('list', [])
|
||||
order_id = open_orders_list[0].get('orderId') if open_orders_list else None
|
||||
close_side = "Sell" if side == "Buy" else "Buy"
|
||||
ticker_resp = client.get_tickers(category="linear", symbol=symbol)
|
||||
current_price = 0.0
|
||||
if ticker_resp.get('retCode') == 0:
|
||||
result = ticker_resp.get('result', {})
|
||||
ticker_list = []
|
||||
if isinstance(result, dict):
|
||||
ticker_list = result.get('list', [])
|
||||
elif isinstance(result, list):
|
||||
ticker_list = result
|
||||
if ticker_list:
|
||||
current_price = float(ticker_list[0].get('lastPrice', 0.0))
|
||||
|
||||
place_resp = client.place_order(
|
||||
category="linear",
|
||||
symbol=symbol,
|
||||
side=close_side,
|
||||
orderType="Market",
|
||||
qty=str(qty),
|
||||
timeInForce="GTC",
|
||||
reduceOnly=True
|
||||
)
|
||||
|
||||
if place_resp.get('retCode', -1) == 0:
|
||||
trade_fee = 0
|
||||
try:
|
||||
trades_resp = client.get_closed_pnl(category="linear", symbol=symbol)
|
||||
if trades_resp.get('retCode') == 0:
|
||||
trades = trades_resp.get('result', {}).get('list', [])
|
||||
for trade in trades:
|
||||
if trade.get('orderId') == order_id:
|
||||
trade_fee += float(trade.get('execFee', 0))
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при получении сделок: {e}")
|
||||
trade_fee = 0
|
||||
|
||||
pnl = (current_price - entry_price) * qty if side == "Buy" else (entry_price - current_price) * qty
|
||||
|
||||
if include_fee:
|
||||
pnl -= trade_fee
|
||||
pnl_percent = (pnl / (entry_price * qty)) * 100 if entry_price * qty > 0 else 0
|
||||
return True
|
||||
else:
|
||||
if message:
|
||||
await message.answer(f"Ошибка закрытия сделки {symbol}.",
|
||||
reply_markup=inline_markup.back_to_main)
|
||||
return False
|
||||
place_resp = client.place_order(
|
||||
category="linear",
|
||||
symbol=symbol,
|
||||
side=close_side,
|
||||
orderType="Market",
|
||||
qty=str(qty),
|
||||
timeInForce="GTC",
|
||||
reduceOnly=True
|
||||
)
|
||||
|
||||
if place_resp.get('retCode') == 0:
|
||||
await message.answer(f"Сделка {symbol} успешно закрыта.", reply_markup=inline_markup.back_to_main)
|
||||
return True
|
||||
else:
|
||||
await message.answer(f"Ошибка закрытия сделки {symbol}.", reply_markup=inline_markup.back_to_main)
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка закрытия сделки {symbol} для пользователя {tg_id}: {e}", exc_info=True)
|
||||
if message:
|
||||
await message.answer("Произошла ошибка при закрытии сделки.", reply_markup=inline_markup.back_to_main)
|
||||
await message.answer("Произошла ошибка при закрытии сделки.", reply_markup=inline_markup.back_to_main)
|
||||
return False
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user