From 90329576318436dc2992d722216e2a6837f47844 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Wed, 27 Aug 2025 12:54:45 +0500 Subject: [PATCH] Added documentation, added websocket and tasks --- app/services/Bybit/functions/Futures.py | 134 +++++------ app/services/Bybit/functions/functions.py | 62 ++--- app/telegram/database/requests.py | 266 ++++++++++++++-------- 3 files changed, 247 insertions(+), 215 deletions(-) diff --git a/app/services/Bybit/functions/Futures.py b/app/services/Bybit/functions/Futures.py index 3ca6713..a7104be 100644 --- a/app/services/Bybit/functions/Futures.py +++ b/app/services/Bybit/functions/Futures.py @@ -1,12 +1,9 @@ import asyncio -import nest_asyncio - import time +import json import logging.config from pybit import exceptions -from pybit.unified_trading import HTTP, WebSocket -from websocket import WebSocketConnectionClosedException - +from pybit.unified_trading import HTTP from logger_helper.logger_helper import LOGGING_CONFIG import app.services.Bybit.functions.price_symbol as price_symbol import app.services.Bybit.functions.balance as balance_g @@ -16,8 +13,6 @@ import app.telegram.Keyboards.inline_keyboards as inline_markup logging.config.dictConfig(LOGGING_CONFIG) logger = logging.getLogger("futures") -nest_asyncio.apply() - def safe_float(val) -> float: """ @@ -33,6 +28,53 @@ def safe_float(val) -> float: return 0.0 +def format_trade_details_position(data): + """ + Форматирует информацию о сделке в виде строки. + """ + msg = data.get('data', [{}])[0] + + closed_size = float(msg.get('closedSize', 0)) + symbol = msg.get('symbol', 'N/A') + entry_price = float(msg.get('execPrice', 0)) + qty = float(msg.get('execQty', 0)) + order_type = msg.get('orderType', 'N/A') + side = msg.get('side', '') + commission_fee = float(msg.get('execFee', 0)) + pnl = float(msg.get('execPnl', 0)) + + movement = '' + if side.lower() == 'buy': + movement = 'Покупка' + elif side.lower() == 'sell': + movement = 'Продажа' + else: + movement = side + + if closed_size > 0: + return ( + f"Сделка закрыта:\n" + f"Торговая пара: {symbol}\n" + f"Цена исполнения: {entry_price:.6f}\n" + f"Количество: {qty}\n" + f"Закрыто позиций: {closed_size}\n" + f"Тип ордера: {order_type}\n" + f"Движение: {movement}\n" + f"Комиссия за сделку: {commission_fee:.6f}\n" + f"Реализованная прибыль: {pnl:.6f} USDT" + ) + else: + return ( + f"Сделка открыта:\n" + f"Торговая пара: {symbol}\n" + f"Цена исполнения: {entry_price:.6f}\n" + f"Количество: {qty}\n" + f"Тип ордера: {order_type}\n" + f"Движение: {movement}\n" + f"Комиссия за сделку: {commission_fee:.6f}" + ) + + def parse_pnl_from_msg(msg) -> float: """ Извлекает реализованную прибыль/убыток из сообщения. @@ -49,8 +91,6 @@ async def handle_execution_message(message, msg: dict) -> None: Обработчик сообщений об исполнении сделки. Логирует событие и проверяет условия для мартингейла и TP. """ - logger.info(f"Исполнена сделка: {msg}") - await message.answer(f"Исполнена сделка: {msg}") pnl = parse_pnl_from_msg(msg) tg_id = message.from_user.id @@ -66,6 +106,9 @@ async def handle_execution_message(message, msg: dict) -> None: positions_list = positions_resp.get('result', {}).get('list', []) position = positions_list[0] if positions_list else None + trade_info = format_trade_details_position(msg) + await message.answer(f"{trade_info}", reply_markup=inline_markup.back_to_main) + liquidation_threshold = -100 if pnl <= liquidation_threshold: @@ -94,64 +137,6 @@ async def handle_execution_message(message, msg: dict) -> None: await rq.update_martingale_step(tg_id, 0) -async def start_execution_ws(tg_id, message) -> None: - """ - Запускает WebSocket для отслеживания исполнения сделок в режиме реального времени. - Переподключается при ошибках. - """ - api_key = await rq.get_bybit_api_key(tg_id) - api_secret = await rq.get_bybit_secret_key(tg_id) - - reconnect_delay = 5 - - while True: - try: - ws = WebSocket(api_key=api_key, - api_secret=api_secret, - testnet=False, - channel_type="private") - - async def on_execution(msg): - await handle_execution_message(message, msg) - - def on_execution_sync(msg): - asyncio.create_task(on_execution(msg)) - - ws.execution_stream(on_execution_sync) - - while True: - await asyncio.sleep(1) - - except WebSocketConnectionClosedException: - logging.warning("WebSocket закрыт, переподключение через 5 секунд...") - await asyncio.sleep(reconnect_delay) - - except Exception as e: - logging.error(f"Ошибка WebSocket: {e}") - await asyncio.sleep(reconnect_delay) - - -async def info_access_open_deal(message, symbol, trade_mode, margin_mode, leverage, qty, tp, sl, entry_price, - limit_price, order_type) -> None: - """ - Отправляет сообщение об успешном открытии позиции или выставлении лимитного ордера. - """ - human_margin_mode = 'Isolated' if margin_mode == 'ISOLATED_MARGIN' else 'Cross' - text = ( - f"{'Позиция была успешна открыта' if order_type == 'Market' else 'Лимитный ордер установлен'}!\n" - f"Торговая пара: {symbol}\n" - f"Цена входа: {entry_price if order_type == 'Market' else round(limit_price, 5)}\n" - f"Движение: {trade_mode}\n" - f"Тип-маржи: {human_margin_mode}\n" - f"Кредитное плечо: {leverage}x\n" - f"Количество: {qty}\n" - f"Тейк-профит: {round(tp, 5)}\n" - f"Стоп-лосс: {round(sl, 5)}\n" - ) - - await message.answer(text=text, parse_mode='html', reply_markup=inline_markup.create_close_deal_markup(symbol)) - - async def error_max_step(message) -> None: """ Сообщение об ошибке превышения максимального количества шагов мартингейла. @@ -340,10 +325,6 @@ async def open_position(tg_id, message, side: str, margin_mode: str, tpsl_mode=' ) if response.get('retCode', -1) == 0: - await info_access_open_deal(message, symbol, data_main_stgs.get('trading_mode', ''), - bybit_margin_mode, - data_main_stgs.get('size_leverage', 1), next_quantity, take_profit_price, - stop_loss_price, entry_price, limit_price, order_type=order_type) await rq.update_martingale_step(tg_id, current_martingale_step) return True else: @@ -417,7 +398,7 @@ async def set_take_profit_stop_loss(tg_id: int, message, take_profit_price: floa return client = HTTP(api_key=api_key, api_secret=secret_key) - await cancel_all_tp_sl_orders(client, symbol) + await cancel_all_tp_sl_orders(tg_id, symbol) try: try: @@ -648,15 +629,6 @@ async def close_user_trade(tg_id: int, symbol: str, message): if include_fee: pnl -= trade_fee pnl_percent = (pnl / (entry_price * qty)) * 100 if entry_price * qty > 0 else 0 - - text = ( - f"Сделка {symbol} успешно закрыта.\n" - f"Цена входа: {entry_price if entry_price else limit_price}\n" - f"Цена закрытия: {current_price}\n" - f"Прибыль: {pnl:.4f} USDT ({pnl_percent:.2f}%)\n" - f"{'Включая комиссию биржи' if include_fee else 'Без учета комиссии'}" - ) - await message.answer(text) return True else: if message: diff --git a/app/services/Bybit/functions/functions.py b/app/services/Bybit/functions/functions.py index 40fadc7..60d0098 100644 --- a/app/services/Bybit/functions/functions.py +++ b/app/services/Bybit/functions/functions.py @@ -1,14 +1,12 @@ -import asyncio -import logging.config +import logging.config from aiogram import F, Router -from app.tasks.tasks import active_close_tasks, active_start_tasks +from app.tasks.tasks import handle_stop_close_trade, handle_start_close_trade, handle_stop_trading, handle_start_trading from logger_helper.logger_helper import LOGGING_CONFIG -from app.services.Bybit.functions.Futures import (close_user_trade, close_trade_after_delay, - trading_cycle, open_position, set_take_profit_stop_loss, \ +from app.services.Bybit.functions.Futures import (close_user_trade, open_position, set_take_profit_stop_loss, \ get_active_positions_by_symbol, get_active_orders_by_symbol, - start_execution_ws) + ) from app.services.Bybit.functions.balance import get_balance import app.telegram.Keyboards.inline_keyboards as inline_markup @@ -35,15 +33,16 @@ async def clb_start_bybit_trade_message(callback: CallbackQuery) -> None: Обработка нажатия кнопок запуска торговли или возврата в главное меню. Отправляет информацию о балансе, символе, цене и инструкциях по торговле. """ - balance = await get_balance(callback.from_user.id, callback.message) - price = await get_price(callback.from_user.id) + user_id = callback.from_user.id + balance = await get_balance(user_id, callback.message) + price = await get_price(user_id) if balance: - symbol = await rq.get_symbol(callback.from_user.id) + symbol = await rq.get_symbol(user_id) text = ( f"💎 Торговля на Bybit\n\n" - f"⚖️ Ваш баланс (USDT): {balance}\n" + f"⚖️ Ваш баланс (USDT): {float(balance):.2f}\n" f"📊 Текущая торговая пара: {symbol}\n" f"$$$ Цена: {price}\n\n" "Как начать торговлю?\n\n" @@ -52,7 +51,6 @@ async def clb_start_bybit_trade_message(callback: CallbackQuery) -> None: "3️⃣ Нажмите кнопку 'Выбрать тип входа' и после нажмите начать торговлю.\n" ) await callback.message.edit_text(text=text, parse_mode='html', reply_markup=inline_markup.trading_markup) - asyncio.create_task(start_execution_ws(callback.from_user.id, callback.message)) async def start_bybit_trade_message(message: Message) -> None: @@ -240,6 +238,7 @@ async def start_trading_process(callback: CallbackQuery) -> None: await message.answer("Начинаю торговлю с использованием текущих настроек...") + timer_data = await rq.get_user_timer(tg_id) if isinstance(timer_data, dict): timer_minute = timer_data.get('timer_minutes', 0) @@ -247,15 +246,7 @@ async def start_trading_process(callback: CallbackQuery) -> None: timer_minute = timer_data or 0 if timer_minute > 0: - old_task = active_start_tasks.get(tg_id) - if old_task and not old_task.done(): - old_task.cancel() - try: - await old_task - except asyncio.CancelledError: - logger.info(f"Старая задача торговли для пользователя {tg_id} отменена") - task = asyncio.create_task(trading_cycle(tg_id, message)) - active_start_tasks[tg_id] = task + await handle_start_trading(tg_id, message) await message.answer(f"Торговля начнётся через {timer_minute} мин. Для отмены нажмите кнопку ниже.", reply_markup=inline_markup.cancel_start_markup) await rq.update_user_timer(tg_id, minutes=0) @@ -281,6 +272,9 @@ async def show_my_trades(callback: CallbackQuery) -> None: @router_functions_bybit_trade.callback_query(F.data == "clb_open_deals") async def show_my_trades_callback(callback: CallbackQuery): + """ + Показывает открытые позиции пользователя по символу. + """ await callback.answer() try: @@ -373,13 +367,9 @@ async def cancel_start_callback(callback: CallbackQuery) -> None: Отменяет задачу старта торговли по таймеру, если она активна. """ tg_id = callback.from_user.id - task = active_start_tasks.get(tg_id) - if task: - task.cancel() - del active_start_tasks[tg_id] - await callback.message.answer("Торговля по таймеру отменена.", reply_markup=inline_markup.back_to_main) - else: - await callback.message.answer("Нет активности для отмены.", reply_markup=inline_markup.back_to_main) + await handle_stop_close_trade(tg_id) + + await callback.message.answer("Торговля по таймеру отменена.", reply_markup=inline_markup.back_to_main) await callback.answer() @@ -394,7 +384,7 @@ async def close_trade_callback(callback: CallbackQuery) -> None: result = await close_user_trade(tg_id, symbol, message=callback.message) if result: - await callback.message.answer("Сделка успешно закрыта.", reply_markup=inline_markup.back_to_main) + await handle_stop_trading(tg_id) logger.info(f"Сделка {symbol} успешно закрыта.") else: logger.error(f"Не удалось закрыть сделку {symbol}.") @@ -436,17 +426,7 @@ async def process_close_delay(message: Message, state: FSMContext) -> None: delay = delay_minutes * 60 - if tg_id in active_close_tasks: - old_task = active_close_tasks[tg_id] - if not old_task.done(): - old_task.cancel() - try: - await old_task - except asyncio.CancelledError: - logger.info(f"Предыдущая задача закрытия сделки пользователя {tg_id} отменена") - - task = asyncio.create_task(close_trade_after_delay(tg_id, message, symbol, delay)) - active_close_tasks[tg_id] = task + await handle_start_close_trade(tg_id, message, symbol, delay) await message.answer(f"Закрытие сделки {symbol} запланировано через {delay} секунд.", reply_markup=inline_markup.cancel_start_markup) @@ -458,11 +438,9 @@ async def reset_martingale(callback: CallbackQuery) -> None: """ Сбрасывает шаги мартингейла пользователя. """ - await callback.answer() tg_id = callback.from_user.id await rq.update_martingale_step(tg_id, 0) - await callback.message.answer("Сброс шагов выполнен.", - reply_markup=inline_markup.back_to_main) + await callback.answer("Сброс шагов выполнен.") @router_functions_bybit_trade.callback_query(F.data == "clb_cancel") diff --git a/app/telegram/database/requests.py b/app/telegram/database/requests.py index dc86469..b9e2693 100644 --- a/app/telegram/database/requests.py +++ b/app/telegram/database/requests.py @@ -3,29 +3,37 @@ from logger_helper.logger_helper import LOGGING_CONFIG from datetime import datetime, timedelta from typing import Any +from app.telegram.database.models import ( + async_session, + User_Telegram_Id as UTi, + User_Main_Settings as UMS, + User_Bybit_API as UBA, + User_Symbol, + User_Risk_Management_Settings as URMS, + User_Condition_Settings as UCS, + User_Additional_Settings as UAS, + Trading_Mode, + Margin_type, + Trigger, + USER_DEALS, + UserTimer, +) + +from sqlalchemy import select, update + logging.config.dictConfig(LOGGING_CONFIG) logger = logging.getLogger("requests") -from app.telegram.database.models import async_session -from app.telegram.database.models import User_Telegram_Id as UTi -from app.telegram.database.models import User_Main_Settings as UMS -from app.telegram.database.models import User_Bybit_API as UBA -from app.telegram.database.models import User_Symbol -from app.telegram.database.models import User_Risk_Management_Settings as URMS -from app.telegram.database.models import User_Condition_Settings as UCS -from app.telegram.database.models import User_Additional_Settings as UAS -from app.telegram.database.models import Trading_Mode -from app.telegram.database.models import Margin_type -from app.telegram.database.models import Trigger -from app.telegram.database.models import USER_DEALS, UserTimer -import app.telegram.functions.functions as func # functions +# --- Функции сохранения в БД --- -from sqlalchemy import select, delete, update +async def save_tg_id_new_user(tg_id) -> None: + """ + Сохраняет Telegram ID нового пользователя в базу, если такого ещё нет. - -# SET_DB -async def save_tg_id_new_user(tg_id): + Args: + tg_id (int): Telegram ID пользователя. + """ async with async_session() as session: user = await session.scalar(select(UTi).where(UTi.tg_id == tg_id)) @@ -37,26 +45,33 @@ async def save_tg_id_new_user(tg_id): await session.commit() -async def set_new_user_bybit_api(tg_id): +async def set_new_user_bybit_api(tg_id) -> None: + """ + Создаёт запись API пользователя Bybit, если её ещё нет. + + Args: + tg_id (int): Telegram ID пользователя. + """ async with async_session() as session: user = await session.scalar(select(UBA).where(UBA.tg_id == tg_id)) if not user: - session.add(UBA( - tg_id=tg_id, - )) - + session.add(UBA(tg_id=tg_id)) await session.commit() -async def set_new_user_symbol(tg_id): +async def set_new_user_symbol(tg_id) -> None: + """ + Создаёт запись торгового символа пользователя, если её нет. + + Args: + tg_id (int): Telegram ID пользователя. + """ async with async_session() as session: user = await session.scalar(select(User_Symbol).where(User_Symbol.tg_id == tg_id)) if not user: - session.add(User_Symbol( - tg_id=tg_id - )) + session.add(User_Symbol(tg_id=tg_id)) logger.info(f"Symbol был успешно добавлен %s", tg_id) @@ -64,6 +79,14 @@ async def set_new_user_symbol(tg_id): async def set_new_user_default_main_settings(tg_id, trading_mode, margin_type) -> None: + """ + Создаёт основные настройки пользователя по умолчанию. + + Args: + tg_id (int): Telegram ID пользователя. + trading_mode (str): Режим торговли. + margin_type (str): Тип маржи. + """ async with async_session() as session: settings = await session.scalar(select(UMS).where(UMS.tg_id == tg_id)) @@ -80,6 +103,12 @@ async def set_new_user_default_main_settings(tg_id, trading_mode, margin_type) - async def set_new_user_default_risk_management_settings(tg_id) -> None: + """ + Создаёт настройки риск-менеджмента по умолчанию. + + Args: + tg_id (int): Telegram ID пользователя. + """ async with async_session() as session: settings = await session.scalar(select(URMS).where(URMS.tg_id == tg_id)) @@ -94,6 +123,13 @@ async def set_new_user_default_risk_management_settings(tg_id) -> None: async def set_new_user_default_condition_settings(tg_id, trigger) -> None: + """ + Создаёт условные настройки по умолчанию. + + Args: + tg_id (int): Telegram ID пользователя. + trigger (Any): Значение триггера по умолчанию. + """ async with async_session() as session: settings = await session.scalar(select(UCS).where(UCS.tg_id == tg_id)) @@ -109,6 +145,12 @@ async def set_new_user_default_condition_settings(tg_id, trigger) -> None: async def set_new_user_default_additional_settings(tg_id) -> None: + """ + Создаёт дополнительные настройки по умолчанию. + + Args: + tg_id (int): Telegram ID пользователя. + """ async with async_session() as session: settings = await session.scalar(select(UAS).where(UAS.tg_id == tg_id)) @@ -122,32 +164,46 @@ async def set_new_user_default_additional_settings(tg_id) -> None: await session.commit() -# GET_DB +# --- Функции получения данных из БД --- + async def check_user(tg_id): + """ + Проверяет наличие пользователя в базе. + + Args: + tg_id (int): Telegram ID пользователя. + + Returns: + Optional[UTi]: Пользователь или None. + """ async with async_session() as session: user = await session.scalar(select(UTi).where(UTi.tg_id == tg_id)) return user async def get_bybit_api_key(tg_id): + """Получить API ключ Bybit пользователя.""" async with async_session() as session: api_key = await session.scalar(select(UBA.api_key).where(UBA.tg_id == tg_id)) return api_key async def get_bybit_secret_key(tg_id): + """Получить секретный ключ Bybit пользователя.""" async with async_session() as session: secret_key = await session.scalar(select(UBA.secret_key).where(UBA.tg_id == tg_id)) return secret_key async def get_symbol(tg_id): + """Получить символ пользователя.""" async with async_session() as session: symbol = await session.scalar(select(User_Symbol.symbol).where(User_Symbol.tg_id == tg_id)) return symbol async def get_user_trades(tg_id): + """Получить сделки пользователя.""" async with async_session() as session: query = select(USER_DEALS.symbol, USER_DEALS.side).where(USER_DEALS.tg_id == tg_id) result = await session.execute(query) @@ -155,14 +211,63 @@ async def get_user_trades(tg_id): return trades +async def get_entry_order_type(tg_id: object) -> str | None | Any: + """Получить тип входного ордера пользователя.""" + async with async_session() as session: + order_type = await session.scalar( + select(UMS.entry_order_type).where(UMS.tg_id == tg_id) + ) + # Если в базе не установлен тип — возвращаем значение по умолчанию + return order_type or 'Market' + + +# --- Функции обновления данных --- + async def update_user_trades(tg_id, **kwargs): + """Обновить сделки пользователя.""" async with async_session() as session: query = update(USER_DEALS).where(USER_DEALS.tg_id == tg_id).values(**kwargs) await session.execute(query) await session.commit() +async def update_symbol(tg_id: int, symbol: str) -> None: + """Обновить торговый символ пользователя.""" + async with async_session() as session: + await session.execute(update(User_Symbol).where(User_Symbol.tg_id == tg_id).values(symbol=symbol)) + await session.commit() + + +async def update_api_key(tg_id: int, api: str) -> None: + """Обновить API ключ пользователя.""" + async with async_session() as session: + await session.execute(update(UBA).where(UBA.tg_id == tg_id).values(api_key=api)) + await session.commit() + + +async def update_secret_key(tg_id: int, api: str) -> None: + """Обновить секретный ключ пользователя.""" + async with async_session() as session: + await session.execute(update(UBA).where(UBA.tg_id == tg_id).values(secret_key=api)) + await session.commit() + + +# --- Более мелкие обновления и запросы по настройкам --- + +async def update_trade_mode_user(tg_id, trading_mode) -> None: + """Обновить режим торговли пользователя.""" + async with async_session() as session: + mode = await session.scalar(select(Trading_Mode.mode).where(Trading_Mode.mode == trading_mode)) + + if mode: + logger.info("Изменён торговый режим для пользователя %s", tg_id) + await session.execute(update(UMS).where(UMS.tg_id == tg_id).values(trading_mode=mode)) + + await session.commit() + + async def delete_user_trade(tg_id: int, symbol: str): + """Удалить сделку пользователя.""" async with async_session() as session: await session.execute( USER_DEALS.__table__.delete().where( @@ -173,24 +278,28 @@ async def delete_user_trade(tg_id: int, symbol: str): async def get_for_registration_trading_mode(): + """Получить режим торговли по умолчанию.""" async with async_session() as session: mode = await session.scalar(select(Trading_Mode.mode).where(Trading_Mode.id == 1)) return mode async def get_for_registration_margin_type(): + """Получить тип маржи по умолчанию.""" async with async_session() as session: type = await session.scalar(select(Margin_type.type).where(Margin_type.id == 1)) return type async def get_for_registration_trigger(): + """Получить триггер по умолчанию.""" async with async_session() as session: trigger = await session.scalar(select(Trigger.trigger).where(Trigger.id == 1)) return trigger async def get_user_main_settings(tg_id): + """Получить основные настройки пользователя.""" async with async_session() as session: user = await session.scalar(select(UMS).where(UMS.tg_id == tg_id)) @@ -223,6 +332,7 @@ async def get_user_main_settings(tg_id): async def get_user_risk_management_settings(tg_id): + """Получить риск-менеджмента настройки пользователя.""" async with async_session() as session: user = await session.scalar(select(URMS).where(URMS.tg_id == tg_id)) @@ -244,41 +354,8 @@ async def get_user_risk_management_settings(tg_id): return data -# UPDATE_SYMBOL -async def update_symbol(tg_id, symbol) -> None: - async with async_session() as session: - await session.execute(update(User_Symbol).where(User_Symbol.tg_id == tg_id).values(symbol=symbol)) - - await session.commit() - - -async def update_api_key(tg_id, api): - async with async_session() as session: - api_key = await session.execute(update(UBA).where(UBA.tg_id == tg_id).values(api_key=api)) - - await session.commit() - - -async def update_secret_key(tg_id, api): - async with async_session() as session: - secret_key = await session.execute(update(UBA).where(UBA.tg_id == tg_id).values(secret_key=api)) - - await session.commit() - - -# UPDATE_MAIN_SETTINGS_DB -async def update_trade_mode_user(tg_id, trading_mode) -> None: - async with async_session() as session: - mode = await session.scalar(select(Trading_Mode.mode).where(Trading_Mode.mode == trading_mode)) - - if mode: - logger.info("Изменен трейд мод %s", tg_id) - await session.execute(update(UMS).where(UMS.tg_id == tg_id).values(trading_mode=mode)) - - await session.commit() - - async def update_margin_type(tg_id, margin_type) -> None: + """Обновить тип маржи пользователя.""" async with async_session() as session: type = await session.scalar(select(Margin_type.type).where(Margin_type.type == margin_type)) @@ -290,6 +367,7 @@ async def update_margin_type(tg_id, margin_type) -> None: async def update_size_leverange(tg_id, num): + """Обновить размер левеража пользователя.""" async with async_session() as session: await session.execute(update(UMS).where(UMS.tg_id == tg_id).values(size_leverage=num)) @@ -297,6 +375,7 @@ async def update_size_leverange(tg_id, num): async def update_starting_quantity(tg_id, num): + """Обновить размер левеража пользователя.""" async with async_session() as session: await session.execute(update(UMS).where(UMS.tg_id == tg_id).values(starting_quantity=num)) @@ -304,6 +383,7 @@ async def update_starting_quantity(tg_id, num): async def update_martingale_factor(tg_id, num): + """Обновить размер левеража пользователя.""" async with async_session() as session: await session.execute(update(UMS).where(UMS.tg_id == tg_id).values(martingale_factor=num)) @@ -311,15 +391,17 @@ async def update_martingale_factor(tg_id, num): async def update_maximal_quantity(tg_id, num): + """Обновить размер левеража пользователя.""" async with async_session() as session: await session.execute(update(UMS).where(UMS.tg_id == tg_id).values(maximal_quantity=num)) await session.commit() -# UPDATE_RISK_MANAGEMENT_SETTINGS_DB +# ОБНОВЛЕНИЕ НАСТРОЕК РИСК-МЕНЕДЖМЕНТА async def update_price_profit(tg_id, num): + """Обновить цену тейк-профита (прибыль) пользователя.""" async with async_session() as session: await session.execute(update(URMS).where(URMS.tg_id == tg_id).values(price_profit=num)) @@ -327,6 +409,7 @@ async def update_price_profit(tg_id, num): async def update_price_loss(tg_id, num): + """Обновить цену тейк-лосса (убыток) пользователя.""" async with async_session() as session: await session.execute(update(URMS).where(URMS.tg_id == tg_id).values(price_loss=num)) @@ -334,6 +417,7 @@ async def update_price_loss(tg_id, num): async def update_max_risk_deal(tg_id, num): + """Обновить максимальную сумму риска пользователя.""" async with async_session() as session: await session.execute(update(URMS).where(URMS.tg_id == tg_id).values(max_risk_deal=num)) @@ -341,6 +425,7 @@ async def update_max_risk_deal(tg_id, num): async def update_entry_order_type(tg_id, order_type): + """Обновить тип входного ордера пользователя.""" async with async_session() as session: await session.execute( update(UMS) @@ -350,16 +435,8 @@ async def update_entry_order_type(tg_id, order_type): await session.commit() -async def get_entry_order_type(tg_id: object) -> str | None | Any: - async with async_session() as session: - order_type = await session.scalar( - select(UMS.entry_order_type).where(UMS.tg_id == tg_id) - ) - # Если в базе не установлен тип — возвращаем значение по умолчанию - return order_type or 'Market' - - async def get_limit_price(tg_id): + """Получить лимитную цену пользователя как float, либо None.""" async with async_session() as session: result = await session.execute( select(UMS.limit_order_price) @@ -375,6 +452,7 @@ async def get_limit_price(tg_id): async def update_limit_price(tg_id, price): + """Обновить лимитную цену пользователя.""" async with async_session() as session: await session.execute( update(UMS) @@ -385,6 +463,7 @@ async def update_limit_price(tg_id, price): async def update_commission_fee(tg_id, num): + """Обновить комиссию пользователя.""" async with async_session() as session: await session.execute(update(URMS).where(URMS.tg_id == tg_id).values(commission_fee=num)) @@ -392,6 +471,7 @@ async def update_commission_fee(tg_id, num): async def get_user_timer(tg_id): + """Получить данные о таймере пользователя.""" async with async_session() as session: result = await session.execute(select(UserTimer).where(UserTimer.tg_id == tg_id)) user_timer = result.scalars().first() @@ -422,38 +502,39 @@ async def get_user_timer(tg_id): async def update_user_timer(tg_id, minutes: int): + """Обновить данные о таймере пользователя.""" async with async_session() as session: try: - async with async_session() as session: - timer_start = None - timer_end = None + timer_start = None + timer_end = None - if minutes > 0: - timer_start = datetime.utcnow() - timer_end = timer_start + timedelta(minutes=minutes) + if minutes > 0: + timer_start = datetime.utcnow() + timer_end = timer_start + timedelta(minutes=minutes) - result = await session.execute(select(UserTimer).where(UserTimer.tg_id == tg_id)) - user_timer = result.scalars().first() + result = await session.execute(select(UserTimer).where(UserTimer.tg_id == tg_id)) + user_timer = result.scalars().first() - if user_timer: - user_timer.timer_minutes = minutes - user_timer.timer_start = timer_start - user_timer.timer_end = timer_end - else: - user_timer = UserTimer( - tg_id=tg_id, - timer_minutes=minutes, - timer_start=timer_start, - timer_end=timer_end - ) - session.add(user_timer) + if user_timer: + user_timer.timer_minutes = minutes + user_timer.timer_start = timer_start + user_timer.timer_end = timer_end + else: + user_timer = UserTimer( + tg_id=tg_id, + timer_minutes=minutes, + timer_start=timer_start, + timer_end=timer_end + ) + session.add(user_timer) - await session.commit() + await session.commit() except Exception as e: logging.error(f"Ошибка обновления таймера пользователя {tg_id}: {e}") async def get_martingale_step(tg_id): + """Получить шаг мартингейла пользователя.""" async with async_session() as session: result = await session.execute(select(UMS).where(UMS.tg_id == tg_id)) user_settings = result.scalars().first() @@ -461,6 +542,7 @@ async def get_martingale_step(tg_id): async def update_martingale_step(tg_id, step): + """Обновить шаг мартингейла пользователя.""" async with async_session() as session: await session.execute(update(UMS).where(UMS.tg_id == tg_id).values(martingale_step=step))