diff --git a/README.md b/README.md index 6c8adf8..70599b7 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ nvim .env 5. Запустите бота: ```bash -python BybitBot_API.py +python run.py ``` ## Настройка автономной работы diff --git a/logger_helper/logger_helper.py b/logger_helper/logger_helper.py index 7f17351..553a0f4 100644 --- a/logger_helper/logger_helper.py +++ b/logger_helper/logger_helper.py @@ -2,15 +2,18 @@ import os current_directory = os.path.dirname(os.path.abspath(__file__)) log_directory = os.path.join(current_directory, 'loggers') +error_log_directory = os.path.join(log_directory, 'errors') os.makedirs(log_directory, exist_ok=True) +os.makedirs(error_log_directory, exist_ok=True) log_filename = os.path.join(log_directory, 'app.log') +error_log_filename = os.path.join(error_log_directory, 'error.log') LOGGING_CONFIG = { "version": 1, "disable_existing_loggers": False, "formatters": { "default": { - "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + "format": "TELEGRAM: %(asctime)s - %(name)s - %(levelname)s - %(message)s", "datefmt": "%Y-%m-%d %H:%M:%S", # Формат даты }, }, @@ -23,90 +26,112 @@ LOGGING_CONFIG = { "backupCount": 7, # Количество сохраняемых архивов (0 - не сохранять) "formatter": "default", "encoding": "utf-8", + "level": "DEBUG", + }, + "error_file": { + "class": "logging.handlers.TimedRotatingFileHandler", + "filename": error_log_filename, + "when": "midnight", + "interval": 1, + "backupCount": 30, + "formatter": "default", + "encoding": "utf-8", + "level": "ERROR", }, "console": { "class": "logging.StreamHandler", "formatter": "default", + "level": "DEBUG", }, }, "loggers": { - "main": { - "handlers": ["console", "timed_rotating_file"], + "run": { + "handlers": ["console", "timed_rotating_file", "error_file"], + "level": "DEBUG", + "propagate": False, + }, + "config": { + "handlers": ["console", "timed_rotating_file", "error_file"], + "level": "DEBUG", + "propagate": False, + }, + "common": { + "handlers": ["console", "timed_rotating_file", "error_file"], + "level": "DEBUG", + "propagate": False, + }, + "handlers_main": { + "handlers": ["console", "timed_rotating_file", "error_file"], + "level": "DEBUG", + "propagate": False, + }, + "database": { + "handlers": ["console", "timed_rotating_file", "error_file"], + "level": "DEBUG", + "propagate": False, + }, + "request": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, "add_bybit_api": { - "handlers": ["console", "timed_rotating_file"], + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "balance": { - "handlers": ["console", "timed_rotating_file"], + "profile_tg": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "functions": { - "handlers": ["console", "timed_rotating_file"], + "settings": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "futures": { - "handlers": ["console", "timed_rotating_file"], + "additional_settings": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "get_valid_symbol": { - "handlers": ["console", "timed_rotating_file"], + "helper_functions": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "min_qty": { - "handlers": ["console", "timed_rotating_file"], + "risk_management": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "price_symbol": { - "handlers": ["console", "timed_rotating_file"], + "start_trading": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "requests": { - "handlers": ["console", "timed_rotating_file"], + "changing_the_symbol": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "handlers": { - "handlers": ["console", "timed_rotating_file"], + "conditional_settings": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "condition_settings": { - "handlers": ["console", "timed_rotating_file"], + "get_positions_handlers": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "main_settings": { - "handlers": ["console", "timed_rotating_file"], + "close_orders": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, - "risk_management_settings": { - "handlers": ["console", "timed_rotating_file"], - "level": "DEBUG", - "propagate": False, - }, - "models": { - "handlers": ["console", "timed_rotating_file"], - "level": "DEBUG", - "propagate": False, - }, - "bybit_ws": { - "handlers": ["console", "timed_rotating_file"], - "level": "DEBUG", - "propagate": False, - }, - "tasks": { - "handlers": ["console", "timed_rotating_file"], + "tp_sl_handlers": { + "handlers": ["console", "timed_rotating_file", "error_file"], "level": "DEBUG", "propagate": False, }, diff --git a/run.py b/run.py new file mode 100644 index 0000000..8d92a6d --- /dev/null +++ b/run.py @@ -0,0 +1,56 @@ +import asyncio +import contextlib +import logging.config + +from aiogram import Bot, Dispatcher +from aiogram.fsm.storage.redis import RedisStorage + +from app.bybit.web_socket import WebSocketBot +from app.telegram.handlers import router +from config import BOT_TOKEN +from database import init_db +from logger_helper.logger_helper import LOGGING_CONFIG + +logging.config.dictConfig(LOGGING_CONFIG) +logger = logging.getLogger("run") + + +async def main(): + """ + The main function of launching the bot. + + Performs database initialization, creation of bot and dispatcher objects, + then it triggers the long polling event. + + Logs important events and errors. + """ + try: + await init_db() + bot = Bot(token=BOT_TOKEN) + storage = RedisStorage.from_url("redis://localhost:6379") + dp = Dispatcher(storage=storage) + dp.include_router(router) + web_socket = WebSocketBot(telegram_bot=bot) + await web_socket.clear_user_sockets() + ws_task = asyncio.create_task(web_socket.run_user_check_loop()) + tg_task = asyncio.create_task(dp.start_polling(bot)) + + try: + logger.info("Bot started") + await asyncio.gather(ws_task, tg_task) + except Exception as e: + logger.error("Bot stopped with error: %s", e) + finally: + for task in (ws_task, tg_task): + task.cancel() + 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) + + +if __name__ == "__main__": + asyncio.run(main())