diff --git a/.env.sample b/.env.sample
new file mode 100644
index 0000000..f5fde1c
--- /dev/null
+++ b/.env.sample
@@ -0,0 +1 @@
+TOKEN_TELEGRAM_BOT=
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2a4caf9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+.env
+!*.sample
+
+__pycache__/
+*.pyc
+
+env/
+venv/
+.venv/
+
+requirements.txt
+
diff --git a/BibytBot_API.py b/BibytBot_API.py
new file mode 100644
index 0000000..3cb2ffd
--- /dev/null
+++ b/BibytBot_API.py
@@ -0,0 +1,31 @@
+import asyncio
+
+from aiogram import Bot, Dispatcher
+from aiogram.filters import Command, CommandStart
+from aiogram.types import Message
+
+from app.telegram.database.models import async_main
+
+from app.telegram.handlers.handlers import router # Для вызова событий
+from app.telegram.functions.main_settings.settings import router_main_settings # Для вызова событий
+
+from config import TOKEN_TG_BOT
+
+from app.telegram.logs import logger
+
+bot = Bot(token=TOKEN_TG_BOT)
+dp = Dispatcher()
+
+async def main():
+ await async_main()
+
+ dp.include_router(router)
+ dp.include_router(router_main_settings)
+
+ await dp.start_polling(bot)
+
+if __name__ == '__main__':
+ try:
+ asyncio.run(main())
+ except KeyboardInterrupt:
+ print("Bot is off")
diff --git a/BibytBot_API.pyproj b/BibytBot_API.pyproj
new file mode 100644
index 0000000..d6c1cc1
--- /dev/null
+++ b/BibytBot_API.pyproj
@@ -0,0 +1,59 @@
+
+
+ Debug
+ 2.0
+ bc1d7460-d8ca-4977-a249-0f6d6cc2375a
+ .
+ BibytBot_API.py
+
+
+ .
+ .
+ BibytBot_API
+ BibytBot_API
+
+
+ true
+ false
+
+
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BibytBot_API.sln b/BibytBot_API.sln
new file mode 100644
index 0000000..e90ff6b
--- /dev/null
+++ b/BibytBot_API.sln
@@ -0,0 +1,23 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.13.35825.156 d17.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "BibytBot_API", "BibytBot_API.pyproj", "{BC1D7460-D8CA-4977-A249-0F6D6CC2375A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BC1D7460-D8CA-4977-A249-0F6D6CC2375A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BC1D7460-D8CA-4977-A249-0F6D6CC2375A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {9AF00E9A-19FB-4146-96C0-B86C8B1E02C0}
+ EndGlobalSection
+EndGlobal
diff --git a/app/telegram/Keyboards/inline_keyboards.py b/app/telegram/Keyboards/inline_keyboards.py
new file mode 100644
index 0000000..4748f3d
--- /dev/null
+++ b/app/telegram/Keyboards/inline_keyboards.py
@@ -0,0 +1,88 @@
+from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
+
+start_markup = InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text="Зарегистрироваться", callback_data="callback_registration")],
+ [InlineKeyboardButton(text="Авторизоваться", callback_data="callback_autorisation")]
+])
+
+settings_markup = InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text="Настройки", callback_data='clb_settings_message')]
+])
+
+back_btn_profile = [InlineKeyboardButton(text="Назад", callback_data='callback_autorisation')]
+
+special_settings_markup = InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text="Основные настройки", callback_data='clb_change_main_settings'),
+ 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')],
+
+ back_btn_profile
+])
+
+back_btn_list_settings = [InlineKeyboardButton(text="Назад", callback_data='clb_back_to_special_settings_message')] # Кнопка для возврата к списку каталога настроек
+back_btn_list_settings_markup = InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Назад", callback_data='clb_back_to_special_settings_message')]]) # Клавиатура для возврата к списку каталога настроек
+
+main_settings_markup = InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text='Режим торговли', callback_data='clb_change_trading_mode'),
+ InlineKeyboardButton(text='Тип маржи', callback_data='clb_change_margin_type')],
+
+ [InlineKeyboardButton(text='Размер кредитного плеча', callback_data='clb_change_size_leverage'),
+ InlineKeyboardButton(text='Начальная ставка', callback_data='clb_change_starting_quantity')],
+
+ [InlineKeyboardButton(text='Коэффициент Мартингейла', callback_data='clb_change_martingale_factor'),
+ InlineKeyboardButton(text='Максимльное кол-во ставок', callback_data='clb_change_maximum_quantity')],
+
+ back_btn_list_settings
+])
+
+risk_management_settings_markup = InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text='Изм. цены прибыли', callback_data='clb_change_price_profit'),
+ InlineKeyboardButton(text='Изм. цены убытков', callback_data='clb_change_price_loss')],
+
+ [InlineKeyboardButton(text='Иакс. риск на сделку', callback_data='clb_change_max_risk_deal')],
+
+ back_btn_list_settings
+])
+
+condition_settings_markup = InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text='Триггер', callback_data='clb_change_trigger'),
+ InlineKeyboardButton(text='Фильтр времени', callback_data='clb_change_filter_time')],
+
+ [InlineKeyboardButton(text='Фильтр волатильности', callback_data='clb_change_filter_volatility'),
+ InlineKeyboardButton(text='Внешние сигналы', callback_data='clb_change_external_cues')],
+
+ [InlineKeyboardButton(text='Сигналы TradingView', callback_data='clb_change_tradingview_cues'),
+ InlineKeyboardButton(text='Webhook URL', callback_data='clb_change_webhook')],
+
+ [InlineKeyboardButton(text='AI - аналитика', callback_data='clb_change_ai_analytics')],
+
+ back_btn_list_settings
+])
+
+additional_settings_markup = InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text='Сохранить шаблон', callback_data='clb_change_save_pattern'),
+ InlineKeyboardButton(text='Автозапуск', callback_data='clb_change_auto_start')],
+
+ [InlineKeyboardButton(text='Уведомления', callback_data='clb_change_notifications')],
+
+ back_btn_list_settings
+])
+
+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")],
+
+ back_btn_list_settings
+])
+
+margin_type_markup = InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text="Изолированный", callback_data="margin_type_isolated"),
+ InlineKeyboardButton(text="Кросс", callback_data="margin_type_cross")],
+
+ back_btn_list_settings
+])
\ No newline at end of file
diff --git a/app/telegram/Keyboards/reply_keyboards.py b/app/telegram/Keyboards/reply_keyboards.py
new file mode 100644
index 0000000..e0fae5c
--- /dev/null
+++ b/app/telegram/Keyboards/reply_keyboards.py
@@ -0,0 +1,19 @@
+from aiogram.types import ReplyKeyboardMarkup, KeyboardButton
+
+base_buttons_markup = ReplyKeyboardMarkup(keyboard=[
+ [KeyboardButton(text="👤 Профиль")],
+ # [KeyboardButton(text="Настройки")]
+], resize_keyboard=True)
+
+trigger_markup = ReplyKeyboardMarkup(keyboard=[ # ИЗМЕНИТЬ НА INLINE
+ [KeyboardButton(text='Ручной'), KeyboardButton(text='TradingView')],
+ [KeyboardButton(text="Автоматический")]
+], resize_keyboard=True)
+
+buttons_yes_no_markup = ReplyKeyboardMarkup(keyboard=[ # ИЗМЕНИТЬ НА INLINE
+ [KeyboardButton(text='Да'), KeyboardButton(text='Нет')]
+], resize_keyboard=True)
+
+buttons_on_off_markup = ReplyKeyboardMarkup(keyboard=[ # ИЗМЕНИТЬ НА INLINE
+ [KeyboardButton(text='Включить'), KeyboardButton(text='Выключить')]
+], resize_keyboard=True)
\ No newline at end of file
diff --git a/app/telegram/database/models.py b/app/telegram/database/models.py
new file mode 100644
index 0000000..655f61c
--- /dev/null
+++ b/app/telegram/database/models.py
@@ -0,0 +1,120 @@
+import logging
+logger = logging.getLogger(__name__)
+
+from sqlalchemy import BigInteger, Boolean, Integer, String, ForeignKey
+from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
+from sqlalchemy.ext.asyncio import AsyncAttrs, async_sessionmaker, create_async_engine
+
+from sqlalchemy import select, insert
+
+engine = create_async_engine(url='sqlite+aiosqlite:///db.sqlite3')
+
+async_session = async_sessionmaker(engine)
+
+class Base(AsyncAttrs, DeclarativeBase):
+ pass
+
+class User_Telegram_Id(Base):
+ __tablename__ = 'user_telegram_id'
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+
+ tg_id = mapped_column(BigInteger)
+
+class Trading_Mode(Base):
+ __tablename__ = 'trading_modes'
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+
+ mode = mapped_column(String(10), unique=True)
+
+class Margin_type(Base):
+ __tablename__ = 'margin_types'
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+
+ type = mapped_column(String(15), unique=True)
+
+class Trigger(Base):
+ __tablename__ = 'triggers'
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+
+ trigger = mapped_column(String(15), unique=True)
+
+class User_Main_Settings(Base):
+ __tablename__ = 'user_main_settings'
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+
+ tg_id = mapped_column(ForeignKey("user_telegram_id.tg_id"))
+
+ trading_mode = mapped_column(ForeignKey("trading_modes.mode"))
+ margin_type = mapped_column(ForeignKey("margin_types.type"))
+ size_leverage = mapped_column(Integer(), default=1)
+ starting_quantity = mapped_column(Integer(), default=1)
+ martingale_factor = mapped_column(Integer(), default=1)
+ maximal_quantity = mapped_column(Integer(), default=10)
+
+class User_Risk_Management_Settings(Base):
+ __tablename__ = 'user_risk_management_settings'
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+
+ tg_id = mapped_column(ForeignKey("user_telegram_id.tg_id"))
+
+ price_profit = mapped_column(Integer(), default=1)
+ price_loss = mapped_column(Integer(), default=1)
+ max_risk_deal = mapped_column(Integer(), default=1)
+
+class User_Condition_Settings(Base):
+ __tablename__ = 'user_condition_settings'
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+
+ tg_id = mapped_column(ForeignKey("user_telegram_id.tg_id"))
+
+ trigger = mapped_column(ForeignKey("triggers.trigger"))
+ filter_time = mapped_column(String(25), default='???')
+ filter_volatility = mapped_column(Boolean, default=False)
+ external_cues = mapped_column(Boolean, default=False)
+ tradingview_cues = mapped_column(Boolean, default=False)
+ webhook = mapped_column(String(40), default='')
+ ai_analytics = mapped_column(Boolean, default=False)
+
+class User_Additional_Settings(Base):
+ __tablename__ = 'user_additional_settings'
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+
+ tg_id = mapped_column(ForeignKey("user_telegram_id.tg_id"))
+
+ pattern_save = mapped_column(Boolean, default=False)
+ autostart = mapped_column(Boolean, default=False)
+ notifications = mapped_column(Boolean, default=False)
+
+async def async_main():
+ async with engine.begin() as conn:
+ await conn.run_sync(Base.metadata.create_all)
+
+ # Заполнение таблиц
+ modes = ['Long', 'Short', 'Switch', 'Smart']
+ for mode in modes:
+ result = await conn.execute(select(Trading_Mode).where(Trading_Mode.mode == mode))
+ if not result.first():
+ logger.info("Заполение таблицы режима торговли")
+ await conn.execute(Trading_Mode.__table__.insert().values(mode=mode))
+
+ types = ['Изолированный', 'Кросс']
+ for type in types:
+ result = await conn.execute(select(Margin_type).where(Margin_type.type == type))
+ if not result.first():
+ logger.info("Заполение таблицы типов маржи")
+ await conn.execute(Margin_type.__table__.insert().values(type=type))
+
+ triggers = ['Ручной', 'Автоматический', 'TradingView']
+ for trigger in triggers:
+ result = await conn.execute(select(Trigger).where(Trigger.trigger == trigger))
+ if not result.first():
+ logger.info("Заполение таблицы триггеров")
+ await conn.execute(Trigger.__table__.insert().values(trigger=trigger))
\ No newline at end of file
diff --git a/app/telegram/database/requests.py b/app/telegram/database/requests.py
new file mode 100644
index 0000000..6cd01a0
--- /dev/null
+++ b/app/telegram/database/requests.py
@@ -0,0 +1,174 @@
+import logging
+logger = logging.getLogger(__name__)
+
+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_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
+
+import app.telegram.functions.functions as func # functions
+
+from sqlalchemy import select, delete, update
+
+# SET_DB
+async def save_tg_id_new_user(tg_id):
+ async with async_session() as session:
+ user = await session.scalar(select(UTi).where(UTi.tg_id == tg_id))
+
+ if not user:
+ session.add(UTi(tg_id=tg_id))
+
+ logger.info("Новый пользователь был добавлен в бд")
+
+ await session.commit()
+
+async def set_new_user_default_main_settings(tg_id, trading_mode, margin_type) -> None:
+ async with async_session() as session:
+ settings = await session.scalar(select(UMS).where(UMS.tg_id == tg_id))
+
+ if not settings:
+ session.add(UMS(
+ tg_id=tg_id,
+ trading_mode=trading_mode,
+ margin_type=margin_type,
+ ))
+
+ logger.info("Основные настройки нового пользователя были заполнены")
+
+ await session.commit()
+
+async def set_new_user_default_risk_management_settings(tg_id) -> None:
+ async with async_session() as session:
+ settings = await session.scalar(select(URMS).where(URMS.tg_id == tg_id))
+
+ if not settings:
+ session.add(URMS(
+ tg_id=tg_id
+ ))
+
+ logger.info("Риск-Менеджмент настройки нового пользователя были заполнены")
+
+ await session.commit()
+
+async def set_new_user_default_condition_settings(tg_id, trigger) -> None:
+ async with async_session() as session:
+ settings = await session.scalar(select(UCS).where(UCS.tg_id == tg_id))
+
+ if not settings:
+ session.add(UCS(
+ tg_id=tg_id,
+ trigger=trigger
+ ))
+
+ logger.info("Условные настройки нового пользователя были заполнены")
+
+ await session.commit()
+
+async def set_new_user_default_additional_settings(tg_id) -> None:
+ async with async_session() as session:
+ settings = await session.scalar(select(UAS).where(UAS.tg_id == tg_id))
+
+ if not settings:
+ session.add(UAS(
+ tg_id=tg_id,
+ ))
+
+ logger.info("Дополнительные настройки нового пользователя были заполнены")
+
+ await session.commit()
+
+# GET_DB
+async def check_user(tg_id):
+ async with async_session() as session:
+ user = await session.scalar(select(UTi).where(UTi.tg_id == tg_id))
+ return user
+
+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))
+
+ if user:
+ logger.info("Получение основных настроек пользователя")
+
+ trading_mode = await session.scalar(select(UMS.trading_mode).where(UMS.tg_id == tg_id))
+ margin_mode = await session.scalar(select(UMS.margin_type).where(UMS.tg_id == tg_id))
+ size_leverage = await session.scalar(select(UMS.size_leverage).where(UMS.tg_id == tg_id))
+ starting_quantity = await session.scalar(select(UMS.starting_quantity).where(UMS.tg_id == tg_id))
+ martingale_factor = await session.scalar(select(UMS.martingale_factor).where(UMS.tg_id == tg_id))
+ maximal_quantity = await session.scalar(select(UMS.maximal_quantity).where(UMS.tg_id == tg_id))
+
+ data = {
+ 'trading_mode': trading_mode,
+ 'margin_type': margin_mode,
+ 'size_leverage': size_leverage,
+ 'starting_quantity': starting_quantity,
+ 'martingale_factor': martingale_factor,
+ 'maximal_quantity': maximal_quantity
+ }
+
+ return data
+
+# UPDATE_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("Изменен трейд мод")
+ 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))
+
+ if type:
+ logger.info("Изменен тип маржи")
+ await session.execute(update(UMS).where(UMS.tg_id == tg_id).values(margin_type = type))
+
+ await session.commit()
+
+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))
+
+ await session.commit()
+
+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))
+
+ await session.commit()
+
+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))
+
+ await session.commit()
+
+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()
\ No newline at end of file
diff --git a/app/telegram/functions/additional_settings/settings.py b/app/telegram/functions/additional_settings/settings.py
new file mode 100644
index 0000000..c1f5069
--- /dev/null
+++ b/app/telegram/functions/additional_settings/settings.py
@@ -0,0 +1,41 @@
+import app.telegram.Keyboards.inline_keyboards as inline_markup
+import app.telegram.Keyboards.reply_keyboards as reply_markup
+
+import app.telegram.database.requests as rq
+
+async def reg_new_user_default_additional_settings(id, message):
+ tg_id = id
+
+ await rq.set_new_user_default_additional_settings(tg_id)
+
+async def main_settings_message(message):
+ text = '''Дополнительные параметры
+
+Сохранить как шаблон стратегии: да / нет
+
+Автозапуск после сохранения: да / нет
+
+Уведомления в Telegram: включено / отключено '''
+
+ await message.answer(text=text, parse_mode='html', reply_markup=inline_markup.additional_settings_markup)
+
+async def save_pattern_message(message, state):
+ text = '''Сохранение шаблона
+
+ Описание... '''
+
+ await message.answer(text=text, parse_mode='html', reply_markup=reply_markup.buttons_yes_no_markup)
+
+async def auto_start_message(message, state):
+ text = '''Автозапуск
+
+ Описание... '''
+
+ await message.answer(text=text, parse_mode='html', reply_markup=reply_markup.buttons_yes_no_markup)
+
+async def notifications_message(message, state):
+ text = '''Уведомления
+
+ Описание... '''
+
+ await message.answer(text=text, parse_mode='html', reply_markup=reply_markup.buttons_on_off_markup)
\ No newline at end of file
diff --git a/app/telegram/functions/condition_settings/settings.py b/app/telegram/functions/condition_settings/settings.py
new file mode 100644
index 0000000..1f98a02
--- /dev/null
+++ b/app/telegram/functions/condition_settings/settings.py
@@ -0,0 +1,82 @@
+import app.telegram.Keyboards.inline_keyboards as inline_markup
+import app.telegram.Keyboards.reply_keyboards as reply_markup
+
+import app.telegram.database.requests as rq
+
+async def reg_new_user_default_condition_settings(id, message):
+ tg_id = id
+
+ trigger = await rq.get_for_registration_trigger()
+
+ await rq.set_new_user_default_condition_settings(tg_id, trigger)
+
+async def main_settings_message(message):
+ text = """ Условия запуска
+
+Триггер: Ручной запуск / Сигнал TradingView / Полностью автоматический
+
+Фильтр времени: диапазон по дням недели и времени суток
+
+Фильтр волатильности / объёма: включить/отключить
+
+Интеграции и внешние сигналы:
+
+Использовать сигналы TradingView: да / нет
+
+Использовать AI-аналитику от ChatGPT: да / нет
+
+Webhook URL для сигналов (если используется TradingView).
+
+"""
+
+ await message.answer(text=text, parse_mode='html', reply_markup=inline_markup.condition_settings_markup)
+
+async def trigger_message(message, state):
+ text = '''Триггер
+
+ Описание ручного запуска, сигналов, автоматического режима '''
+
+ await message.answer(text=text, parse_mode='html', reply_markup=reply_markup.trigger_markup)
+
+async def filter_time_message(message, state):
+ text = '''Фильтр времени
+
+ ???
+ '''
+
+ await message.answer(text=text)
+
+async def filter_volatility_message(message, state):
+ text = '''Фильтр волатильности
+
+ Описание... '''
+
+ await message.answer(text=text, parse_mode='html', reply_markup=reply_markup.buttons_on_off_markup)
+
+async def external_cues_message(message, state):
+ text = '''Внешние сигналы
+
+ Описание... '''
+
+ await message.answer(text=text, parse_mode='html', reply_markup=None)
+
+async def trading_cues_message(message, state):
+ text = '''Использование сигналов
+
+ Описание... '''
+
+ await message.answer(text=text, parse_mode='html', reply_markup=reply_markup.buttons_yes_no_markup)
+
+async def webhook_message(message, state):
+ text = '''Скиньте ссылку на webhook (если есть trading view): '''
+
+ await message.answer(text=text, parse_mode='html')
+
+async def ai_analytics_message(message, state):
+ text = '''ИИ - Аналитика
+
+ Описание... '''
+
+ await message.answer(text=text, parse_mode='html', reply_markup=reply_markup.buttons_yes_no_markup)
+
+
diff --git a/app/telegram/functions/functions.py b/app/telegram/functions/functions.py
new file mode 100644
index 0000000..8b35365
--- /dev/null
+++ b/app/telegram/functions/functions.py
@@ -0,0 +1,34 @@
+import app.telegram.Keyboards.inline_keyboards as inline_markup
+import app.telegram.Keyboards.reply_keyboards as reply_markup
+
+async def start_message(message):
+ await message.answer(f""" Привет {message.from_user.username}! 👋
+
+Добро пожаловать в бот по трейдингу на Bibyt — вашего надежного помощника для анализа рынка и принятия взвешенных решений. Здесь вы получите:
+
+📊 Анализ текущих трендов
+📈 Инструменты для прогнозирования и оценки рисков
+⚡️ Сигналы и рекомендации по сделкам
+🔔 Уведомления о важных изменениях и новостях
+
+Просто отправляйте интересующий вас инструмент или команду, и бот быстро предоставит актуальную информацию и аналитику.
+
+Начнем торговать умно и эффективно вместе! 🚀
+
+""", parse_mode='html', reply_markup=inline_markup.start_markup)
+
+async def profile_message(username, message):
+ await message.answer(f""" {username}
+
+Баланс:
+⭐️ 0
+
+Описание:
+Активный трейдер на платформе Bibyt с индивидуальной стратегией и аналитикой в реальном времени. Постоянно улучшает навыки и следит за рыночными тенденциями для максимальной прибыли.
+""", parse_mode='html', reply_markup=inline_markup.settings_markup)
+
+async def check_profile_message(message):
+ await message.answer(f'Добро пожаловать {message.from_user.username}!', reply_markup=reply_markup.base_buttons_markup)
+
+async def settings_message(message):
+ await message.edit_text("Выберите что настроить", reply_markup=inline_markup.special_settings_markup)
\ No newline at end of file
diff --git a/app/telegram/functions/main_settings/settings.py b/app/telegram/functions/main_settings/settings.py
new file mode 100644
index 0000000..13c1a5d
--- /dev/null
+++ b/app/telegram/functions/main_settings/settings.py
@@ -0,0 +1,211 @@
+from aiogram import Router
+
+import app.telegram.Keyboards.inline_keyboards as inline_markup
+import app.telegram.Keyboards.reply_keyboards as reply_markup
+
+import app.telegram.database.requests as rq
+from aiogram.types import Message, CallbackQuery
+
+# FSM - Механизм состояния
+from aiogram.fsm.state import State, StatesGroup
+
+router_main_settings = Router()
+
+class update_main_settings(StatesGroup):
+ trading_mode = State() # +
+ size_leverage = State() #
+ margin_type = State() #
+ martingale_factor = State() #
+ starting_quantity = State() #
+ maximal_quantity = State() #
+
+async def reg_new_user_default_main_settings(id, message):
+ tg_id = id
+
+ trading_mode = await rq.get_for_registration_trading_mode()
+ margin_type = await rq.get_for_registration_margin_type()
+
+ await rq.set_new_user_default_main_settings(tg_id, trading_mode, margin_type)
+
+
+async def main_settings_message(id, message, state):
+ data = await rq.get_user_main_settings(id)
+
+ await message.answer(f"""Основные настройки
+Режим торговли: {data['trading_mode']}
+
+Тип маржи: {data['margin_type']}
+
+Размер кредитного плеча: х{data['size_leverage']}
+
+Начальная ставка: {data['starting_quantity']}
+
+Коэффициент мартингейла: {data['martingale_factor']}
+
+Максимальное количесиво ставок в серии: {data['maximal_quantity']}
+""", parse_mode='html', reply_markup=inline_markup.main_settings_markup)
+
+async def trading_mode_message(message, state):
+ await state.set_state(update_main_settings.trading_mode)
+
+ await message.edit_text("""Режим торговли
+
+Лонг — стратегия, ориентированная на покупку актива с целью заработать на повышении его стоимости.
+
+Шорт — метод продажи активов, взятых в кредит, чтобы получить прибыль от снижения цены.
+
+Смарт — автоматизированный режим, который подбирает оптимальную стратегию в зависимости от текущих рыночных условий.
+
+Свитч — динамическое переключение между торговыми режимами для максимизации эффективности.
+
+Выберите ниже для изменений:
+""", parse_mode='html', reply_markup=inline_markup.trading_mode_markup)
+
+@router_main_settings.callback_query(update_main_settings.trading_mode)
+async def state_trading_mode(callback: CallbackQuery, state):
+ await callback.answer()
+
+ id = callback.from_user.id
+ print(f"sdljfngdjklfg ## {callback.data}")
+
+ try:
+ match callback.data:
+ case 'trade_mode_long':
+ await rq.update_trade_mode_user(id, 'Long')
+ await main_settings_message(id, callback.message, state)
+
+ await state.clear()
+ case 'trade_mode_short':
+ await rq.update_trade_mode_user(id, 'Short')
+ await main_settings_message(id, callback.message, state)
+
+ await state.clear()
+ case 'trade_mode_switch':
+ await rq.update_trade_mode_user(id, 'Switch')
+ await main_settings_message(id, callback.message, state)
+
+ await state.clear()
+ case 'trade_mode_smart':
+ await rq.update_trade_mode_user(id, 'Smart')
+ await main_settings_message(id, callback.message, state)
+
+ await state.clear()
+ except Exception as e:
+ print(f"error: {e}")
+
+async def size_leverage_message (message, state):
+ await state.set_state(update_main_settings.size_leverage)
+
+ await message.edit_text("Введите размер кредитного плеча (от 1 до 100): ", parse_mode='html', reply_markup=inline_markup.back_btn_list_settings_markup)
+
+@router_main_settings.message(update_main_settings.size_leverage)
+async def state_size_leverage(message: Message, state):
+ await state.update_data(size_leverage = message.text)
+
+ data = await state.get_data()
+
+ if data['size_leverage'].isdigit() and int(data['size_leverage']) <= 100:
+ await rq.update_size_leverange(message.from_user.id, data['size_leverage'])
+ await main_settings_message(message.from_user.id, message, state)
+
+ await state.clear()
+ else:
+ await main_settings_message(message.from_user.id, message, state)
+
+async def martingale_factor_message(message, state):
+ await state.set_state(update_main_settings.martingale_factor)
+
+ await message.edit_text("Введите коэффициент Мартингейла:", parse_mode='html', reply_markup=inline_markup.back_btn_list_settings_markup)
+
+@router_main_settings.message(update_main_settings.martingale_factor)
+async def state_martingale_factor(message: Message, state):
+ await state.update_data(martingale_factor = message.text)
+
+ data = await state.get_data()
+
+ if data['martingale_factor'].isdigit() and int(data['martingale_factor']) <= 100:
+ await rq.update_martingale_factor(message.from_user.id, data['martingale_factor'])
+ await main_settings_message(message.from_user.id, message, state)
+
+ await state.clear()
+ else:
+ await main_settings_message(message.from_user.id, message, state)
+
+async def margin_type_message(message, state):
+ await state.set_state(update_main_settings.margin_type)
+
+ await message.edit_text("""Тип маржи
+
+Изолированная маржа
+Этот тип маржи позволяет ограничить риск конкретной позиции.
+При использовании изолированной маржи вы выделяете определённую сумму средств только для одной позиции.
+Если позиция начинает приносить убытки, ваши потери ограничиваются этой суммой,
+и остальные средства на счёте не затрагиваются.
+
+Кросс-маржа
+Кросс-маржа объединяет весь маржинальный баланс на счёте и использует все доступные средства для поддержания открытых позиций.
+В случае убытков средства с других позиций или баланса автоматически покрывают дефицит,
+снижая риск ликвидации, но увеличивая общий риск потери капитала.
+
+Выберите ниже для изменений:
+""", parse_mode='html', reply_markup=inline_markup.margin_type_markup)
+
+@router_main_settings.callback_query(update_main_settings.margin_type)
+async def state_margin_type(callback: CallbackQuery, state):
+ await callback.answer()
+
+ id = callback.from_user.id
+ print(f"sdljfngdjklfg ## {callback.data}")
+
+ try:
+ match callback.data:
+ case 'margin_type_isolated':
+ await rq.update_margin_type(id, 'Изолированный')
+ await main_settings_message(id, callback.message, state)
+
+ await state.clear()
+ case 'margin_type_cross':
+ await rq.update_margin_type(id, 'Кросс')
+ await main_settings_message(id, callback.message, state)
+
+ await state.clear()
+ except Exception as e:
+ print(f"error: {e}")
+
+async def starting_quantity_message (message, state):
+ await state.set_state(update_main_settings.starting_quantity)
+
+ await message.edit_text("Введите началаьную ставку:", parse_mode='html', reply_markup=inline_markup.back_btn_list_settings_markup)
+
+@router_main_settings.message(update_main_settings.starting_quantity)
+async def state_starting_quantity(message: Message, state):
+ await state.update_data(starting_quantity = message.text)
+
+ data = await state.get_data()
+
+ if data['starting_quantity'].isdigit() and int(data['starting_quantity']) <= 100:
+ await rq.update_starting_quantity(message.from_user.id, data['starting_quantity'])
+ await main_settings_message(message.from_user.id, message, state)
+
+ await state.clear()
+ else:
+ await main_settings_message(message.from_user.id, message, state)
+
+async def maximum_quantity_message(message, state):
+ await state.set_state(update_main_settings.maximal_quantity)
+
+ await message.edit_text("Введите максимальное количество ставок:", parse_mode='html', reply_markup=inline_markup.back_btn_list_settings_markup)
+
+@router_main_settings.message(update_main_settings.maximal_quantity)
+async def state_maximal_quantity(message: Message, state):
+ await state.update_data(maximal_quantity = message.text)
+
+ data = await state.get_data()
+
+ if data['maximal_quantity'].isdigit() and int(data['maximal_quantity']) <= 100:
+ await rq.update_maximal_quantity(message.from_user.id, data['maximal_quantity'])
+ await main_settings_message(message.from_user.id, message, state)
+
+ await state.clear()
+ else:
+ await main_settings_message(message.from_user.id, message, state)
\ No newline at end of file
diff --git a/app/telegram/functions/risk_management_settings/settings.py b/app/telegram/functions/risk_management_settings/settings.py
new file mode 100644
index 0000000..541672f
--- /dev/null
+++ b/app/telegram/functions/risk_management_settings/settings.py
@@ -0,0 +1,37 @@
+import app.telegram.Keyboards.inline_keyboards as inline_markup
+import app.telegram.Keyboards.reply_keyboards as reply_markup
+
+import app.telegram.database.requests as rq
+
+async def reg_new_user_default_risk_management_settings(id, message):
+ tg_id = id
+
+ await rq.set_new_user_default_risk_management_settings(tg_id)
+
+
+async def main_settings_message(message):
+ text = f"""Риск менеджмент,
+
+ Процент изменения цены для фиксации прибыли: 0
+
+ Процент изменения цены для фиксации убытков: 0
+
+ Максимальный риск на сделку (в % от баланса): 0
+ """
+
+ await message.edit_text(text=text, parse_mode='html', reply_markup=inline_markup.risk_management_settings_markup)
+
+async def price_profit_message(message, state):
+ text = 'Введите число изменения цены для фиксации прибыли: '
+
+ await message.answer(text=text, parse_mode='html', reply_markup=None)
+
+async def price_loss_message(message, state):
+ text = 'Введите число изменения цены для фиксации убытков: '
+
+ await message.answer(text=text, parse_mode='html', reply_markup=None)
+
+async def max_risk_deal_message(message, state):
+ text = 'Введите число (процент от баланса) для изменения максимального риска на сделку: '
+
+ await message.answer(text=text, parse_mode='html', reply_markup=None)
\ No newline at end of file
diff --git a/app/telegram/handlers/handlers.py b/app/telegram/handlers/handlers.py
new file mode 100644
index 0000000..a5b6533
--- /dev/null
+++ b/app/telegram/handlers/handlers.py
@@ -0,0 +1,195 @@
+import logging
+
+from aiogram import F, Router
+from aiogram.filters import CommandStart, Command
+from aiogram.types import Message, CallbackQuery
+from aiogram.fsm.context import FSMContext
+
+import app.telegram.functions.functions as func # functions
+import app.telegram.functions.main_settings.settings as func_main_settings
+import app.telegram.functions.risk_management_settings.settings as func_rmanagement_settings
+import app.telegram.functions.condition_settings.settings as func_condition_settings
+import app.telegram.functions.additional_settings.settings as func_additional_settings
+
+import app.telegram.database.requests as rq
+import app.telegram.Keyboards.inline_keyboards as inline_markup
+import app.telegram.Keyboards.reply_keyboards as reply_markup
+
+router = Router()
+
+@router.message(CommandStart())
+async def start_message(message: Message):
+ await func.start_message(message)
+
+@router.message(F.text == "👤 Профиль")
+async def profile_message(message: Message):
+ user = await rq.check_user(message.from_user.id)
+
+ if user:
+ await func.profile_message(message.from_user.username, message)
+
+@router.message(F.text == "Настройки")
+async def settings_msg(message: Message):
+ user = await rq.check_user(message.from_user.id)
+
+ if user:
+ await func.settings_message(message)
+
+@router.callback_query(F.data == "callback_registration")
+async def clb_func_reg (callback: CallbackQuery):
+ await rq.save_tg_id_new_user(callback.from_user.id)
+ print(callback.from_user.id)
+
+ await func_main_settings.reg_new_user_default_main_settings(callback.from_user.id, callback.message)
+ await func_rmanagement_settings.reg_new_user_default_risk_management_settings(callback.from_user.id, callback.message)
+ await func_condition_settings.reg_new_user_default_condition_settings(callback.from_user.id, callback.message)
+ await func_additional_settings.reg_new_user_default_additional_settings(callback.from_user.id, callback.message)
+
+ await callback.message.answer(f'Регистрация прошла успешно, здравствуйте {callback.from_user.username}!', reply_markup=reply_markup.base_buttons_markup)
+
+ await callback.answer()
+
+@router.callback_query(F.data == "callback_autorisation")
+async def clb_func_reg (callback: CallbackQuery):
+ user = await rq.check_user(callback.from_user.id)
+
+ if user:
+ await func.profile_message(callback.from_user.username, callback.message)
+
+ # Настройки торговли
+@router.callback_query(F.data == "clb_settings_message")
+async def clb_settings_msg (callback: CallbackQuery):
+ await func.settings_message(callback.message)
+
+ await callback.answer()
+
+@router.callback_query(F.data == "clb_back_to_special_settings_message")
+async def clb_back_to_settings_msg(callback: CallbackQuery):
+ await func.settings_message(callback.message)
+
+ await callback.answer()
+
+@router.callback_query(F.data == "clb_change_main_settings")
+async def clb_change_main_settings_message(callback: CallbackQuery, state: FSMContext):
+ await func_main_settings.main_settings_message(callback.from_user.id, callback.message, state)
+
+ await callback.answer()
+
+@router.callback_query(F.data == "clb_change_risk_management_settings")
+async def clb_change_risk_management_message(callback: CallbackQuery, state: FSMContext):
+ await func_rmanagement_settings.main_settings_message(callback.from_user.id, callback.message, state)
+
+ await callback.answer()
+
+@router.callback_query(F.data == "clb_change_condition_settings")
+async def clb_change_condition_message(callback: CallbackQuery, state: FSMContext):
+ await func_condition_settings.main_settings_message(callback.from_user.id, callback.message, state)
+
+ await callback.answer()
+
+@router.callback_query(F.data == "clb_change_additional_settings")
+async def clb_change_additional_message(callback: CallbackQuery, state: FSMContext):
+ await func_additional_settings.main_settings_message(callback.from_user.id, callback.message, state)
+
+ await callback.answer()
+
+ # Конкретные настройки каталогов
+list_main_settings = ['clb_change_trading_mode',
+ 'clb_change_margin_type',
+ 'clb_change_size_leverage',
+ 'clb_change_starting_quantity',
+ 'clb_change_martingale_factor',
+ 'clb_change_maximum_quantity'
+]
+@router.callback_query(F.data.in_(list_main_settings))
+async def clb_main_settings_msg(callback: CallbackQuery, state: FSMContext):
+ await callback.answer()
+
+ try:
+ match callback.data:
+ case 'clb_change_trading_mode':
+ await func_main_settings.trading_mode_message(callback.message, state)
+ case 'clb_change_margin_type':
+ await func_main_settings.margin_type_message(callback.message, state)
+ case 'clb_change_size_leverage':
+ await func_main_settings.size_leverage_message(callback.message, state)
+ case 'clb_change_starting_quantity':
+ await func_main_settings.starting_quantity_message(callback.message, state)
+ case 'clb_change_martingale_factor':
+ await func_main_settings.martingale_factor_message(callback.message, state)
+ case 'clb_change_maximum_quantity':
+ await func_main_settings.maximum_quantity_message(callback.message, state)
+ except Exception as e:
+ logging.error(f"Error callback in main_settings match-case: {e}")
+
+
+list_risk_management_settings = ['clb_change_price_profit',
+ 'clb_change_price_loss',
+ 'clb_change_max_risk_deal',
+]
+@router.callback_query(F.data.in_(list_risk_management_settings))
+async def clb_risk_management_settings_msg(callback: CallbackQuery, state: FSMContext):
+ await callback.answer()
+
+ try:
+ match callback.data:
+ case 'clb_change_price_profit':
+ await func_rmanagement_settings.price_profit_message(callback.message, state)
+ case 'clb_change_price_loss':
+ await func_rmanagement_settings.price_loss_message(callback.message, state)
+ case 'clb_change_max_risk_deal':
+ await func_rmanagement_settings.max_risk_deal_message(callback.message, state)
+ except Exception as e:
+ logging.error(f"Error callback in risk_management match-case: {e}")
+
+
+list_condition_settings = ['clb_change_trigger',
+ 'clb_change_filter_time',
+ 'clb_change_filter_volatility',
+ 'clb_change_external_cues',
+ 'clb_change_tradingview_cues',
+ 'clb_change_webhook',
+ 'clb_change_ai_analytics'
+]
+@router.callback_query(F.data.in_(list_condition_settings))
+async def clb_condition_settings_msg(callback: CallbackQuery, state: FSMContext):
+ await callback.answer()
+
+ try:
+ match callback.data:
+ case 'clb_change_trigger':
+ await func_condition_settings.trigger_message(callback.message, state)
+ case 'clb_change_filter_time':
+ await func_condition_settings.filter_time_message(callback.message, state)
+ case 'clb_change_filter_volatility':
+ await func_condition_settings.filter_volatility_message(callback.message, state)
+ case 'clb_change_external_cues':
+ await func_condition_settings.external_cues_message(callback.message, state)
+ case 'clb_change_tradingview_cues':
+ await func_condition_settings.trading_cues_message(callback.message, state)
+ case 'clb_change_webhook':
+ await func_condition_settings.webhook_message(callback.message, state)
+ case 'clb_change_ai_analytics':
+ await func_condition_settings.ai_analytics_message(callback.message, state)
+ except Exception as e:
+ logging.error(f"Error callback in main_settings match-case: {e}")
+
+
+list_additional_settings = ['clb_change_save_pattern',
+ 'clb_change_auto_start',
+ 'clb_change_notifications',
+]
+@router.callback_query(F.data.in_(list_additional_settings))
+async def clb_additional_settings_msg(callback: CallbackQuery, state: FSMContext):
+ await callback.answer()
+
+ try:
+ match callback.data:
+ case 'clb_change_save_pattern':
+ await func_additional_settings.save_pattern_message(callback.message, state)
+ case 'clb_change_auto_start':
+ await func_additional_settings.auto_start_message(callback.message, state)
+ case 'clb_change_notifications':
+ await func_additional_settings.notifications_message(callback.message, state)
+ except Exception as e:
+ logging.error(f"Error callback in additional_settings match-case: {e}")
\ No newline at end of file
diff --git a/app/telegram/logs.py b/app/telegram/logs.py
new file mode 100644
index 0000000..eaf7fa3
--- /dev/null
+++ b/app/telegram/logs.py
@@ -0,0 +1,8 @@
+import logging
+
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+)
+
+logger = logging.getLogger(__name__)
\ No newline at end of file
diff --git a/config.py b/config.py
new file mode 100644
index 0000000..230e0c2
--- /dev/null
+++ b/config.py
@@ -0,0 +1,6 @@
+from dotenv import load_dotenv
+import os
+
+load_dotenv('.env')
+
+TOKEN_TG_BOT = os.getenv('TOKEN_TELEGRAM_BOT')
\ No newline at end of file