develop #3
							
								
								
									
										40
									
								
								app/services/Bybit/functions/get_valid_symbol.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								app/services/Bybit/functions/get_valid_symbol.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
| @@ -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 | import app.telegram.database.requests as rq | ||||||
|  | from logger_helper.logger_helper import LOGGING_CONFIG | ||||||
| from pybit.unified_trading import HTTP | 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) |     api_key = await rq.get_bybit_api_key(tg_id) | ||||||
|     secret_key = await rq.get_bybit_secret_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( |     client = HTTP(api_key=api_key, api_secret=secret_key) | ||||||
|         api_key=api_key, |  | ||||||
|         api_secret=secret_key |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     price = await price_symbol(tg_id) |     price = await get_price(tg_id) | ||||||
|     json_data = client.get_instruments_info(symbol=SYMBOL, category='linear') |  | ||||||
|  |  | ||||||
|     min_qty = int(5 / price * 1.1) # <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1% <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 5 USDT |     response = client.get_instruments_info(symbol=symbol, category='linear') | ||||||
|  |  | ||||||
|     return min_qty |     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 | ||||||
|   | |||||||
| @@ -1,14 +1,23 @@ | |||||||
| import app.telegram.database.requests as rq | import app.telegram.database.requests as rq | ||||||
|  | import logging.config | ||||||
|  | from logger_helper.logger_helper import LOGGING_CONFIG | ||||||
| from pybit import exceptions | from pybit import exceptions | ||||||
| from pybit.unified_trading import HTTP | 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) |     api_key = await rq.get_bybit_api_key(tg_id) | ||||||
|     secret_key = await rq.get_bybit_secret_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( |     client = HTTP( | ||||||
|         api_key=api_key, |         api_key=api_key, | ||||||
| @@ -16,9 +25,9 @@ async def get_price(tg_id, message): | |||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
|         price = float(client.get_tickers(category='linear', symbol=SYMBOL).get('result').get('list')[0].get('ask1Price')) |         price = float( | ||||||
|  |             client.get_tickers(category='linear', symbol=symbol).get('result').get('list')[0].get('ask1Price')) | ||||||
|         return price |         return price | ||||||
|     except exceptions.InvalidRequestError as e: |     except exceptions.InvalidRequestError as e: | ||||||
|         await message.answer('Неверно указана торговая пара') |         logger.error(f"Ошибка при получении цены: {e}") | ||||||
|         return 1.0 |         return 1.0 | ||||||
		Reference in New Issue
	
	Block a user