From b074d1d8a14a953e4b2953997558dd146ec13860 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 19 Sep 2025 14:41:18 +0500 Subject: [PATCH 1/6] Added the ability to set a price trigger and limit --- .../functions/condition_settings/settings.py | 97 ++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/app/telegram/functions/condition_settings/settings.py b/app/telegram/functions/condition_settings/settings.py index 0f3116d..bfc9eec 100644 --- a/app/telegram/functions/condition_settings/settings.py +++ b/app/telegram/functions/condition_settings/settings.py @@ -5,7 +5,7 @@ from aiogram.types import Message, CallbackQuery from aiogram.fsm.context import FSMContext import app.telegram.database.requests as rq from app.states.States import condition_settings - +from app.states.States import state_limit_price, state_update_entry_type, state_trigger_price from logger_helper.logger_helper import LOGGING_CONFIG logging.config.dictConfig(LOGGING_CONFIG) @@ -23,8 +23,24 @@ async def reg_new_user_default_condition_settings(id): async def main_settings_message(id, message): + data = await rq.get_user_main_settings(id) + entry_order_type = data['entry_order_type'] + + if entry_order_type == "Market": + entry_order_type_rus = "Маркет" + elif entry_order_type == "Limit": + entry_order_type_rus = "Лимит" + else: + entry_order_type_rus = "Условный" + + trigger_price = data['trigger_price'] or 0.0 + limit_price = data['limit_order_price'] or 0.0 + text = f""" Условия запуска - Таймер: установить таймер / удалить таймер +- Тип позиции: {entry_order_type_rus} +- Триггер цена: {trigger_price:,.4f} +- Лимит цена: {limit_price:,.4f} """ await message.answer(text=text, parse_mode='html', reply_markup=inline_markup.condition_settings_markup) @@ -75,6 +91,85 @@ async def delete_timer_callback(callback: CallbackQuery, state: FSMContext): await callback.answer() +@condition_settings_router.callback_query(F.data == 'clb_update_entry_type') +async def update_entry_type_message(callback: CallbackQuery, state: FSMContext) -> None: + """ + Запрашивает у пользователя тип входа в позицию (Market или Limit). + """ + await state.set_state(state_update_entry_type.entry_type) + await callback.message.answer("Выберите тип входа в позицию:", reply_markup=inline_markup.entry_order_type_markup) + await callback.answer() + + +@condition_settings_router.callback_query(lambda c: c.data and c.data.startswith('entry_order_type:')) +async def entry_order_type_callback(callback: CallbackQuery, state: FSMContext) -> None: + """ + Обработка выбора типа входа в позицию. + Если Limit, запрашивает цену лимитного ордера. + Если Market — обновляет настройки. + """ + order_type = callback.data.split(':')[1] + + if order_type not in ['Market', 'Limit']: + await callback.answer("Ошибка выбора", show_alert=True) + return + + if order_type == 'Limit': + order_type_rus = 'Лимит' + else: + order_type_rus = 'Маркет' + try: + await state.update_data(entry_order_type=order_type) + await rq.update_entry_order_type(callback.from_user.id, order_type) + await callback.message.answer(f"Выбран тип входа {order_type_rus}", + reply_markup=inline_markup.back_to_condition_settings) + await callback.answer() + except Exception as e: + logger.error("Произошла ошибка при обновлении типа входа в позицию: %s", e) + await callback.message.answer("Произошла ошибка при обновлении типа входа в позицию", + reply_markup=inline_markup.back_to_condition_settings) + await state.clear() + + +@condition_settings_router.callback_query(F.data == 'clb_change_limit_price') +async def set_limit_price_callback(callback: CallbackQuery, state: FSMContext) -> None: + await state.set_state(state_limit_price.price) + await callback.message.answer("Введите цену лимитного ордера:", reply_markup=inline_markup.cancel) + await callback.answer() + + +@condition_settings_router.message(state_limit_price.price) +async def process_limit_price_input(message: Message, state: FSMContext) -> None: + try: + price = float(message.text) + await state.update_data(price=price) + await rq.update_limit_price(tg_id=message.from_user.id, price=price) + await message.answer(f"Цена лимитного ордера установлена: {price}", reply_markup=inline_markup.back_to_condition_settings) + await state.clear() + except ValueError: + await message.reply("Пожалуйста, введите корректную цену.") + + +@condition_settings_router.callback_query(F.data == 'clb_change_trigger_price') +async def change_trigger_price_callback(callback: CallbackQuery, state: FSMContext) -> None: + await state.set_state(state_trigger_price.price) + await callback.message.answer("Введите цену триггера:", reply_markup=inline_markup.cancel) + await callback.answer() + + +@condition_settings_router.message(state_trigger_price.price) +async def process_trigger_price_input(message: Message, state: FSMContext) -> None: + try: + price = float(message.text) + await state.update_data(price=price) + await rq.update_trigger_price(tg_id=message.from_user.id, price=price) + await message.answer(f"Цена триггера установлена: {price}", reply_markup=inline_markup.back_to_condition_settings) + await state.clear() + except ValueError: + await message.reply("Пожалуйста, введите корректную цену.") + + + async def filter_volatility_message(message, state): text = '''Фильтр волатильности -- 2.51.0 From 887b46c1d433a67f90ca055e50fea3b19da395a5 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 19 Sep 2025 14:42:12 +0500 Subject: [PATCH 2/6] Added a condition for the price trigger --- app/states/States.py | 3 +++ app/telegram/functions/main_settings/settings.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/states/States.py b/app/states/States.py index 6b9c7d1..ba211fa 100644 --- a/app/states/States.py +++ b/app/states/States.py @@ -21,6 +21,9 @@ class state_limit_price(StatesGroup): """FSM состояние для установки лимита.""" price = State() +class state_trigger_price(StatesGroup): + """FSM состояние для установки лимита.""" + price = State() class CloseTradeTimerState(StatesGroup): """FSM состояние ожидания задержки перед закрытием сделки.""" diff --git a/app/telegram/functions/main_settings/settings.py b/app/telegram/functions/main_settings/settings.py index 6b133bc..92ee946 100644 --- a/app/telegram/functions/main_settings/settings.py +++ b/app/telegram/functions/main_settings/settings.py @@ -2,7 +2,6 @@ import logging.config import app.telegram.Keyboards.inline_keyboards as inline_markup -from pybit.unified_trading import HTTP import app.telegram.database.requests as rq from aiogram.types import Message, CallbackQuery @@ -125,6 +124,7 @@ async def state_trading_mode(callback: CallbackQuery, state): logger.error("Ошибка при обновлении режима торговли: %s", e) + async def switch_mode_enabled_message(message, state): await state.set_state(update_main_settings.switch_mode_enabled) -- 2.51.0 From 2fb8cb4acb491caa9b0d10910a2cc49839dd7046 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 19 Sep 2025 14:42:46 +0500 Subject: [PATCH 3/6] Added the ability to open a deal at a trigger price --- app/services/Bybit/functions/Futures.py | 86 +++++++++++++++++------ app/services/Bybit/functions/functions.py | 71 +------------------ 2 files changed, 66 insertions(+), 91 deletions(-) diff --git a/app/services/Bybit/functions/Futures.py b/app/services/Bybit/functions/Futures.py index a6ce0e2..4f82c68 100644 --- a/app/services/Bybit/functions/Futures.py +++ b/app/services/Bybit/functions/Futures.py @@ -1,6 +1,7 @@ import asyncio import logging.config import time +import json import app.services.Bybit.functions.balance as balance_g import app.services.Bybit.functions.price_symbol as price_symbol import app.telegram.database.requests as rq @@ -106,6 +107,7 @@ def format_order_details_position(data): symbol = msg.get("symbol", "N/A") order_type = msg.get("orderType", "N/A") side = msg.get("side", "") + trigger_price = msg.get("triggerPrice", "N/A") movement = "" if side.lower() == "buy": @@ -149,6 +151,28 @@ def format_order_details_position(data): f"Движение: {movement}\n" ) return text + elif order_status.lower() == "untriggered": + text = ( + f"Условный ордер создан:\n" + f"Торговая пара: {symbol}\n" + f"Цена: {price:.6f}\n" + f"Триггер цена: {trigger_price}\n" + f"Количество: {qty}\n" + f"Тип ордера: {order_type}\n" + f"Движение: {movement}\n" + ) + return text + elif order_status.lower() == "deactivated": + text = ( + f"Условный ордер отменен:\n" + f"Торговая пара: {symbol}\n" + f"Цена: {price:.6f}\n" + f"Триггер цена: {trigger_price}\n" + f"Количество: {qty}\n" + f"Тип ордера: {order_type}\n" + f"Движение: {movement}\n" + ) + return text return None @@ -200,6 +224,7 @@ async def handle_execution_message(message, msg): """ tg_id = message.from_user.id data = msg.get("data", [{}])[0] + logger.info("исполнео:", json.dumps(msg, indent=4)) data_main_risk_stgs = await rq.get_user_risk_management_settings(tg_id) commission_fee = data_main_risk_stgs.get("commission_fee", "ДА") pnl = parse_pnl_from_msg(msg) @@ -275,7 +300,7 @@ async def handle_order_message(message, msg: dict) -> None: Обработчик сообщений об исполнении ордера. Логирует событие и проверяет условия для мартингейла и TP. """ - # logger.info(f"Исполнен ордер:\n{json.dumps(msg, indent=4, ensure_ascii=False)}") + logger.info(f"Исполнен ордер:\n{json.dumps(msg, indent=4, ensure_ascii=False)}") trade_info = format_order_details_position(msg) @@ -322,7 +347,7 @@ async def open_position( bybit_margin_mode = ( "ISOLATED_MARGIN" if margin_mode == "Isolated" else "REGULAR_MARGIN" ) - + trigger_price = await rq.get_trigger_price(tg_id) limit_price = None if order_type == "Limit": limit_price = await rq.get_limit_price(tg_id) @@ -426,18 +451,33 @@ async def open_position( if bybit_margin_mode == "ISOLATED_MARGIN": # Открываем позицию - response = client.place_order( - category="linear", - symbol=symbol, - side=side, - orderType=order_type, - qty=str(quantity), - price=( - str(limit_price) if order_type == "Limit" and limit_price else None - ), - timeInForce="GTC", - orderLinkId=f"deal_{symbol}_{int(time.time())}", - ) + if trigger_price and float(trigger_price) > 0: + response = client.place_order( + category="linear", + symbol=symbol, + side=side, + orderType="Stop" if order_type == "Conditional" else order_type, + qty=str(quantity), + price=(str(limit_price) if order_type == "Limit" and limit_price else None), + triggerPrice=str(trigger_price), + triggerBy="LastPrice", + triggerDirection=2 if side == "Buy" else 1, + timeInForce="GTC", + orderLinkId=f"deal_{symbol}_{int(time.time())}", + ) + else: + # Обычный ордер, без триггера + response = client.place_order( + category="linear", + symbol=symbol, + side=side, + orderType=order_type, + qty=str(quantity), + price=(str(limit_price) if order_type == "Limit" and limit_price else None), + timeInForce="GTC", + orderLinkId=f"deal_{symbol}_{int(time.time())}", + ) + if response.get("retCode", -1) == 0: return True if response.get("retCode", -1) != 0: @@ -544,6 +584,9 @@ async def open_position( slOrderType=sl_order_type, slLimitPrice=str(sl_limit_price) if sl_limit_price else None, tpslMode=tpsl_mode, + triggerPrice=str(trigger_price) if trigger_price and float(trigger_price) > 0 else None, + triggerBy="LastPrice" if trigger_price and float(trigger_price) > 0 else None, + triggerDirection=2 if side == "Buy" else 1 if trigger_price and float(trigger_price) > 0 else None, timeInForce="GTC", orderLinkId=f"deal_{symbol}_{int(time.time())}", ) @@ -724,20 +767,20 @@ async def get_active_orders(tg_id, message): """ client = await get_bybit_client(tg_id) response = client.get_open_orders( - category="linear", settleCoin="USDT", orderType="Limit" + category="linear", settleCoin="USDT", orderType="Limit" and "Market" ) orders = response.get("result", {}).get("list", []) - limit_orders = [order for order in orders if order.get("orderType") == "Limit"] + limit_orders = [order for order in orders] 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 @@ -752,12 +795,12 @@ async def get_active_orders_by_symbol(tg_id, symbol, message): limit_orders = [ order for order in active_orders.get("result", {}).get("list", []) - if order.get("orderType") == "Limit" ] + logger.info(limit_orders) if not limit_orders: await message.answer( - "Нет активных лимитных ордеров по данной торговой паре.", + "Нет активных ордеров по данной торговой паре.", reply_markup=inline_markup.back_to_main, ) return @@ -769,9 +812,8 @@ async def get_active_orders_by_symbol(tg_id, symbol, message): f"Тип ордера: {order.get('orderType')}\n" f"Сторона: {order.get('side')}\n" f"Цена: {order.get('price')}\n" + f"Триггер цена: {order.get('triggerPrice')}\n" f"Количество: {order.get('qty')}\n" - f"Тейк-профит: {order.get('takeProfit')}\n" - f"Стоп-лосс: {order.get('stopLoss')}\n" ) texts.append(text) diff --git a/app/services/Bybit/functions/functions.py b/app/services/Bybit/functions/functions.py index ec19984..6f6b817 100644 --- a/app/services/Bybit/functions/functions.py +++ b/app/services/Bybit/functions/functions.py @@ -10,7 +10,6 @@ from app.services.Bybit.functions.Futures import (close_user_trade, set_take_pro get_active_positions_by_symbol, get_active_orders_by_symbol, get_active_positions, get_active_orders, cancel_all_tp_sl_orders, open_position, close_trade_after_delay, safe_float, - calculate_total_budget, get_bybit_client, ) from app.services.Bybit.functions.balance import get_balance import app.telegram.Keyboards.inline_keyboards as inline_markup @@ -18,9 +17,9 @@ import app.telegram.Keyboards.inline_keyboards as inline_markup import app.telegram.database.requests as rq from aiogram.types import Message, CallbackQuery from app.services.Bybit.functions.price_symbol import get_price -import app.services.Bybit.functions.balance as balance_g +# import app.services.Bybit.functions.balance as balance_g -from app.states.States import (state_update_entry_type, state_update_symbol, state_limit_price, +from app.states.States import (state_update_symbol, SetTP_SL_State, CloseTradeTimerState) from aiogram.fsm.context import FSMContext @@ -121,72 +120,6 @@ async def update_symbol_for_trade(message: Message, state: FSMContext) -> None: await state.clear() -@router_functions_bybit_trade.callback_query(F.data == 'clb_update_entry_type') -async def update_entry_type_message(callback: CallbackQuery, state: FSMContext) -> None: - """ - Запрашивает у пользователя тип входа в позицию (Market или Limit). - """ - await state.set_state(state_update_entry_type.entry_type) - await callback.message.answer("Выберите тип входа в позицию:", reply_markup=inline_markup.entry_order_type_markup) - await callback.answer() - - -@router_functions_bybit_trade.callback_query(lambda c: c.data and c.data.startswith('entry_order_type:')) -async def entry_order_type_callback(callback: CallbackQuery, state: FSMContext) -> None: - """ - Обработка выбора типа входа в позицию. - Если Limit, запрашивает цену лимитного ордера. - Если Market — обновляет настройки. - """ - order_type = callback.data.split(':')[1] - - if order_type not in ['Market', 'Limit']: - await callback.answer("Ошибка выбора", show_alert=True) - return - - if order_type == 'Limit': - await state.set_state(state_limit_price.price) - await callback.message.answer("Введите цену:", reply_markup=inline_markup.cancel) - await callback.answer() - return - - try: - await state.update_data(entry_order_type=order_type) - await rq.update_entry_order_type(callback.from_user.id, order_type) - await callback.message.answer("Выбран тип входа в позицию по текущей цене:", - reply_markup=inline_markup.start_trading_markup) - await callback.answer() - except Exception as e: - logger.error("Произошла ошибка при обновлении типа входа в позицию: %s", e) - await callback.message.answer("Произошла ошибка при обновлении типа входа в позицию", - reply_markup=inline_markup.back_to_main) - await state.clear() - - -@router_functions_bybit_trade.message(state_limit_price.price) -async def set_limit_price(message: Message, state: FSMContext) -> None: - """ - Обрабатывает ввод цены лимитного ордера, проверяет формат и сохраняет настройки. - """ - try: - price = float(message.text) - if price <= 0: - await message.answer("Цена должна быть положительным числом. Попробуйте снова.", - reply_markup=inline_markup.cancel) - return - except ValueError: - await message.answer("Некорректный формат цены. Введите число.", reply_markup=inline_markup.cancel) - return - - await state.update_data(entry_order_type='Limit', limit_price=price) - - await rq.update_entry_order_type(message.from_user.id, 'Limit') - await rq.update_limit_price(message.from_user.id, price) - - await message.answer(f"Триггер цена установлена: {price}", reply_markup=inline_markup.start_trading_markup) - await state.clear() - - @router_functions_bybit_trade.callback_query(F.data == "clb_start_chatbot_trading") async def start_trading_process(callback: CallbackQuery) -> None: """ -- 2.51.0 From 29bb6bd0a866da60bc3252c529a8be62606c6bcd Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 19 Sep 2025 14:43:42 +0500 Subject: [PATCH 4/6] The buttons for selecting the type of entry and setting the limit and trigger prices have been updated. --- app/telegram/Keyboards/inline_keyboards.py | 41 +++++++++++++--------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/app/telegram/Keyboards/inline_keyboards.py b/app/telegram/Keyboards/inline_keyboards.py index 4ec335f..023c36a 100644 --- a/app/telegram/Keyboards/inline_keyboards.py +++ b/app/telegram/Keyboards/inline_keyboards.py @@ -30,13 +30,11 @@ special_settings_markup = InlineKeyboardMarkup(inline_keyboard=[ InlineKeyboardButton(text="Риск-менеджмент", callback_data='clb_change_risk_management_settings')], [InlineKeyboardButton(text="Условия запуска", callback_data='clb_change_condition_settings')], - # InlineKeyboardButton(text="Дополнительные параметры", callback_data='clb_change_additional_settings')], + # InlineKeyboardButton(text="Дополнительные параметры", callback_data='clb_change_additional_settings')], [InlineKeyboardButton(text="Подключить Bybit", callback_data='clb_new_user_connect_bybit_api_message')], back_btn_to_main ]) - - connect_bybit_api_markup = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text="Подключить Bybit", callback_data='clb_new_user_connect_bybit_api')] ]) @@ -45,7 +43,7 @@ trading_markup = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text="Настройки", callback_data='clb_settings_message')], [InlineKeyboardButton(text="Мои сделки", callback_data='clb_my_deals')], [InlineKeyboardButton(text="Указать торговую пару", callback_data='clb_update_trading_pair')], - [InlineKeyboardButton(text="Начать торговать", callback_data='clb_update_entry_type')], + [InlineKeyboardButton(text="Начать торговать", callback_data='clb_start_chatbot_trading')], [InlineKeyboardButton(text="Остановить торговлю", callback_data='clb_stop_trading')], ]) @@ -61,14 +59,12 @@ cancel = InlineKeyboardMarkup(inline_keyboard=[ entry_order_type_markup = InlineKeyboardMarkup( inline_keyboard=[ [ - InlineKeyboardButton(text="Текущая цена", callback_data="entry_order_type:Market"), - InlineKeyboardButton(text="Триггер цена", callback_data="entry_order_type:Limit"), + InlineKeyboardButton(text="Маркет", callback_data="entry_order_type:Market"), + InlineKeyboardButton(text="Лимит", callback_data="entry_order_type:Limit"), ], back_btn_to_main ] ) - - back_to_main = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text="На главную", callback_data='back_to_main')], ]) @@ -83,7 +79,7 @@ main_settings_markup = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text='Коэффициент Мартингейла', callback_data='clb_change_martingale_factor'), InlineKeyboardButton(text='Сбросить шаги Мартингейла', callback_data='clb_change_martingale_reset')], - [InlineKeyboardButton(text='Максимальное кол-во ставок', callback_data='clb_change_maximum_quantity')], + [InlineKeyboardButton(text='Максимальное кол-во ставок', callback_data='clb_change_maximum_quantity')], back_btn_list_settings, back_btn_to_main @@ -101,8 +97,10 @@ risk_management_settings_markup = InlineKeyboardMarkup(inline_keyboard=[ ]) condition_settings_markup = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text='Режим торговли', callback_data='clb_change_mode'), - InlineKeyboardButton(text='Таймер', callback_data='clb_change_timer')], + [InlineKeyboardButton(text='Таймер', callback_data='clb_change_timer'), + InlineKeyboardButton(text='Тип позиции', callback_data='clb_update_entry_type')], + [InlineKeyboardButton(text='Триггер цена', callback_data='clb_change_trigger_price'), + InlineKeyboardButton(text='Лимит цена', callback_data='clb_change_limit_price')], # # [InlineKeyboardButton(text='Фильтр волатильности', callback_data='clb_change_filter_volatility'), # InlineKeyboardButton(text='Внешние сигналы', callback_data='clb_change_external_cues')], @@ -116,6 +114,11 @@ condition_settings_markup = InlineKeyboardMarkup(inline_keyboard=[ back_btn_to_main ]) +back_to_condition_settings = InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text='Назад', callback_data='clb_change_condition_settings')], + back_btn_to_main +]) + additional_settings_markup = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text='Сохранить шаблон', callback_data='clb_change_save_pattern'), InlineKeyboardButton(text='Автозапуск', callback_data='clb_change_auto_start')], @@ -130,7 +133,7 @@ trading_mode_markup = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text="Лонг", callback_data="trade_mode_long"), InlineKeyboardButton(text="Шорт", callback_data="trade_mode_short"), InlineKeyboardButton(text="Свитч", callback_data="trade_mode_switch")], - # InlineKeyboardButton(text="Смарт", callback_data="trade_mode_smart")], + # InlineKeyboardButton(text="Смарт", callback_data="trade_mode_smart")], back_btn_list_settings, back_btn_to_main @@ -145,7 +148,7 @@ margin_type_markup = InlineKeyboardMarkup(inline_keyboard=[ trigger_markup = InlineKeyboardMarkup(inline_keyboard=[ # ИЗМЕНИТЬ НА INLINE [InlineKeyboardButton(text='Ручной', callback_data="clb_trigger_manual")], - # [InlineKeyboardButton(text='TradingView', callback_data="clb_trigger_tradingview")], + # [InlineKeyboardButton(text='TradingView', callback_data="clb_trigger_tradingview")], [InlineKeyboardButton(text="Автоматический", callback_data="clb_trigger_auto")], back_btn_list_settings, back_btn_to_main @@ -162,11 +165,12 @@ buttons_on_off_markup = InlineKeyboardMarkup(inline_keyboard=[ # ИЗМЕНИТ ]) my_deals_select_markup = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text='Открытые сделки', callback_data="clb_open_deals"), - InlineKeyboardButton(text='Лимитные ордера', callback_data="clb_open_orders")], + [InlineKeyboardButton(text='Позиции', callback_data="clb_open_deals"), + InlineKeyboardButton(text='Ордера', callback_data="clb_open_orders")], back_btn_to_main ]) + def create_trades_inline_keyboard(trades): builder = InlineKeyboardBuilder() for trade in trades: @@ -174,6 +178,7 @@ def create_trades_inline_keyboard(trades): builder.adjust(2) return builder.as_markup() + def create_trades_inline_keyboard_limits(trades): builder = InlineKeyboardBuilder() for trade in trades: @@ -190,12 +195,14 @@ def create_close_deal_markup(symbol: str) -> InlineKeyboardMarkup: back_btn_to_main ]) + def create_close_limit_markup(symbol: str) -> InlineKeyboardMarkup: return InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text="Закрыть лимитный ордер", callback_data=f"close_limit:{symbol}")], + [InlineKeyboardButton(text="Закрыть ордер", callback_data=f"close_limit:{symbol}")], back_btn_to_main ]) + timer_markup = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text="Установить таймер", callback_data="clb_set_timer")], [InlineKeyboardButton(text="Удалить таймер", callback_data="clb_delete_timer")], @@ -214,4 +221,4 @@ stop_choice_markup = InlineKeyboardMarkup( switch_state_markup = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text='По направлению', callback_data="clb_long_switch"), InlineKeyboardButton(text='Против направления', callback_data="clb_short_switch")], -]) \ No newline at end of file +]) -- 2.51.0 From 49d4bb26bfc33cd3aa84b003d4b29997eb80e564 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 19 Sep 2025 14:45:34 +0500 Subject: [PATCH 5/6] Added trigger price --- app/services/Bybit/functions/Futures.py | 5 +---- app/telegram/database/models.py | 10 +++++++++- app/telegram/database/requests.py | 26 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/app/services/Bybit/functions/Futures.py b/app/services/Bybit/functions/Futures.py index 4f82c68..553b10f 100644 --- a/app/services/Bybit/functions/Futures.py +++ b/app/services/Bybit/functions/Futures.py @@ -1,7 +1,6 @@ import asyncio import logging.config import time -import json import app.services.Bybit.functions.balance as balance_g import app.services.Bybit.functions.price_symbol as price_symbol import app.telegram.database.requests as rq @@ -224,7 +223,6 @@ async def handle_execution_message(message, msg): """ tg_id = message.from_user.id data = msg.get("data", [{}])[0] - logger.info("исполнео:", json.dumps(msg, indent=4)) data_main_risk_stgs = await rq.get_user_risk_management_settings(tg_id) commission_fee = data_main_risk_stgs.get("commission_fee", "ДА") pnl = parse_pnl_from_msg(msg) @@ -300,7 +298,7 @@ async def handle_order_message(message, msg: dict) -> None: Обработчик сообщений об исполнении ордера. Логирует событие и проверяет условия для мартингейла и TP. """ - logger.info(f"Исполнен ордер:\n{json.dumps(msg, indent=4, ensure_ascii=False)}") + # logger.info(f"Исполнен ордер:\n{json.dumps(msg, indent=4, ensure_ascii=False)}") trade_info = format_order_details_position(msg) @@ -796,7 +794,6 @@ async def get_active_orders_by_symbol(tg_id, symbol, message): order for order in active_orders.get("result", {}).get("list", []) ] - logger.info(limit_orders) if not limit_orders: await message.answer( diff --git a/app/telegram/database/models.py b/app/telegram/database/models.py index d20ad8c..73c56cd 100644 --- a/app/telegram/database/models.py +++ b/app/telegram/database/models.py @@ -153,7 +153,8 @@ class User_Main_Settings(Base): martingale_step = mapped_column(Integer(), default=1) maximal_quantity = mapped_column(Integer(), default=10) entry_order_type = mapped_column(String(10), default='Market') - limit_order_price = mapped_column(Numeric(18, 15), nullable=True) + limit_order_price = mapped_column(Numeric(18, 15), nullable=True, default=0) + trigger_price = mapped_column(Numeric(18, 15), nullable=True, default=0) last_side = mapped_column(String(10), default='Buy') trading_start_stop = mapped_column(Integer(), default=0) @@ -306,3 +307,10 @@ async def async_main(): if not result.first(): logger.info("Заполение таблицы последнего направления") await conn.execute(User_Main_Settings.__table__.insert().values(last_side=side)) + + order_type = ['Limit', 'Market'] + for typ in order_type: + result = await conn.execute(select(User_Main_Settings).where(User_Main_Settings.entry_order_type == typ)) + if not result.first(): + logger.info("Заполение таблицы типов ордеров") + await conn.execute(User_Main_Settings.__table__.insert().values(entry_order_type=typ)) diff --git a/app/telegram/database/requests.py b/app/telegram/database/requests.py index fb41694..f796e1a 100644 --- a/app/telegram/database/requests.py +++ b/app/telegram/database/requests.py @@ -318,6 +318,7 @@ async def get_user_main_settings(tg_id): 'maximal_quantity': user.maximal_quantity, 'entry_order_type': user.entry_order_type, 'limit_order_price': user.limit_order_price, + 'trigger_price': user.trigger_price, 'martingale_step': user.martingale_step, 'last_side': user.last_side, 'trading_start_stop': user.trading_start_stop, @@ -438,6 +439,31 @@ async def update_entry_order_type(tg_id, order_type): await session.commit() +async def update_trigger_price(tg_id, price): + """Обновить условную цену пользователя.""" + async with async_session() as session: + await session.execute( + update(UMS) + .where(UMS.tg_id == tg_id) + .values(trigger_price=str(price)) + ) + await session.commit() + +async def get_trigger_price(tg_id): + """Получить условную цену пользователя как float, либо None.""" + async with async_session() as session: + result = await session.execute( + select(UMS.trigger_price) + .where(UMS.tg_id == tg_id) + ) + price = result.scalar_one_or_none() + if price: + try: + return float(price) + except ValueError: + return None + return None + async def get_limit_price(tg_id): """Получить лимитную цену пользователя как float, либо None.""" async with async_session() as session: -- 2.51.0 From 4bbff680aa1b2a4dfeea829a41a71c6d8afb7363 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 19 Sep 2025 14:57:08 +0500 Subject: [PATCH 6/6] Update --- app/services/Bybit/functions/Futures.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/services/Bybit/functions/Futures.py b/app/services/Bybit/functions/Futures.py index 553b10f..7a314ac 100644 --- a/app/services/Bybit/functions/Futures.py +++ b/app/services/Bybit/functions/Futures.py @@ -247,6 +247,9 @@ async def handle_execution_message(message, msg): if trade_info: await message.answer(f"{trade_info}", reply_markup=inline_markup.back_to_main) + if data is not None: + await rq.update_trigger_price(tg_id, 0.0) + if closed_size == 0: side = data.get("side", "") @@ -299,7 +302,10 @@ async def handle_order_message(message, msg: dict) -> None: Логирует событие и проверяет условия для мартингейла и TP. """ # logger.info(f"Исполнен ордер:\n{json.dumps(msg, indent=4, ensure_ascii=False)}") - + data = msg.get("data", [{}])[0] + tg_id = message.from_user.id + if data is not None: + await rq.update_trigger_price(tg_id, 0.0) trade_info = format_order_details_position(msg) if trade_info: -- 2.51.0