diff --git a/app/bybit/open_positions.py b/app/bybit/open_positions.py index 147d4b7..07a7d4c 100644 --- a/app/bybit/open_positions.py +++ b/app/bybit/open_positions.py @@ -10,6 +10,7 @@ from app.bybit.get_functions.get_tickers import get_tickers from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG from app.bybit.set_functions.set_leverage import set_leverage from app.bybit.set_functions.set_margin_mode import set_margin_mode +from app.bybit.set_functions.set_switch_position_mode import set_switch_position_mode from app.helper_functions import get_liquidation_price, safe_float logging.config.dictConfig(LOGGING_CONFIG) @@ -90,6 +91,10 @@ async def start_trading_cycle( price_for_cals = trigger_price if po_trigger_price is not None else price_symbol total_commission = price_for_cals * qty_formatted * commission_fee_percent + await set_switch_position_mode( + tg_id=tg_id, + symbol=symbol, + mode=0) await set_margin_mode(tg_id=tg_id, margin_mode=margin_type) await set_leverage( tg_id=tg_id, @@ -291,10 +296,10 @@ async def open_positions( if (liq_long > 0 or liq_short > 0) and price_for_cals > 0: if side == "Buy": base_tp = price_for_cals + (price_for_cals - liq_long) - take_profit_price = base_tp + commission_fee_percent + take_profit_price = base_tp + commission_fee_percent / qty_formatted else: base_tp = price_for_cals - (liq_short - price_for_cals) - take_profit_price = base_tp - commission_fee_percent + take_profit_price = base_tp - commission_fee_percent / qty_formatted take_profit_price = max(take_profit_price, 0) else: take_profit_price = None @@ -302,11 +307,11 @@ async def open_positions( stop_loss_price = None else: if side == "Buy": - take_profit_price = price_for_cals * (1 + take_profit_percent / 100) + commission_fee_percent - stop_loss_price = price_for_cals * (1 - stop_loss_percent / 100) - commission_fee_percent + take_profit_price = price_for_cals * (1 + take_profit_percent / 100) + commission_fee_percent / qty_formatted + stop_loss_price = price_for_cals * (1 - stop_loss_percent / 100) else: - take_profit_price = price_for_cals * (1 - take_profit_percent / 100) - commission_fee_percent - stop_loss_price = price_for_cals * (1 + stop_loss_percent / 100) + commission_fee_percent + take_profit_price = price_for_cals * (1 - take_profit_percent / 100) - commission_fee_percent / qty_formatted + stop_loss_price = price_for_cals * (1 + stop_loss_percent / 100) take_profit_price = max(take_profit_price, 0) stop_loss_price = max(stop_loss_price, 0) diff --git a/app/bybit/telegram_message_handler.py b/app/bybit/telegram_message_handler.py index 8071c03..5de198c 100644 --- a/app/bybit/telegram_message_handler.py +++ b/app/bybit/telegram_message_handler.py @@ -41,11 +41,26 @@ class TelegramMessageHandler: if order_status == "Filled" or order_status not in status_map: return None + user_auto_trading = await rq.get_user_auto_trading( + tg_id=tg_id, symbol=symbol + ) + auto_trading = ( + user_auto_trading.auto_trading if user_auto_trading else False + ) + user_deals_data = await rq.get_user_deal_by_symbol( + tg_id=tg_id, symbol=symbol + ) + text = ( f"Торговая пара: {symbol}\n" - f"Количество: {qty}\n" f"Движение: {side_rus}\n" ) + + if user_deals_data is not None and auto_trading: + text += f"Текущая ставка: {user_deals_data.order_quantity}\n" + else: + text += f"Количество: {qty}\n" + if price and price != "0": text += f"Цена: {price}\n" if take_profit and take_profit != "Нет данных": @@ -67,13 +82,21 @@ class TelegramMessageHandler: closed_size = format_value(execution.get("closedSize")) symbol = format_value(execution.get("symbol")) exec_price = format_value(execution.get("execPrice")) - exec_fee = format_value(execution.get("execFee")) + exec_qty = format_value(execution.get("execQty")) + exec_fees = format_value(execution.get("execFee")) + fee_rate = format_value(execution.get("feeRate")) side = format_value(execution.get("side")) side_rus = ( "Покупка" if side == "Buy" else "Продажа" if side == "Sell" else "Нет данных" ) + if safe_float(exec_fees) == 0: + exec_fee = safe_float(exec_price) * safe_float(exec_qty) * safe_float( + fee_rate + ) + else: + exec_fee = safe_float(exec_fees) if safe_float(closed_size) == 0: await rq.set_fee_user_auto_trading( @@ -86,9 +109,7 @@ class TelegramMessageHandler: get_total_fee = user_auto_trading.total_fee total_fee = safe_float(exec_fee) + safe_float(get_total_fee) - await rq.set_total_fee_user_auto_trading( - tg_id=tg_id, symbol=symbol, total_fee=total_fee - ) + if user_auto_trading is not None and user_auto_trading.fee is not None: fee = user_auto_trading.fee @@ -109,17 +130,24 @@ class TelegramMessageHandler: ) text = f"{header}\n" f"Торговая пара: {symbol}\n" + auto_trading = ( + user_auto_trading.auto_trading if user_auto_trading else False + ) user_deals_data = await rq.get_user_deal_by_symbol( tg_id=tg_id, symbol=symbol ) - exec_bet = user_deals_data.order_quantity - base_quantity = user_deals_data.base_quantity + if user_deals_data is not None and auto_trading: + await rq.set_total_fee_user_auto_trading( + tg_id=tg_id, symbol=symbol, total_fee=total_fee + ) + text += f"Текущая ставка: {user_deals_data.order_quantity}\n" + else: + text += f"Количество: {exec_qty}\n" text += ( f"Цена исполнения: {exec_price}\n" - f"Текущая ставка: {exec_bet}\n" f"Движение: {side_rus}\n" - f"Комиссия за сделку: {exec_fee}\n" + f"Комиссия: {exec_fee:.8f}\n" ) if safe_float(closed_size) > 0: @@ -129,9 +157,6 @@ class TelegramMessageHandler: chat_id=tg_id, text=text, reply_markup=kbi.profile_bybit ) - auto_trading = ( - user_auto_trading.auto_trading if user_auto_trading else False - ) user_symbols = user_auto_trading.symbol if user_auto_trading else None if ( @@ -154,6 +179,7 @@ class TelegramMessageHandler: await rq.set_fee_user_auto_trading( tg_id=tg_id, symbol=symbol, fee=0 ) + base_quantity = user_deals_data.base_quantity await rq.set_order_quantity( tg_id=tg_id, order_quantity=base_quantity ) @@ -174,7 +200,7 @@ class TelegramMessageHandler: "Risk is too high for this trade": "❗️ Риск сделки слишком высок для продолжения", "ab not enough for new order": "❗️ Недостаточно средств для продолжения торговли", "InvalidRequestError": "❗️ Недостаточно средств для размещения нового ордера с заданным количеством и плечом.", - "The number of contracts exceeds maximum limit allowed": "❗️ Количество контрактов превышает допустимое максимальное количество контрактов", + "The number of contracts exceeds maximum limit allowed": "❗️ Превышен максимальный лимит ставки", } error_text = errors.get( res, "❗️ Не удалось открыть новую сделку" diff --git a/app/telegram/handlers/main_settings/additional_settings.py b/app/telegram/handlers/main_settings/additional_settings.py index df2d1d9..1f9099e 100644 --- a/app/telegram/handlers/main_settings/additional_settings.py +++ b/app/telegram/handlers/main_settings/additional_settings.py @@ -7,6 +7,7 @@ from aiogram.types import CallbackQuery, Message import app.telegram.keyboards.inline as kbi import database.request as rq from app.bybit.get_functions.get_instruments_info import get_instruments_info +from app.bybit.get_functions.get_positions import get_active_positions_by_symbol, get_active_orders_by_symbol from app.bybit.set_functions.set_leverage import set_leverage from app.bybit.set_functions.set_margin_mode import set_margin_mode from app.helper_functions import is_int, is_number, safe_float @@ -211,6 +212,31 @@ async def settings_for_margin_type( """ try: await state.clear() + symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id) + deals = await get_active_positions_by_symbol( + tg_id=callback_query.from_user.id, symbol=symbol + ) + position = next((d for d in deals if d.get("symbol") == symbol), None) + + if position: + size = position.get("size", 0) + else: + size = 0 + + if safe_float(size) > 0: + await callback_query.answer( + text="У вас есть активная позиция по текущей паре", + ) + return + + orders = await get_active_orders_by_symbol( + tg_id=callback_query.from_user.id, symbol=symbol) + + if orders is not None: + await callback_query.answer( + text="У вас есть активный ордер по текущей паре", + ) + return await callback_query.message.edit_text( text="Выберите тип маржи:\n\n" "Примечание: Если у вас есть открытые позиции, то маржа примениться ко всем позициям", diff --git a/app/telegram/handlers/settings.py b/app/telegram/handlers/settings.py index 727b667..592e063 100644 --- a/app/telegram/handlers/settings.py +++ b/app/telegram/handlers/settings.py @@ -123,8 +123,8 @@ async def risk_management(callback_query: CallbackQuery, state: FSMContext) -> N await callback_query.message.edit_text( text=f"Риск-менеджмент:\n\n" - f"- Процент изменения цены для фиксации прибыли: {take_profit_percent}%\n" - f"- Процент изменения цены для фиксации убытка: {stop_loss_percent}%\n\n" + f"- Процент изменения цены для фиксации прибыли: {take_profit_percent:.2f}%\n" + f"- Процент изменения цены для фиксации убытка: {stop_loss_percent:.2f}%\n\n" f"- Комиссия биржи для расчета прибыли: {commission_fee_rus}\n\n", reply_markup=kbi.risk_management, ) diff --git a/app/telegram/handlers/start_trading.py b/app/telegram/handlers/start_trading.py index 36c8801..440f35c 100644 --- a/app/telegram/handlers/start_trading.py +++ b/app/telegram/handlers/start_trading.py @@ -7,7 +7,7 @@ from aiogram.types import CallbackQuery import app.telegram.keyboards.inline as kbi import database.request as rq -from app.bybit.get_functions.get_positions import get_active_positions_by_symbol +from app.bybit.get_functions.get_positions import get_active_positions_by_symbol, get_active_orders_by_symbol from app.bybit.open_positions import start_trading_cycle from app.helper_functions import safe_float from app.telegram.tasks.tasks import ( @@ -33,6 +33,7 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non """ try: await state.clear() + tg_id = callback_query.from_user.id symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id) deals = await get_active_positions_by_symbol( tg_id=callback_query.from_user.id, symbol=symbol @@ -46,7 +47,16 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non if safe_float(size) > 0: await callback_query.answer( - text="У вас есть активная позиция", + text="У вас есть активная позиция по текущей паре", + ) + return + + orders = await get_active_orders_by_symbol( + tg_id=callback_query.from_user.id, symbol=symbol) + + if orders is not None: + await callback_query.answer( + text="У вас есть активный ордер по текущей паре", ) return @@ -73,22 +83,29 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non symbol=symbol, auto_trading=True, ) + await rq.set_total_fee_user_auto_trading( + tg_id=tg_id, symbol=symbol, total_fee=0 + ) + await rq.set_fee_user_auto_trading( + tg_id=tg_id, symbol=symbol, fee=0 + ) res = await start_trading_cycle( tg_id=callback_query.from_user.id, ) error_messages = { - "Limit price is out min price": "Цена лимитного ордера меньше минимального", - "Limit price is out max price": "Цена лимитного ордера больше максимального", + "Limit price is out min price": "Цена лимитного ордера меньше допустимого", + "Limit price is out max price": "Цена лимитного ордера больше допустимого", "Risk is too high for this trade": "Риск сделки превышает допустимый убыток", "estimated will trigger liq": "Лимитный ордер может вызвать мгновенную ликвидацию. Проверьте параметры ордера.", "ab not enough for new order": "Недостаточно средств для создания нового ордера", "InvalidRequestError": "Произошла ошибка при запуске торговли.", - "Order does not meet minimum order value": "Сумма ордера не достаточна для запуска торговли", - "position idx not match position mode": "Ошибка режима позиции для данного инструмента", - "Qty invalid": "Некорректное значение ордера для данного инструмента", - "The number of contracts exceeds maximum limit allowed": "️️Количество контрактов превышает допустимое максимальное количество контрактов", - "The number of contracts exceeds minimum limit allowed": "️️Количество контрактов превышает допустимое минимальное количество контрактов", + "Order does not meet minimum order value": "Сумма ставки меньше допустимого для запуска торговли. " + "Увеличьте ставку, чтобы запустить торговлю", + "position idx not match position mode": "Измените режим позиции, чтобы запустить торговлю", + "Qty invalid": "Некорректное значение ставки для данного инструмента", + "The number of contracts exceeds maximum limit allowed": "️️Превышен максимальный лимит ставки", + "The number of contracts exceeds minimum limit allowed": "️️Лимит ставки меньше минимально допустимого", } if res == "OK": @@ -112,7 +129,7 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non except Exception as e: await callback_query.answer(text="Произошла ошибка при запуске торговли") logger.error( - "Error processing command long for user %s: %s", + "Error processing command start_trading for user %s: %s", callback_query.from_user.id, e, ) diff --git a/run.py b/run.py index e86f084..110b270 100644 --- a/run.py +++ b/run.py @@ -46,7 +46,6 @@ async def main(): with contextlib.suppress(asyncio.CancelledError): await ws_task await tg_task - await web_socket.clear_user_sockets() except Exception as e: logger.error("Bot stopped with error: %s", e)