From afe61ea7d617d3377361aac8068511c6df3e6b42 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Sat, 23 Aug 2025 14:33:33 +0500 Subject: [PATCH] Added documentation --- .../Bybit/functions/get_valid_symbol.py | 40 +++++++++++++ app/services/Bybit/functions/min_qty.py | 57 ++++++++++++++----- app/services/Bybit/functions/price_symbol.py | 27 ++++++--- 3 files changed, 101 insertions(+), 23 deletions(-) create mode 100644 app/services/Bybit/functions/get_valid_symbol.py diff --git a/app/services/Bybit/functions/get_valid_symbol.py b/app/services/Bybit/functions/get_valid_symbol.py new file mode 100644 index 0000000..1a8d830 --- /dev/null +++ b/app/services/Bybit/functions/get_valid_symbol.py @@ -0,0 +1,40 @@ +import logging.config +from pybit.unified_trading import HTTP +import app.telegram.database.requests as rq +from logger_helper.logger_helper import LOGGING_CONFIG + +logging.config.dictConfig(LOGGING_CONFIG) +logger = logging.getLogger("get_valid_symbol") + + +async def get_valid_symbols(user_id: int, symbol: str) -> bool: + """ + Проверяет существование торговой пары на Bybit в категории 'linear'. + + Эта функция получает API-ключи пользователя из базы данных и + с помощью Bybit API проверяет наличие данного символа в списке + торговых инструментов категории 'linear'. + + Args: + user_id (int): Идентификатор пользователя Telegram. + symbol (str): Торговый символ (валютная пара), например "BTCUSDT". + + Returns: + bool: Возвращает True, если торговая пара существует, иначе False. + + Raises: + Исключения подавляются и вызывается False, если произошла ошибка запроса к API. + """ + api_key = await rq.get_bybit_api_key(user_id) + secret_key = await rq.get_bybit_secret_key(user_id) + client = HTTP(api_key=api_key, api_secret=secret_key) + + try: + resp = client.get_instruments_info(category='linear', symbol=symbol) + # Проверка наличия результата и непустого списка инструментов + if resp.get('retCode') == 0 and resp.get('result') and resp['result'].get('list'): + return len(resp['result']['list']) > 0 + return False + except Exception as e: + logging.error(f"Ошибка при получении списка инструментов: {e}") + return False diff --git a/app/services/Bybit/functions/min_qty.py b/app/services/Bybit/functions/min_qty.py index 07c1485..7cfb453 100644 --- a/app/services/Bybit/functions/min_qty.py +++ b/app/services/Bybit/functions/min_qty.py @@ -1,23 +1,52 @@ -from app.services.Bybit.functions import price_symbol +import math +import logging.config +from app.services.Bybit.functions.price_symbol import get_price import app.telegram.database.requests as rq - +from logger_helper.logger_helper import LOGGING_CONFIG from pybit.unified_trading import HTTP -client = HTTP() +logging.config.dictConfig(LOGGING_CONFIG) +logger = logging.getLogger("min_qty") -async def get_min_qty(tg_id): +def round_up_qty(value: float, step: float) -> float: + """ + Округление value вверх до ближайшего кратного step. + """ + return math.ceil(value / step) * step + +async def get_min_qty(tg_id: int) -> float: + """ + Получает минимальный объем (количество) ордера для символа пользователя на Bybit, + округленное с учетом шага количества qtyStep. + + :param tg_id: int - идентификатор пользователя Telegram + :return: float - минимальное количество лота для ордера + """ 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) + symbol = await rq.get_symbol(tg_id) - client = HTTP( - api_key=api_key, - api_secret=secret_key - ) + client = HTTP(api_key=api_key, api_secret=secret_key) - price = await price_symbol(tg_id) - json_data = client.get_instruments_info(symbol=SYMBOL, category='linear') + price = await get_price(tg_id) - min_qty = int(5 / price * 1.1) # 1% 5 USDT - - return min_qty \ No newline at end of file + response = client.get_instruments_info(symbol=symbol, category='linear') + + instrument = response['result'][0] + lot_size_filter = instrument.get('lotSizeFilter', {}) + + min_order_qty = float(lot_size_filter.get('minOrderQty', 0)) + min_notional_value = float(lot_size_filter.get('minNotionalValue', 0)) + qty_step = float(lot_size_filter.get('qtyStep', 1)) + + calculated_qty = (5 / price) * 1.1 + + min_qty = max(min_order_qty, calculated_qty) + + min_qty_rounded = round_up_qty(min_qty, qty_step) + + logger.debug(f"tg_id={tg_id}: price={price}, min_order_qty={min_order_qty}, " + f"min_notional_value={min_notional_value}, qty_step={qty_step}, " + f"calculated_qty={calculated_qty}, min_qty_rounded={min_qty_rounded}") + + return min_qty_rounded diff --git a/app/services/Bybit/functions/price_symbol.py b/app/services/Bybit/functions/price_symbol.py index 8971a66..3421228 100644 --- a/app/services/Bybit/functions/price_symbol.py +++ b/app/services/Bybit/functions/price_symbol.py @@ -1,24 +1,33 @@ import app.telegram.database.requests as rq - +import logging.config +from logger_helper.logger_helper import LOGGING_CONFIG from pybit import exceptions from pybit.unified_trading import HTTP -client = HTTP() +logging.config.dictConfig(LOGGING_CONFIG) +logger = logging.getLogger("price_symbol") -async def get_price(tg_id, message): + +async def get_price(tg_id: int) -> float: + """ + Асинхронно получает текущую цену символа пользователя на Bybit. + + :param tg_id: int - ID пользователя Telegram + :return: float - текущая цена символа + """ 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) + symbol = await rq.get_symbol(tg_id) client = HTTP( api_key=api_key, api_secret=secret_key ) - - try: - price = float(client.get_tickers(category='linear', symbol=SYMBOL).get('result').get('list')[0].get('ask1Price')) + try: + price = float( + client.get_tickers(category='linear', symbol=symbol).get('result').get('list')[0].get('ask1Price')) return price except exceptions.InvalidRequestError as e: - await message.answer('Неверно указана торговая пара') - return 1.0 \ No newline at end of file + logger.error(f"Ошибка при получении цены: {e}") + return 1.0