2
0
forked from kodorvan/stcs

3 Commits

39 changed files with 1120 additions and 76 deletions

View File

@@ -1 +1,6 @@
BOT_TOKEN=YOUR_BOT_TOKEN BOT_TOKEN=YOUR_BOT_TOKEN
DB_USER=your_username
DB_PASS=your_password
DB_HOST=your_host
DB_PORT=your_port
DB_NAME=your_database

1
.gitignore vendored
View File

@@ -148,7 +148,6 @@ env.bak/
venv.bak/ venv.bak/
/logger_helper/loggers /logger_helper/loggers
/app/bybit/logger_bybit/loggers /app/bybit/logger_bybit/loggers
*.db
# Spyder project settings # Spyder project settings
.spyderproject .spyderproject
.spyproject .spyproject

View File

@@ -55,7 +55,12 @@ cp .env.sample .env
nvim .env nvim .env
``` ```
5. Запустите бота: 5. Для применения миграций выполните команду:
```bash
alembic upgrade head
```
6. Запустите бота:
```bash ```bash
python run.py python run.py

View File

@@ -84,7 +84,7 @@ path_separator = os
# database URL. This is consumed by the user-maintained env.py script only. # database URL. This is consumed by the user-maintained env.py script only.
# other means of configuring database URLs may be customized within the env.py # other means of configuring database URLs may be customized within the env.py
# file. # file.
sqlalchemy.url = sqlite+aiosqlite:///./database/db/stcs.db sqlalchemy.url = driver://user:pass@localhost/dbname
[post_write_hooks] [post_write_hooks]

View File

@@ -1,29 +1,73 @@
import asyncio import asyncio
from logging.config import fileConfig from logging.config import fileConfig
from sqlalchemy import pool from sqlalchemy import pool
from sqlalchemy.engine import Connection
from sqlalchemy.ext.asyncio import async_engine_from_config from sqlalchemy.ext.asyncio import async_engine_from_config
from alembic import context from alembic import context
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
from config import DATABASE_URL
config = context.config config = context.config
config.set_main_option('sqlalchemy.url', DATABASE_URL)
# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None: if config.config_file_name is not None:
fileConfig(config.config_file_name) fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from database.models import Base from database.models import Base
target_metadata = Base.metadata target_metadata = Base.metadata
def do_run_migrations(connection): # other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
context.configure( context.configure(
connection=connection, url=DATABASE_URL,
target_metadata=target_metadata, target_metadata=target_metadata,
compare_type=True, literal_binds=True,
dialect_opts={"paramstyle": "named"},
) )
with context.begin_transaction(): with context.begin_transaction():
context.run_migrations() context.run_migrations()
async def run_async_migrations():
def do_run_migrations(connection: Connection) -> None:
context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
async def run_async_migrations() -> None:
"""In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = async_engine_from_config( connectable = async_engine_from_config(
config.get_section(config.config_ini_section), config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.", prefix="sqlalchemy.",
poolclass=pool.NullPool, poolclass=pool.NullPool,
) )
@@ -33,20 +77,13 @@ async def run_async_migrations():
await connectable.dispose() await connectable.dispose()
def run_migrations_offline():
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online(): def run_migrations_online() -> None:
"""Run migrations in 'online' mode."""
asyncio.run(run_async_migrations()) asyncio.run(run_async_migrations())
if context.is_offline_mode(): if context.is_offline_mode():
run_migrations_offline() run_migrations_offline()
else: else:

View File

@@ -0,0 +1,40 @@
"""fixed switch_side type
Revision ID: 07020b2808d3
Revises: c710f4e2259c
Create Date: 2025-10-09 14:36:07.393387
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '07020b2808d3'
down_revision: Union[str, Sequence[str], None] = 'c710f4e2259c'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user_additional_settings', 'switch_side',
existing_type=sa.BOOLEAN(),
type_=sa.String(),
existing_nullable=False,
existing_server_default=sa.text('false'))
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user_additional_settings', 'switch_side',
existing_type=sa.String(),
type_=sa.BOOLEAN(),
existing_nullable=False,
existing_server_default=sa.text('false'))
# ### end Alembic commands ###

View File

@@ -0,0 +1,36 @@
"""updated user deals table
Revision ID: 09db71875980
Revises: 77197715747c
Create Date: 2025-09-29 12:57:39.943294
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '09db71875980'
down_revision: Union[str, Sequence[str], None] = '77197715747c'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('order_quantity', sa.Float(), nullable=True))
op.create_unique_constraint('uq_user_symbol', 'user_deals', ['user_id', 'symbol'])
op.drop_column('user_deals', 'quantity')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('quantity', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True))
op.drop_constraint('uq_user_symbol', 'user_deals', type_='unique')
op.drop_column('user_deals', 'order_quantity')
# ### end Alembic commands ###

View File

@@ -0,0 +1,40 @@
"""Added conditional_order_type
Revision ID: 0eed68eddcdb
Revises: 70094ba27e80
Create Date: 2025-09-24 13:47:23.282807
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '0eed68eddcdb'
down_revision: Union[str, Sequence[str], None] = '70094ba27e80'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_additional_settings', sa.Column('conditional_order_type', sa.String(), nullable=False))
op.add_column('user_additional_settings', sa.Column('limit_price', sa.Float(), nullable=False))
op.add_column('user_additional_settings', sa.Column('trigger_price', sa.Float(), nullable=False))
op.drop_column('user_conditional_settings', 'trigger_price')
op.drop_column('user_conditional_settings', 'limit_price')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_conditional_settings', sa.Column('limit_price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=False))
op.add_column('user_conditional_settings', sa.Column('trigger_price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=False))
op.drop_column('user_additional_settings', 'trigger_price')
op.drop_column('user_additional_settings', 'limit_price')
op.drop_column('user_additional_settings', 'conditional_order_type')
# ### end Alembic commands ###

View File

@@ -0,0 +1,34 @@
"""Added fee for user auto trading
Revision ID: 10bf073c71f9
Revises: 2b9572b49ecd
Create Date: 2025-10-02 17:52:05.235523
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '10bf073c71f9'
down_revision: Union[str, Sequence[str], None] = '2b9572b49ecd'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_auto_trading', sa.Column('fee', sa.Float(), nullable=True))
op.drop_column('user_deals', 'fee')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('fee', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True))
op.drop_column('user_auto_trading', 'fee')
# ### end Alembic commands ###

View File

@@ -0,0 +1,34 @@
"""Added side for user auto trading
Revision ID: 2b9572b49ecd
Revises: ef342b38e17b
Create Date: 2025-10-02 17:21:20.904797
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '2b9572b49ecd'
down_revision: Union[str, Sequence[str], None] = 'ef342b38e17b'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_auto_trading', sa.Column('side', sa.String(), nullable=True))
op.drop_constraint(op.f('uq_user_auto_trading_symbol'), 'user_auto_trading', type_='unique')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_unique_constraint(op.f('uq_user_auto_trading_symbol'), 'user_auto_trading', ['user_id', 'symbol'], postgresql_nulls_not_distinct=False)
op.drop_column('user_auto_trading', 'side')
# ### end Alembic commands ###

View File

@@ -0,0 +1,32 @@
"""update last side the conditional data
Revision ID: 3534adf891fc
Revises: ef38c90eed55
Create Date: 2025-09-30 08:39:02.971158
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '3534adf891fc'
down_revision: Union[str, Sequence[str], None] = 'ef38c90eed55'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View File

@@ -0,0 +1,38 @@
"""Updated martingale factor
Revision ID: 42c66cfe8d4e
Revises: 45977e9d8558
Create Date: 2025-09-22 17:17:39.779979
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '42c66cfe8d4e'
down_revision: Union[str, Sequence[str], None] = '45977e9d8558'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user_additional_settings', 'martingale_factor',
existing_type=sa.INTEGER(),
type_=sa.Float(),
existing_nullable=False)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user_additional_settings', 'martingale_factor',
existing_type=sa.Float(),
type_=sa.INTEGER(),
existing_nullable=False)
# ### end Alembic commands ###

View File

@@ -0,0 +1,38 @@
"""Updated order quantity
Revision ID: 45977e9d8558
Revises: fd8581c0cc87
Create Date: 2025-09-22 16:59:40.415398
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '45977e9d8558'
down_revision: Union[str, Sequence[str], None] = 'fd8581c0cc87'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user_additional_settings', 'order_quantity',
existing_type=sa.INTEGER(),
type_=sa.Float(),
existing_nullable=False)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user_additional_settings', 'order_quantity',
existing_type=sa.Float(),
type_=sa.INTEGER(),
existing_nullable=False)
# ### end Alembic commands ###

View File

@@ -0,0 +1,40 @@
"""Create User Conditional Setting
Revision ID: 70094ba27e80
Revises: 42c66cfe8d4e
Create Date: 2025-09-23 16:47:07.161544
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '70094ba27e80'
down_revision: Union[str, Sequence[str], None] = '42c66cfe8d4e'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('user_conditional_settings',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('limit_price', sa.Float(), nullable=False),
sa.Column('trigger_price', sa.Float(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('user_id')
)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('user_conditional_settings')
# ### end Alembic commands ###

View File

@@ -0,0 +1,42 @@
"""added user_auto_trading table
Revision ID: 73a00faa4f7f
Revises: 968f8121104f
Create Date: 2025-10-01 12:30:21.830851
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '73a00faa4f7f'
down_revision: Union[str, Sequence[str], None] = '968f8121104f'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('user_auto_trading',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('symbol', sa.String(), nullable=True),
sa.Column('auto_trading', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('user_id', 'symbol', name='uq_user_auto_trading_symbol')
)
op.drop_column('user_conditional_settings', 'auto_trading')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_conditional_settings', sa.Column('auto_trading', sa.BOOLEAN(), autoincrement=False, nullable=True))
op.drop_table('user_auto_trading')
# ### end Alembic commands ###

View File

@@ -0,0 +1,32 @@
"""deleted position_idx for user deals table
Revision ID: 77197715747c
Revises: 8f1476c68efa
Create Date: 2025-09-29 12:20:18.928995
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '77197715747c'
down_revision: Union[str, Sequence[str], None] = '8f1476c68efa'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('user_deals', 'position_idx')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('position_idx', sa.INTEGER(), autoincrement=False, nullable=True))
# ### end Alembic commands ###

View File

@@ -0,0 +1,32 @@
"""Updated Deals
Revision ID: 863d6215e1eb
Revises: f00a94ccdf01
Create Date: 2025-09-28 23:13:39.484468
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '863d6215e1eb'
down_revision: Union[str, Sequence[str], None] = 'f00a94ccdf01'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('margin_type', sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('user_deals', 'margin_type')
# ### end Alembic commands ###

View File

@@ -0,0 +1,34 @@
"""added position_idx for user deals table
Revision ID: 8f1476c68efa
Revises: 863d6215e1eb
Create Date: 2025-09-29 11:40:46.512160
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '8f1476c68efa'
down_revision: Union[str, Sequence[str], None] = '863d6215e1eb'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('position_idx', sa.Integer(), nullable=True))
op.drop_constraint(op.f('user_deals_user_id_key'), 'user_deals', type_='unique')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_unique_constraint(op.f('user_deals_user_id_key'), 'user_deals', ['user_id'], postgresql_nulls_not_distinct=False)
op.drop_column('user_deals', 'position_idx')
# ### end Alembic commands ###

View File

@@ -0,0 +1,36 @@
"""updated user_deals and user_conditional_settings
Revision ID: 968f8121104f
Revises: dbffe818030c
Create Date: 2025-10-01 11:45:49.073865
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '968f8121104f'
down_revision: Union[str, Sequence[str], None] = 'dbffe818030c'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_conditional_settings', sa.Column('auto_trading', sa.Boolean(), nullable=True))
op.drop_column('user_deals', 'commission_fee')
op.drop_column('user_deals', 'auto_trading')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('auto_trading', sa.BOOLEAN(), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('commission_fee', sa.VARCHAR(), autoincrement=False, nullable=True))
op.drop_column('user_conditional_settings', 'auto_trading')
# ### end Alembic commands ###

View File

@@ -0,0 +1,60 @@
"""Updated UserDeals
Revision ID: acbcc95de48d
Revises: ccdc5764eb4f
Create Date: 2025-09-28 16:57:28.384116
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'acbcc95de48d'
down_revision: Union[str, Sequence[str], None] = 'ccdc5764eb4f'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_conditional_settings', sa.Column('auto_trading', sa.String(), nullable=False))
op.add_column('user_deals', sa.Column('trading_type', sa.String(), nullable=True))
op.add_column('user_deals', sa.Column('conditional_order_type', sa.String(), nullable=True))
op.add_column('user_deals', sa.Column('take_profit_percent', sa.Integer(), nullable=True))
op.add_column('user_deals', sa.Column('stop_loss_percent', sa.Integer(), nullable=True))
op.add_column('user_deals', sa.Column('max_risk_percent', sa.Integer(), nullable=True))
op.add_column('user_deals', sa.Column('commission_fee', sa.String(), nullable=True))
op.add_column('user_deals', sa.Column('switch_side_mode', sa.String(), nullable=True))
op.drop_index(op.f('ix_user_deals_deal_series_id'), table_name='user_deals')
op.drop_column('user_deals', 'take_profit')
op.drop_column('user_deals', 'deal_series_id')
op.drop_column('user_deals', 'price')
op.drop_column('user_deals', 'exec_fee')
op.drop_column('user_deals', 'stop_loss')
op.drop_column('user_deals', 'closed_size')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('closed_size', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('stop_loss', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('exec_fee', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('deal_series_id', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('take_profit', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True))
op.create_index(op.f('ix_user_deals_deal_series_id'), 'user_deals', ['deal_series_id'], unique=False)
op.drop_column('user_deals', 'switch_side_mode')
op.drop_column('user_deals', 'commission_fee')
op.drop_column('user_deals', 'max_risk_percent')
op.drop_column('user_deals', 'stop_loss_percent')
op.drop_column('user_deals', 'take_profit_percent')
op.drop_column('user_deals', 'conditional_order_type')
op.drop_column('user_deals', 'trading_type')
op.drop_column('user_conditional_settings', 'auto_trading')
# ### end Alembic commands ###

View File

@@ -0,0 +1,34 @@
"""added base_quantity
Revision ID: baf03ce269e0
Revises: 07020b2808d3
Create Date: 2025-10-09 16:49:52.979556
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'baf03ce269e0'
down_revision: Union[str, Sequence[str], None] = '07020b2808d3'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('base_quantity', sa.Float(), nullable=True))
op.drop_column('user_deals', 'trading_type')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('trading_type', sa.VARCHAR(), autoincrement=False, nullable=True))
op.drop_column('user_deals', 'base_quantity')
# ### end Alembic commands ###

View File

@@ -0,0 +1,79 @@
"""unnecessary data has been deleted
Revision ID: c710f4e2259c
Revises: 10bf073c71f9
Create Date: 2025-10-09 14:17:32.632574
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'c710f4e2259c'
down_revision: Union[str, Sequence[str], None] = '10bf073c71f9'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_additional_settings',
sa.Column('switch_side', sa.Boolean(), nullable=False, server_default=sa.false()))
op.drop_column('user_additional_settings', 'leverage_to_buy')
op.drop_column('user_additional_settings', 'order_type')
op.drop_column('user_additional_settings', 'limit_price')
op.drop_column('user_additional_settings', 'leverage_to_sell')
op.drop_column('user_additional_settings', 'conditional_order_type')
op.add_column('user_auto_trading', sa.Column('total_fee', sa.Float(), nullable=True))
op.drop_column('user_auto_trading', 'side')
op.drop_column('user_deals', 'switch_side_mode')
op.drop_column('user_deals', 'leverage_to_buy')
op.drop_column('user_deals', 'order_type')
op.drop_column('user_deals', 'limit_price')
op.drop_column('user_deals', 'max_risk_percent')
op.drop_column('user_deals', 'leverage_to_sell')
op.drop_column('user_deals', 'conditional_order_type')
op.alter_column('user_risk_management', 'take_profit_percent',
existing_type=sa.INTEGER(),
type_=sa.Float(),
existing_nullable=False)
op.alter_column('user_risk_management', 'stop_loss_percent',
existing_type=sa.INTEGER(),
type_=sa.Float(),
existing_nullable=False)
op.drop_column('user_risk_management', 'max_risk_percent')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_risk_management', sa.Column('max_risk_percent', sa.INTEGER(), autoincrement=False, nullable=False))
op.alter_column('user_risk_management', 'stop_loss_percent',
existing_type=sa.Float(),
type_=sa.INTEGER(),
existing_nullable=False)
op.alter_column('user_risk_management', 'take_profit_percent',
existing_type=sa.Float(),
type_=sa.INTEGER(),
existing_nullable=False)
op.add_column('user_deals', sa.Column('conditional_order_type', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('leverage_to_sell', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('max_risk_percent', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('limit_price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('order_type', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('leverage_to_buy', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('user_deals', sa.Column('switch_side_mode', sa.BOOLEAN(), autoincrement=False, nullable=True))
op.add_column('user_auto_trading', sa.Column('side', sa.VARCHAR(), autoincrement=False, nullable=True))
op.drop_column('user_auto_trading', 'total_fee')
op.add_column('user_additional_settings', sa.Column('conditional_order_type', sa.VARCHAR(), autoincrement=False, nullable=False))
op.add_column('user_additional_settings', sa.Column('leverage_to_sell', sa.VARCHAR(), autoincrement=False, nullable=False))
op.add_column('user_additional_settings', sa.Column('limit_price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=False))
op.add_column('user_additional_settings', sa.Column('order_type', sa.VARCHAR(), server_default=sa.text("'Market'::character varying"), autoincrement=False, nullable=False))
op.add_column('user_additional_settings', sa.Column('leverage_to_buy', sa.VARCHAR(), autoincrement=False, nullable=False))
op.drop_column('user_additional_settings', 'switch_side')
# ### end Alembic commands ###

View File

@@ -0,0 +1,38 @@
"""Fixed auto_trade
Revision ID: c98b9dc36d15
Revises: acbcc95de48d
Create Date: 2025-09-28 21:33:08.319232
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'c98b9dc36d15'
down_revision: Union[str, Sequence[str], None] = 'acbcc95de48d'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user_conditional_settings', 'auto_trading',
existing_type=sa.VARCHAR(),
type_=sa.Boolean(),
existing_nullable=False)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user_conditional_settings', 'auto_trading',
existing_type=sa.Boolean(),
type_=sa.VARCHAR(),
existing_nullable=False)
# ### end Alembic commands ###

View File

@@ -0,0 +1,50 @@
"""Added UserDeals
Revision ID: ccdc5764eb4f
Revises: 0eed68eddcdb
Create Date: 2025-09-25 22:39:17.246594
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'ccdc5764eb4f'
down_revision: Union[str, Sequence[str], None] = '0eed68eddcdb'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_conditional_settings', sa.Column('timer_start', sa.Integer(), nullable=False))
op.add_column('user_conditional_settings', sa.Column('timer_end', sa.Integer(), nullable=False))
op.add_column('user_deals', sa.Column('trade_mode', sa.String(), nullable=True))
op.add_column('user_deals', sa.Column('order_type', sa.String(), nullable=True))
op.add_column('user_deals', sa.Column('leverage', sa.String(), nullable=True))
op.add_column('user_deals', sa.Column('leverage_to_buy', sa.String(), nullable=True))
op.add_column('user_deals', sa.Column('leverage_to_sell', sa.String(), nullable=True))
op.add_column('user_deals', sa.Column('closed_side', sa.String(), nullable=True))
op.add_column('user_deals', sa.Column('martingale_factor', sa.Float(), nullable=True))
op.add_column('user_deals', sa.Column('max_bets_in_series', sa.Integer(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('user_deals', 'max_bets_in_series')
op.drop_column('user_deals', 'martingale_factor')
op.drop_column('user_deals', 'closed_side')
op.drop_column('user_deals', 'leverage_to_sell')
op.drop_column('user_deals', 'leverage_to_buy')
op.drop_column('user_deals', 'leverage')
op.drop_column('user_deals', 'order_type')
op.drop_column('user_deals', 'trade_mode')
op.drop_column('user_conditional_settings', 'timer_end')
op.drop_column('user_conditional_settings', 'timer_start')
# ### end Alembic commands ###

View File

@@ -0,0 +1,34 @@
"""added limit and trigger price for user deals
Revision ID: d3c85bad8c98
Revises: 09db71875980
Create Date: 2025-09-29 16:50:36.818798
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'd3c85bad8c98'
down_revision: Union[str, Sequence[str], None] = '09db71875980'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('limit_price', sa.Float(), nullable=True))
op.add_column('user_deals', sa.Column('trigger_price', sa.Float(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('user_deals', 'trigger_price')
op.drop_column('user_deals', 'limit_price')
# ### end Alembic commands ###

View File

@@ -0,0 +1,40 @@
"""added last_side and auto_trading for user_deals
Revision ID: dbffe818030c
Revises: 3534adf891fc
Create Date: 2025-10-01 09:29:55.554101
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'dbffe818030c'
down_revision: Union[str, Sequence[str], None] = '3534adf891fc'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('user_conditional_settings', 'last_side')
op.drop_column('user_conditional_settings', 'auto_trading')
op.add_column('user_deals', sa.Column('last_side', sa.String(), nullable=True))
op.add_column('user_deals', sa.Column('auto_trading', sa.Boolean(), nullable=True))
op.drop_column('user_deals', 'side')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('side', sa.VARCHAR(), autoincrement=False, nullable=True))
op.drop_column('user_deals', 'auto_trading')
op.drop_column('user_deals', 'last_side')
op.add_column('user_conditional_settings', sa.Column('auto_trading', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
op.add_column('user_conditional_settings', sa.Column('last_side', sa.VARCHAR(), autoincrement=False, nullable=False))
# ### end Alembic commands ###

View File

@@ -0,0 +1,32 @@
"""added fee user deals
Revision ID: ef342b38e17b
Revises: 73a00faa4f7f
Create Date: 2025-10-02 15:10:25.456983
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'ef342b38e17b'
down_revision: Union[str, Sequence[str], None] = '73a00faa4f7f'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_deals', sa.Column('fee', sa.Float(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('user_deals', 'fee')
# ### end Alembic commands ###

View File

@@ -0,0 +1,38 @@
"""added last side the conditional data
Revision ID: ef38c90eed55
Revises: d3c85bad8c98
Create Date: 2025-09-30 08:33:23.415545
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'ef38c90eed55'
down_revision: Union[str, Sequence[str], None] = 'd3c85bad8c98'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
op.add_column('user_conditional_settings', sa.Column('last_side', sa.String(), nullable=True))
# Обновляем все существующие строки значением по умолчанию
op.execute(
"UPDATE user_conditional_settings SET last_side = 'default_value' WHERE last_side IS NULL"
)
# Устанавливаем ограничение NOT NULL
op.alter_column('user_conditional_settings', 'last_side', nullable=False)
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('user_conditional_settings', 'last_side')
# ### end Alembic commands ###

View File

@@ -0,0 +1,40 @@
"""Updated Deals
Revision ID: f00a94ccdf01
Revises: c98b9dc36d15
Create Date: 2025-09-28 22:25:00.092196
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'f00a94ccdf01'
down_revision: Union[str, Sequence[str], None] = 'c98b9dc36d15'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user_deals', 'switch_side_mode',
existing_type=sa.VARCHAR(),
type_=sa.Boolean(),
existing_nullable=True)
op.create_unique_constraint(None, 'user_deals', ['user_id'])
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'user_deals', type_='unique')
op.alter_column('user_deals', 'switch_side_mode',
existing_type=sa.Boolean(),
type_=sa.VARCHAR(),
existing_nullable=True)
# ### end Alembic commands ###

View File

@@ -0,0 +1,32 @@
"""Updated leverage
Revision ID: fd8581c0cc87
Revises: bb586fa9bcd2
Create Date: 2025-09-22 15:13:21.487402
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'fd8581c0cc87'
down_revision: Union[str, Sequence[str], None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View File

@@ -280,6 +280,10 @@ async def open_positions(
price_for_cals = trigger_price if po_trigger_price is not None else price_symbol price_for_cals = trigger_price if po_trigger_price is not None else price_symbol
tp_multiplier = 1 + (take_profit_percent / 100)
if commission_fee_percent > 0:
tp_multiplier += commission_fee_percent
if margin_type == "ISOLATED_MARGIN": if margin_type == "ISOLATED_MARGIN":
liq_long, liq_short = await get_liquidation_price( liq_long, liq_short = await get_liquidation_price(
tg_id=tg_id, tg_id=tg_id,
@@ -302,15 +306,21 @@ async def open_positions(
stop_loss_price = None stop_loss_price = None
else: else:
if side == "Buy": if side == "Buy":
take_profit_price = price_for_cals * (1 + take_profit_percent / 100) + commission_fee_percent take_profit_price = price_for_cals * tp_multiplier
stop_loss_price = price_for_cals * (1 - stop_loss_percent / 100) - commission_fee_percent stop_loss_price = price_for_cals * (1 - stop_loss_percent / 100)
else: else:
take_profit_price = price_for_cals * (1 - take_profit_percent / 100) - commission_fee_percent take_profit_price = price_for_cals * (
stop_loss_price = price_for_cals * (1 + stop_loss_percent / 100) + commission_fee_percent 1 - (take_profit_percent / 100) - commission_fee_percent
)
stop_loss_price = trigger_price * (1 + stop_loss_percent / 100)
take_profit_price = max(take_profit_price, 0) take_profit_price = max(take_profit_price, 0)
stop_loss_price = max(stop_loss_price, 0) stop_loss_price = max(stop_loss_price, 0)
logger.info("Take profit price: %s", take_profit_price)
logger.info("Stop loss price: %s", stop_loss_price)
logger.info("Commission fee percent: %s", commission_fee_percent)
# Place order # Place order
order_params = { order_params = {
"category": "linear", "category": "linear",

View File

@@ -21,20 +21,16 @@ async def user_profile_bybit(tg_id: int, message: Message, state: FSMContext) ->
if wallet: if wallet:
balance = wallet.get("totalWalletBalance", "0") balance = wallet.get("totalWalletBalance", "0")
symbol = await rq.get_user_symbol(tg_id=tg_id) symbol = await rq.get_user_symbol(tg_id=tg_id)
if symbol is None: await message.answer(
await rq.set_user_symbol(tg_id=tg_id, symbol="BTCUSDT") text=f"💎Ваш профиль:\n\n"
await user_profile_bybit(tg_id=tg_id, message=message, state=state) f"⚖️ Баланс: {float(balance):,.2f} USD\n"
else: f"📊Торговая пара: {symbol}\n\n"
await message.answer( f"Краткая инструкция:\n"
text=f"💎Ваш профиль:\n\n" f"1. Укажите торговую пару (например: BTCUSDT).\n"
f"⚖️ Баланс: {float(balance):,.2f} USD\n" f"2. В настройках выставьте все необходимые параметры.\n"
f"📊Торговая пара: {symbol}\n\n" f"3. Нажмите кнопку 'Начать торговлю'.\n",
f"Краткая инструкция:\n" reply_markup=kbi.main_menu,
f"1. Укажите торговую пару (например: BTCUSDT).\n" )
f"2. В настройках выставьте все необходимые параметры.\n"
f"3. Нажмите кнопку 'Начать торговлю'.\n",
reply_markup=kbi.main_menu,
)
else: else:
await message.answer( await message.answer(
text="Ошибка при подключении, повторите попытку", text="Ошибка при подключении, повторите попытку",

View File

@@ -155,7 +155,7 @@ class TelegramMessageHandler:
tg_id=tg_id, symbol=symbol, fee=0 tg_id=tg_id, symbol=symbol, fee=0
) )
await rq.set_order_quantity( await rq.set_order_quantity(
tg_id=tg_id, order_quantity=base_quantity tg_id=message.from_user.id, order_quantity=base_quantity
) )
else: else:
open_order_text = "\n❗️ Сделка закрылась в минус, открываю новую сделку с увеличенной ставкой.\n" open_order_text = "\n❗️ Сделка закрылась в минус, открываю новую сделку с увеличенной ставкой.\n"

View File

@@ -162,9 +162,11 @@ async def conditions(callback_query: CallbackQuery, state: FSMContext) -> None:
) )
if conditional_settings_data: if conditional_settings_data:
start_timer = conditional_settings_data.timer_start or 0 start_timer = conditional_settings_data.timer_start or 0
stop_timer = conditional_settings_data.timer_end or 0
await callback_query.message.edit_text( await callback_query.message.edit_text(
text="Условия торговли:\n\n" text="Условия торговли:\n\n"
f"- Таймер для старта: {start_timer} мин.\n", f"- Таймер для старта: {start_timer} мин.\n"
f"- Таймер для остановки: {stop_timer} мин.\n",
reply_markup=kbi.conditions, reply_markup=kbi.conditions,
) )
logger.debug( logger.debug(

View File

@@ -227,6 +227,9 @@ conditions = InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[ [
InlineKeyboardButton(text="Таймер для старта", callback_data="start_timer"), InlineKeyboardButton(text="Таймер для старта", callback_data="start_timer"),
InlineKeyboardButton(
text="Таймер для остановки", callback_data="stop_timer"
),
], ],
[ [
InlineKeyboardButton(text="Назад", callback_data="main_settings"), InlineKeyboardButton(text="Назад", callback_data="main_settings"),

View File

@@ -1,8 +1,31 @@
import os import os
from dotenv import load_dotenv, find_dotenv from dotenv import load_dotenv, find_dotenv
import logging.config
from logger_helper.logger_helper import LOGGING_CONFIG
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("config")
env_path = find_dotenv() env_path = find_dotenv()
if env_path: if env_path:
load_dotenv(env_path) load_dotenv(env_path)
logging.info(f"Loaded env from {env_path}")
else:
logging.warning(".env file not found, environment variables won't be loaded")
BOT_TOKEN = os.getenv("BOT_TOKEN") BOT_TOKEN = os.getenv('BOT_TOKEN')
if not BOT_TOKEN:
logging.error("BOT_TOKEN is not set in environment variables")
DB_USER = os.getenv('DB_USER')
DB_PASS = os.getenv('DB_PASS')
DB_HOST = os.getenv('DB_HOST')
DB_PORT = os.getenv('DB_PORT')
DB_NAME = os.getenv('DB_NAME')
if not all([DB_USER, DB_PASS, DB_HOST, DB_PORT, DB_NAME]):
logger.error("One or more database environment variables are not set")
DATABASE_URL = f"postgresql+asyncpg://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}"

View File

@@ -1,39 +1,18 @@
from database.models import Base, User, UserAdditionalSettings, UserApi, UserConditionalSettings, UserDeals, \
UserRiskManagement, UserSymbol
import logging.config import logging.config
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from sqlalchemy import event from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine, AsyncSession
from pathlib import Path
from database.models import Base
from config import DATABASE_URL
from logger_helper.logger_helper import LOGGING_CONFIG from logger_helper.logger_helper import LOGGING_CONFIG
logging.config.dictConfig(LOGGING_CONFIG) logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("database") logger = logging.getLogger("database")
BASE_DIR = Path(__file__).parent.resolve() async_engine = create_async_engine(DATABASE_URL, echo=False)
DATA_DIR = BASE_DIR / "db"
DATA_DIR.mkdir(parents=True, exist_ok=True)
DATABASE_URL = f"sqlite+aiosqlite:///{DATA_DIR / 'stcs.db'}" async_session = async_sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False)
async_engine = create_async_engine(
DATABASE_URL,
echo=False,
connect_args={"check_same_thread": False}
)
@event.listens_for(async_engine.sync_engine, "connect")
def _enable_foreign_keys(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
async_session = async_sessionmaker(
async_engine,
class_=AsyncSession,
expire_on_commit=False
)
async def init_db(): async def init_db():
@@ -42,4 +21,4 @@ async def init_db():
await conn.run_sync(Base.metadata.create_all) await conn.run_sync(Base.metadata.create_all)
logger.info("Database initialized.") logger.info("Database initialized.")
except Exception as e: except Exception as e:
logger.error("Database initialization failed: %s", e) logger.error("Database initialization failed: %s", e, exc_info=True)

View File

@@ -1,6 +1,6 @@
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.asyncio import AsyncAttrs from sqlalchemy.ext.asyncio import AsyncAttrs
from sqlalchemy import Column, ForeignKey, Integer, String, Float, Boolean, UniqueConstraint from sqlalchemy import Column, ForeignKey, Integer, String, BigInteger, Float, Boolean, UniqueConstraint
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
Base = declarative_base(cls=AsyncAttrs) Base = declarative_base(cls=AsyncAttrs)
@@ -11,7 +11,7 @@ class User(Base):
__tablename__ = "users" __tablename__ = "users"
id = Column(Integer, primary_key=True, autoincrement=True) id = Column(Integer, primary_key=True, autoincrement=True)
tg_id = Column(Integer, nullable=False, unique=True) tg_id = Column(BigInteger, nullable=False, unique=True)
username = Column(String, nullable=False) username = Column(String, nullable=False)
user_api = relationship("UserApi", user_api = relationship("UserApi",
@@ -175,4 +175,4 @@ class UserAutoTrading(Base):
fee = Column(Float, nullable=True) fee = Column(Float, nullable=True)
total_fee = Column(Float, nullable=True) total_fee = Column(Float, nullable=True)
user = relationship("User", back_populates="user_auto_trading") user = relationship("User", back_populates="user_auto_trading")

2
run.py
View File

@@ -5,10 +5,10 @@ import logging.config
from aiogram import Bot, Dispatcher from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.redis import RedisStorage from aiogram.fsm.storage.redis import RedisStorage
from database import init_db
from app.bybit.web_socket import WebSocketBot from app.bybit.web_socket import WebSocketBot
from app.telegram.handlers import router from app.telegram.handlers import router
from config import BOT_TOKEN from config import BOT_TOKEN
from database import init_db
from logger_helper.logger_helper import LOGGING_CONFIG from logger_helper.logger_helper import LOGGING_CONFIG
logging.config.dictConfig(LOGGING_CONFIG) logging.config.dictConfig(LOGGING_CONFIG)