From dbbea16c1917c37c64552578fedf273624081872 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Thu, 9 Oct 2025 14:21:21 +0500 Subject: [PATCH 01/43] Added alembic --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 8fb70e2..0ff2590 100644 --- a/.gitignore +++ b/.gitignore @@ -146,9 +146,6 @@ myenv ENV/ env.bak/ venv.bak/ -/alembic/versions -/alembic -alembic.ini # Spyder project settings .spyderproject .spyproject From 2136de5d691a2dd5d20eaa60a4c5e083ccebe168 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Thu, 9 Oct 2025 14:21:54 +0500 Subject: [PATCH 02/43] Added alembic migrations --- alembic.ini | 147 ++++++++++++++++++ alembic/README | 1 + alembic/env.py | 90 +++++++++++ alembic/script.py.mako | 28 ++++ .../09db71875980_updated_user_deals_table.py | 36 +++++ ...ed68eddcdb_added_conditional_order_type.py | 40 +++++ ...73c71f9_added_fee_for_user_auto_trading.py | 34 ++++ ...b49ecd_added_side_for_user_auto_trading.py | 34 ++++ ...c_update_last_side_the_conditional_data.py | 32 ++++ .../42c66cfe8d4e_updated_martingale_factor.py | 38 +++++ .../45977e9d8558_updated_order_quantity.py | 38 +++++ ...ba27e80_create_user_conditional_setting.py | 40 +++++ ...00faa4f7f_added_user_auto_trading_table.py | 42 +++++ ...7c_deleted_position_idx_for_user_deals_.py | 32 ++++ .../versions/863d6215e1eb_updated_deals.py | 32 ++++ ...added_position_idx_for_user_deals_table.py | 34 ++++ ...pdated_user_deals_and_user_conditional_.py | 36 +++++ .../acbcc95de48d_updated_userdeals.py | 60 +++++++ ...2259c_unnecessary_data_has_been_deleted.py | 79 ++++++++++ .../versions/c98b9dc36d15_fixed_auto_trade.py | 38 +++++ .../versions/ccdc5764eb4f_added_userdeals.py | 50 ++++++ ...added_limit_and_trigger_price_for_user_.py | 34 ++++ ...c_added_last_side_and_auto_trading_for_.py | 40 +++++ .../ef342b38e17b_added_fee_user_deals.py | 32 ++++ ...55_added_last_side_the_conditional_data.py | 38 +++++ .../versions/f00a94ccdf01_updated_deals.py | 40 +++++ .../versions/fd8581c0cc87_updated_leverage.py | 32 ++++ 27 files changed, 1177 insertions(+) create mode 100644 alembic.ini create mode 100644 alembic/README create mode 100644 alembic/env.py create mode 100644 alembic/script.py.mako create mode 100644 alembic/versions/09db71875980_updated_user_deals_table.py create mode 100644 alembic/versions/0eed68eddcdb_added_conditional_order_type.py create mode 100644 alembic/versions/10bf073c71f9_added_fee_for_user_auto_trading.py create mode 100644 alembic/versions/2b9572b49ecd_added_side_for_user_auto_trading.py create mode 100644 alembic/versions/3534adf891fc_update_last_side_the_conditional_data.py create mode 100644 alembic/versions/42c66cfe8d4e_updated_martingale_factor.py create mode 100644 alembic/versions/45977e9d8558_updated_order_quantity.py create mode 100644 alembic/versions/70094ba27e80_create_user_conditional_setting.py create mode 100644 alembic/versions/73a00faa4f7f_added_user_auto_trading_table.py create mode 100644 alembic/versions/77197715747c_deleted_position_idx_for_user_deals_.py create mode 100644 alembic/versions/863d6215e1eb_updated_deals.py create mode 100644 alembic/versions/8f1476c68efa_added_position_idx_for_user_deals_table.py create mode 100644 alembic/versions/968f8121104f_updated_user_deals_and_user_conditional_.py create mode 100644 alembic/versions/acbcc95de48d_updated_userdeals.py create mode 100644 alembic/versions/c710f4e2259c_unnecessary_data_has_been_deleted.py create mode 100644 alembic/versions/c98b9dc36d15_fixed_auto_trade.py create mode 100644 alembic/versions/ccdc5764eb4f_added_userdeals.py create mode 100644 alembic/versions/d3c85bad8c98_added_limit_and_trigger_price_for_user_.py create mode 100644 alembic/versions/dbffe818030c_added_last_side_and_auto_trading_for_.py create mode 100644 alembic/versions/ef342b38e17b_added_fee_user_deals.py create mode 100644 alembic/versions/ef38c90eed55_added_last_side_the_conditional_data.py create mode 100644 alembic/versions/f00a94ccdf01_updated_deals.py create mode 100644 alembic/versions/fd8581c0cc87_updated_leverage.py diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..0791816 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,147 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# this is typically a path given in POSIX (e.g. forward slashes) +# format, relative to the token %(here)s which refers to the location of this +# ini file +script_location = %(here)s/alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. for multiple paths, the path separator +# is defined by "path_separator" below. +prepend_sys_path = . + + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to /versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "path_separator" +# below. +# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions + +# path_separator; This indicates what character is used to split lists of file +# paths, including version_locations and prepend_sys_path within configparser +# files such as alembic.ini. +# The default rendered in new alembic.ini files is "os", which uses os.pathsep +# to provide os-dependent path splitting. +# +# Note that in order to support legacy alembic.ini files, this default does NOT +# take place if path_separator is not present in alembic.ini. If this +# option is omitted entirely, fallback logic is as follows: +# +# 1. Parsing of the version_locations option falls back to using the legacy +# "version_path_separator" key, which if absent then falls back to the legacy +# behavior of splitting on spaces and/or commas. +# 2. Parsing of the prepend_sys_path option falls back to the legacy +# behavior of splitting on spaces, commas, or colons. +# +# Valid values for path_separator are: +# +# path_separator = : +# path_separator = ; +# path_separator = space +# path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# 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 +# file. +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module +# hooks = ruff +# ruff.type = module +# ruff.module = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Alternatively, use the exec runner to execute a binary found on your PATH +# hooks = ruff +# ruff.type = exec +# ruff.executable = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration. This is also consumed by the user-maintained +# env.py script only. +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/alembic/README b/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/alembic/env.py b/alembic/env.py new file mode 100644 index 0000000..cadad9c --- /dev/null +++ b/alembic/env.py @@ -0,0 +1,90 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +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.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: + 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 +target_metadata = Base.metadata + +# 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( + url=DATABASE_URL, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_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( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/alembic/script.py.mako b/alembic/script.py.mako new file mode 100644 index 0000000..1101630 --- /dev/null +++ b/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/alembic/versions/09db71875980_updated_user_deals_table.py b/alembic/versions/09db71875980_updated_user_deals_table.py new file mode 100644 index 0000000..017638f --- /dev/null +++ b/alembic/versions/09db71875980_updated_user_deals_table.py @@ -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 ### diff --git a/alembic/versions/0eed68eddcdb_added_conditional_order_type.py b/alembic/versions/0eed68eddcdb_added_conditional_order_type.py new file mode 100644 index 0000000..0353cd1 --- /dev/null +++ b/alembic/versions/0eed68eddcdb_added_conditional_order_type.py @@ -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 ### diff --git a/alembic/versions/10bf073c71f9_added_fee_for_user_auto_trading.py b/alembic/versions/10bf073c71f9_added_fee_for_user_auto_trading.py new file mode 100644 index 0000000..b5e559a --- /dev/null +++ b/alembic/versions/10bf073c71f9_added_fee_for_user_auto_trading.py @@ -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 ### diff --git a/alembic/versions/2b9572b49ecd_added_side_for_user_auto_trading.py b/alembic/versions/2b9572b49ecd_added_side_for_user_auto_trading.py new file mode 100644 index 0000000..cfaef08 --- /dev/null +++ b/alembic/versions/2b9572b49ecd_added_side_for_user_auto_trading.py @@ -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 ### diff --git a/alembic/versions/3534adf891fc_update_last_side_the_conditional_data.py b/alembic/versions/3534adf891fc_update_last_side_the_conditional_data.py new file mode 100644 index 0000000..5bbc3f8 --- /dev/null +++ b/alembic/versions/3534adf891fc_update_last_side_the_conditional_data.py @@ -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 ### diff --git a/alembic/versions/42c66cfe8d4e_updated_martingale_factor.py b/alembic/versions/42c66cfe8d4e_updated_martingale_factor.py new file mode 100644 index 0000000..30b6920 --- /dev/null +++ b/alembic/versions/42c66cfe8d4e_updated_martingale_factor.py @@ -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 ### diff --git a/alembic/versions/45977e9d8558_updated_order_quantity.py b/alembic/versions/45977e9d8558_updated_order_quantity.py new file mode 100644 index 0000000..0fa2f58 --- /dev/null +++ b/alembic/versions/45977e9d8558_updated_order_quantity.py @@ -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 ### diff --git a/alembic/versions/70094ba27e80_create_user_conditional_setting.py b/alembic/versions/70094ba27e80_create_user_conditional_setting.py new file mode 100644 index 0000000..d09e8b0 --- /dev/null +++ b/alembic/versions/70094ba27e80_create_user_conditional_setting.py @@ -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 ### diff --git a/alembic/versions/73a00faa4f7f_added_user_auto_trading_table.py b/alembic/versions/73a00faa4f7f_added_user_auto_trading_table.py new file mode 100644 index 0000000..9e0c743 --- /dev/null +++ b/alembic/versions/73a00faa4f7f_added_user_auto_trading_table.py @@ -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 ### diff --git a/alembic/versions/77197715747c_deleted_position_idx_for_user_deals_.py b/alembic/versions/77197715747c_deleted_position_idx_for_user_deals_.py new file mode 100644 index 0000000..0f4d2a0 --- /dev/null +++ b/alembic/versions/77197715747c_deleted_position_idx_for_user_deals_.py @@ -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 ### diff --git a/alembic/versions/863d6215e1eb_updated_deals.py b/alembic/versions/863d6215e1eb_updated_deals.py new file mode 100644 index 0000000..6863b0b --- /dev/null +++ b/alembic/versions/863d6215e1eb_updated_deals.py @@ -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 ### diff --git a/alembic/versions/8f1476c68efa_added_position_idx_for_user_deals_table.py b/alembic/versions/8f1476c68efa_added_position_idx_for_user_deals_table.py new file mode 100644 index 0000000..2c51fb0 --- /dev/null +++ b/alembic/versions/8f1476c68efa_added_position_idx_for_user_deals_table.py @@ -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 ### diff --git a/alembic/versions/968f8121104f_updated_user_deals_and_user_conditional_.py b/alembic/versions/968f8121104f_updated_user_deals_and_user_conditional_.py new file mode 100644 index 0000000..77b4490 --- /dev/null +++ b/alembic/versions/968f8121104f_updated_user_deals_and_user_conditional_.py @@ -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 ### diff --git a/alembic/versions/acbcc95de48d_updated_userdeals.py b/alembic/versions/acbcc95de48d_updated_userdeals.py new file mode 100644 index 0000000..1822547 --- /dev/null +++ b/alembic/versions/acbcc95de48d_updated_userdeals.py @@ -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 ### diff --git a/alembic/versions/c710f4e2259c_unnecessary_data_has_been_deleted.py b/alembic/versions/c710f4e2259c_unnecessary_data_has_been_deleted.py new file mode 100644 index 0000000..455e1ba --- /dev/null +++ b/alembic/versions/c710f4e2259c_unnecessary_data_has_been_deleted.py @@ -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 ### diff --git a/alembic/versions/c98b9dc36d15_fixed_auto_trade.py b/alembic/versions/c98b9dc36d15_fixed_auto_trade.py new file mode 100644 index 0000000..5bd00be --- /dev/null +++ b/alembic/versions/c98b9dc36d15_fixed_auto_trade.py @@ -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 ### diff --git a/alembic/versions/ccdc5764eb4f_added_userdeals.py b/alembic/versions/ccdc5764eb4f_added_userdeals.py new file mode 100644 index 0000000..4d697c3 --- /dev/null +++ b/alembic/versions/ccdc5764eb4f_added_userdeals.py @@ -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 ### diff --git a/alembic/versions/d3c85bad8c98_added_limit_and_trigger_price_for_user_.py b/alembic/versions/d3c85bad8c98_added_limit_and_trigger_price_for_user_.py new file mode 100644 index 0000000..c2fc35a --- /dev/null +++ b/alembic/versions/d3c85bad8c98_added_limit_and_trigger_price_for_user_.py @@ -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 ### diff --git a/alembic/versions/dbffe818030c_added_last_side_and_auto_trading_for_.py b/alembic/versions/dbffe818030c_added_last_side_and_auto_trading_for_.py new file mode 100644 index 0000000..eec8b99 --- /dev/null +++ b/alembic/versions/dbffe818030c_added_last_side_and_auto_trading_for_.py @@ -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 ### diff --git a/alembic/versions/ef342b38e17b_added_fee_user_deals.py b/alembic/versions/ef342b38e17b_added_fee_user_deals.py new file mode 100644 index 0000000..b9e3983 --- /dev/null +++ b/alembic/versions/ef342b38e17b_added_fee_user_deals.py @@ -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 ### diff --git a/alembic/versions/ef38c90eed55_added_last_side_the_conditional_data.py b/alembic/versions/ef38c90eed55_added_last_side_the_conditional_data.py new file mode 100644 index 0000000..39eff8c --- /dev/null +++ b/alembic/versions/ef38c90eed55_added_last_side_the_conditional_data.py @@ -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 ### diff --git a/alembic/versions/f00a94ccdf01_updated_deals.py b/alembic/versions/f00a94ccdf01_updated_deals.py new file mode 100644 index 0000000..9e0309b --- /dev/null +++ b/alembic/versions/f00a94ccdf01_updated_deals.py @@ -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 ### diff --git a/alembic/versions/fd8581c0cc87_updated_leverage.py b/alembic/versions/fd8581c0cc87_updated_leverage.py new file mode 100644 index 0000000..cc9bb37 --- /dev/null +++ b/alembic/versions/fd8581c0cc87_updated_leverage.py @@ -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 ### From a0a2fd30f0d3d452321ce448978ba88c9dc15bf1 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:14:59 +0500 Subject: [PATCH 03/43] TP AND SL have been converted to float. Switch control has been moved to the main settings, Removed unnecessary parameters --- database/models.py | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/database/models.py b/database/models.py index af5e5cd..df8845e 100644 --- a/database/models.py +++ b/database/models.py @@ -91,14 +91,10 @@ class UserAdditionalSettings(Base): ForeignKey("users.id", ondelete="CASCADE"), nullable=False, unique=True) trade_mode = Column(String, nullable=False, default="Merged_Single") - order_type = Column(String, nullable=False, default="Market") - conditional_order_type = Column(String, nullable=False, default="Market") - limit_price = Column(Float, nullable=False, default=0.0) + switch_side = Column(String, nullable=False, default="По направлению") trigger_price = Column(Float, nullable=False, default=0.0) margin_type = Column(String, nullable=False, default="ISOLATED_MARGIN") leverage = Column(String, nullable=False, default="10") - leverage_to_buy = Column(String, nullable=False, default="10") - leverage_to_sell = Column(String, nullable=False, default="10") order_quantity = Column(Float, nullable=False, default=5.0) martingale_factor = Column(Float, nullable=False, default=1.0) max_bets_in_series = Column(Integer, nullable=False, default=1) @@ -114,9 +110,8 @@ class UserRiskManagement(Base): user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, unique=True) - take_profit_percent = Column(Integer, nullable=False, default=1) - stop_loss_percent = Column(Integer, nullable=False, default=1) - max_risk_percent = Column(Integer, nullable=False, default=100) + take_profit_percent = Column(Float, nullable=False, default=1) + stop_loss_percent = Column(Float, nullable=False, default=1) commission_fee = Column(String, nullable=False, default="Yes_commission_fee") user = relationship("User", back_populates="user_risk_management") @@ -148,13 +143,9 @@ class UserDeals(Base): current_step = Column(Integer, nullable=True) symbol = Column(String, nullable=True) trade_mode = Column(String, nullable=True) - trading_type = Column(String, nullable=True) + base_quantity = Column(Float, nullable=True) margin_type = Column(String, nullable=True) - order_type = Column(String, nullable=True) - conditional_order_type = Column(String, nullable=True) leverage = Column(String, nullable=True) - leverage_to_buy = Column(String, nullable=True) - leverage_to_sell = Column(String, nullable=True) last_side = Column(String, nullable=True) closed_side = Column(String, nullable=True) order_quantity = Column(Float, nullable=True) @@ -162,9 +153,6 @@ class UserDeals(Base): max_bets_in_series = Column(Integer, nullable=True) take_profit_percent = Column(Integer, nullable=True) stop_loss_percent = Column(Integer, nullable=True) - max_risk_percent = Column(Integer, nullable=True) - switch_side_mode = Column(Boolean, nullable=True) - limit_price = Column(Float, nullable=True) trigger_price = Column(Float, nullable=True) user = relationship("User", back_populates="user_deals") @@ -184,7 +172,7 @@ class UserAutoTrading(Base): nullable=False) symbol = Column(String, nullable=True) auto_trading = Column(Boolean, nullable=True) - side = Column(String, nullable=True) fee = Column(Float, nullable=True) + total_fee = Column(Float, nullable=True) user = relationship("User", back_populates="user_auto_trading") \ No newline at end of file From 09606a057b1ba315d527404acb62ddb1c1082348 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:16:00 +0500 Subject: [PATCH 04/43] Added the addition of a common commission --- database/request.py | 353 ++++++++++++-------------------------------- 1 file changed, 95 insertions(+), 258 deletions(-) diff --git a/database/request.py b/database/request.py index 78c401f..6492cea 100644 --- a/database/request.py +++ b/database/request.py @@ -198,15 +198,12 @@ async def create_user_additional_settings(tg_id: int) -> None: # Create the user additional settings user_additional_settings = UserAdditionalSettings( user=user, - trade_mode="Merged_Single", # Default value - order_type="Market", - conditional_order_type="Market", + trade_mode="Long", # Default value + switch_side="По направлению", margin_type="ISOLATED_MARGIN", leverage="10", - leverage_to_buy="10", - leverage_to_sell="10", - order_quantity=5.0, - martingale_factor=1.0, + order_quantity=1.0, + martingale_factor=2.0, max_bets_in_series=10, ) session.add(user_additional_settings) @@ -283,90 +280,6 @@ async def set_trade_mode(tg_id: int, trade_mode: str) -> bool: return False -async def set_order_type(tg_id: int, order_type: str) -> bool: - """ - Set order type for a user in the database. - :param tg_id: Telegram user ID - :param order_type: "Market" or "Limit" - :return: True if successful, False otherwise - """ - try: - async with async_session() as session: - result = await session.execute( - select(User) - .options(joinedload(User.user_additional_settings)) - .filter_by(tg_id=tg_id) - ) - user = result.scalars().first() - - if user: - if user.user_additional_settings: - # Updating existing record - user.user_additional_settings.order_type = order_type - else: - # Creating new record - user_additional_settings = UserAdditionalSettings( - order_type=order_type, - user=user, - ) - session.add(user_additional_settings) - - await session.commit() - logger.info("User order type updated for user: %s", tg_id) - return True - else: - logger.error("User not found with tg_id: %s", tg_id) - return False - except Exception as e: - logger.error("Error adding/updating user order type for user %s: %s", tg_id, e) - return False - - -async def set_conditional_order_type(tg_id: int, conditional_order_type: str) -> bool: - """ - Set conditional order type for a user in the database. - :param tg_id: Telegram user ID - :param conditional_order_type: "Market" or "Limit" - :return: True if successful, False otherwise - """ - try: - async with async_session() as session: - result = await session.execute( - select(User) - .options(joinedload(User.user_additional_settings)) - .filter_by(tg_id=tg_id) - ) - user = result.scalars().first() - - if user: - if user.user_additional_settings: - # Updating existing record - user.user_additional_settings.conditional_order_type = ( - conditional_order_type - ) - else: - # Creating new record - user_additional_settings = UserAdditionalSettings( - conditional_order_type=conditional_order_type, - user=user, - ) - session.add(user_additional_settings) - - await session.commit() - logger.info("User conditional order type updated for user: %s", tg_id) - return True - else: - logger.error("User not found with tg_id: %s", tg_id) - return False - except Exception as e: - logger.error( - "Error adding/updating user conditional order type for user %s: %s", - tg_id, - e, - ) - return False - - async def set_margin_type(tg_id: int, margin_type: str) -> bool: """ Set margin type for a user in the database. @@ -406,6 +319,45 @@ async def set_margin_type(tg_id: int, margin_type: str) -> bool: return False +async def set_switch_side(tg_id: int, switch_side: str) -> bool: + """ + Set switch side for a user in the database. + :param tg_id: Telegram user ID + :param switch_side: "По направлению" or "По цене" + :return: True if successful, False otherwise + """ + try: + async with async_session() as session: + result = await session.execute( + select(User) + .options(joinedload(User.user_additional_settings)) + .filter_by(tg_id=tg_id) + ) + user = result.scalars().first() + + if user: + if user.user_additional_settings: + # Updating existing record + user.user_additional_settings.switch_side = switch_side + else: + # Creating new record + user_additional_settings = UserAdditionalSettings( + switch_side=switch_side, + user=user, + ) + session.add(user_additional_settings) + + await session.commit() + logger.info("User switch side updated for user: %s", tg_id) + return True + else: + logger.error("User not found with tg_id: %s", tg_id) + return False + except Exception as e: + logger.error("Error adding/updating user switch side for user %s: %s", tg_id, e) + return False + + async def set_leverage(tg_id: int, leverage: str) -> bool: """ Set leverage for a user in the database. @@ -445,50 +397,6 @@ async def set_leverage(tg_id: int, leverage: str) -> bool: return False -async def set_leverage_to_buy_and_sell( - tg_id: int, leverage_to_buy: str, leverage_to_sell: str -) -> bool: - """ - Set leverage for a user in the database. - :param tg_id: Telegram user ID - :param leverage_to_buy: Leverage to buy - :param leverage_to_sell: Leverage to sell - :return: True if successful, False otherwise - """ - try: - async with async_session() as session: - result = await session.execute( - select(User) - .options(joinedload(User.user_additional_settings)) - .filter_by(tg_id=tg_id) - ) - user = result.scalars().first() - - if user: - if user.user_additional_settings: - # Updating existing record - user.user_additional_settings.leverage_to_buy = leverage_to_buy - user.user_additional_settings.leverage_to_sell = leverage_to_sell - else: - # Creating new record - user_additional_settings = UserAdditionalSettings( - leverage_to_buy=leverage_to_buy, - leverage_to_sell=leverage_to_sell, - user=user, - ) - session.add(user_additional_settings) - - await session.commit() - logger.info("User leverage updated for user: %s", tg_id) - return True - else: - logger.error("User not found with tg_id: %s", tg_id) - return False - except Exception as e: - logger.error("Error adding/updating user leverage for user %s: %s", tg_id, e) - return False - - async def set_order_quantity(tg_id: int, order_quantity: float) -> bool: """ Set order quantity for a user in the database. @@ -614,45 +522,6 @@ async def set_max_bets_in_series(tg_id: int, max_bets_in_series: int) -> bool: return False -async def set_limit_price(tg_id: int, limit_price: float) -> bool: - """ - Set limit price for a user in the database. - :param tg_id: - :param limit_price: - :return: bool - """ - try: - async with async_session() as session: - result = await session.execute( - select(User) - .options(joinedload(User.user_additional_settings)) - .filter_by(tg_id=tg_id) - ) - user = result.scalars().first() - - if user: - if user.user_additional_settings: - # Updating existing record - user.user_additional_settings.limit_price = limit_price - else: - # Creating new record - user_additional_settings = UserAdditionalSettings( - limit_price=limit_price, - user=user, - ) - session.add(user_additional_settings) - - await session.commit() - logger.info("User limit price updated for user: %s", tg_id) - return True - else: - logger.error("User not found with tg_id: %s", tg_id) - return False - except Exception as e: - logger.error("Error adding/updating user limit price for user %s: %s", tg_id, e) - return False - - async def set_trigger_price(tg_id: int, trigger_price: float) -> bool: """ Set trigger price for a user in the database. @@ -718,9 +587,8 @@ async def create_user_risk_management(tg_id: int) -> None: # Create the user risk management user_risk_management = UserRiskManagement( user=user, - take_profit_percent=1, - stop_loss_percent=1, - max_risk_percent=100, + take_profit_percent=1.0, + stop_loss_percent=1.0, commission_fee="Yes_commission_fee", ) session.add(user_risk_management) @@ -758,7 +626,7 @@ async def get_user_risk_management(tg_id: int): return None -async def set_take_profit_percent(tg_id: int, take_profit_percent: int) -> bool: +async def set_take_profit_percent(tg_id: int, take_profit_percent: float) -> bool: """ Set take profit percent for a user in the database. :param tg_id: Telegram user ID @@ -799,7 +667,7 @@ async def set_take_profit_percent(tg_id: int, take_profit_percent: int) -> bool: return False -async def set_stop_loss_percent(tg_id: int, stop_loss_percent: int) -> bool: +async def set_stop_loss_percent(tg_id: int, stop_loss_percent: float) -> bool: """ Set stop loss percent for a user in the database. :param tg_id: Telegram user ID @@ -840,47 +708,6 @@ async def set_stop_loss_percent(tg_id: int, stop_loss_percent: int) -> bool: return False -async def set_max_risk_percent(tg_id: int, max_risk_percent: int) -> bool: - """ - Set max risk percent for a user in the database. - :param tg_id: Telegram user ID - :param max_risk_percent: Max risk percent - :return: True if successful, False otherwise - """ - try: - async with async_session() as session: - result = await session.execute( - select(User) - .options(joinedload(User.user_risk_management)) - .filter_by(tg_id=tg_id) - ) - user = result.scalars().first() - - if user: - if user.user_risk_management: - # Updating existing record - user.user_risk_management.max_risk_percent = max_risk_percent - else: - # Creating new record - user_risk_management = UserRiskManagement( - max_risk_percent=max_risk_percent, - user=user, - ) - session.add(user_risk_management) - - await session.commit() - logger.info("User max risk percent updated for user: %s", tg_id) - return True - else: - logger.error("User not found with tg_id: %s", tg_id) - return False - except Exception as e: - logger.error( - "Error adding/updating user max risk percent for user %s: %s", tg_id, e - ) - return False - - async def set_commission_fee(tg_id: int, commission_fee: str) -> bool: """ Set commission fee for a user in the database. @@ -1073,19 +900,13 @@ async def set_user_deal( trade_mode: str, margin_type: str, leverage: str, - leverage_to_buy: str, - leverage_to_sell: str, - order_type: str, - conditional_order_type: str, order_quantity: float, - limit_price: float, trigger_price: float, martingale_factor: float, max_bets_in_series: int, take_profit_percent: int, stop_loss_percent: int, - max_risk_percent: int, - switch_side_mode: bool, + base_quantity: float ): """ Set the user deal in the database. @@ -1096,19 +917,13 @@ async def set_user_deal( :param trade_mode: Trade mode :param margin_type: Margin type :param leverage: Leverage - :param leverage_to_buy: Leverage to buy - :param leverage_to_sell: Leverage to sell - :param order_type: Order type - :param conditional_order_type: Conditional order type :param order_quantity: Order quantity - :param limit_price: Limit price :param trigger_price: Trigger price :param martingale_factor: Martingale factor :param max_bets_in_series: Max bets in series :param take_profit_percent: Take profit percent :param stop_loss_percent: Stop loss percent - :param max_risk_percent: Max risk percent - :param switch_side_mode: Switch side mode + :param base_quantity: Base quantity :return: bool """ try: @@ -1131,19 +946,13 @@ async def set_user_deal( deal.trade_mode = trade_mode deal.margin_type = margin_type deal.leverage = leverage - deal.leverage_to_buy = leverage_to_buy - deal.leverage_to_sell = leverage_to_sell - deal.order_type = order_type - deal.conditional_order_type = conditional_order_type deal.order_quantity = order_quantity - deal.limit_price = limit_price deal.trigger_price = trigger_price deal.martingale_factor = martingale_factor deal.max_bets_in_series = max_bets_in_series deal.take_profit_percent = take_profit_percent deal.stop_loss_percent = stop_loss_percent - deal.max_risk_percent = max_risk_percent - deal.switch_side_mode = switch_side_mode + deal.base_quantity = base_quantity else: # Creating new record new_deal = UserDeals( @@ -1154,19 +963,13 @@ async def set_user_deal( trade_mode=trade_mode, margin_type=margin_type, leverage=leverage, - leverage_to_buy=leverage_to_buy, - leverage_to_sell=leverage_to_sell, - order_type=order_type, - conditional_order_type=conditional_order_type, order_quantity=order_quantity, - limit_price=limit_price, trigger_price=trigger_price, martingale_factor=martingale_factor, max_bets_in_series=max_bets_in_series, take_profit_percent=take_profit_percent, stop_loss_percent=stop_loss_percent, - max_risk_percent=max_risk_percent, - switch_side_mode=switch_side_mode, + base_quantity=base_quantity ) session.add(new_deal) @@ -1268,7 +1071,7 @@ async def get_all_user_auto_trading(tg_id: int): return [] -async def get_user_auto_trading(tg_id: int, symbol: str, side: str): +async def get_user_auto_trading(tg_id: int, symbol: str): """Get user auto trading from the database asynchronously.""" try: async with async_session() as session: @@ -1278,7 +1081,7 @@ async def get_user_auto_trading(tg_id: int, symbol: str, side: str): return None result_auto_trading = await session.execute( - select(UserAutoTrading).filter_by(user_id=user.id, symbol=symbol, side=side) + select(UserAutoTrading).filter_by(user_id=user.id, symbol=symbol) ) auto_trading = result_auto_trading.scalars().first() return auto_trading @@ -1287,13 +1090,12 @@ async def get_user_auto_trading(tg_id: int, symbol: str, side: str): return None -async def set_auto_trading(tg_id: int, symbol: str, auto_trading: bool, side: str) -> bool: +async def set_auto_trading(tg_id: int, symbol: str, auto_trading: bool) -> bool: """ Set the auto trading for a user in the database. :param tg_id: Telegram user ID :param symbol: Symbol :param auto_trading: Auto trading - :param side: Side :return: bool """ try: @@ -1305,7 +1107,7 @@ async def set_auto_trading(tg_id: int, symbol: str, auto_trading: bool, side: st return False result = await session.execute( - select(UserAutoTrading).filter_by(user_id=user.id, symbol=symbol, side=side) + select(UserAutoTrading).filter_by(user_id=user.id, symbol=symbol) ) record = result.scalars().first() if record: @@ -1315,7 +1117,6 @@ async def set_auto_trading(tg_id: int, symbol: str, auto_trading: bool, side: st user_id=user.id, symbol=symbol, auto_trading=auto_trading, - side=side ) session.add(new_record) await session.commit() @@ -1326,12 +1127,11 @@ async def set_auto_trading(tg_id: int, symbol: str, auto_trading: bool, side: st return False -async def set_fee_user_auto_trading(tg_id: int, symbol: str, side: str, fee: float) -> bool: +async def set_fee_user_auto_trading(tg_id: int, symbol: str, fee: float) -> bool: """ Set the fee for a user auto trading in the database. :param tg_id: :param symbol: - :param side: :param fee: :return: """ @@ -1344,7 +1144,7 @@ async def set_fee_user_auto_trading(tg_id: int, symbol: str, side: str, fee: flo return False result = await session.execute( - select(UserAutoTrading).filter_by(user_id=user.id, symbol=symbol, side=side) + select(UserAutoTrading).filter_by(user_id=user.id, symbol=symbol) ) record = result.scalars().first() @@ -1355,7 +1155,6 @@ async def set_fee_user_auto_trading(tg_id: int, symbol: str, side: str, fee: flo user_id=user.id, symbol=symbol, fee=fee, - side=side ) session.add(user_fee) await session.commit() @@ -1364,3 +1163,41 @@ async def set_fee_user_auto_trading(tg_id: int, symbol: str, side: str, fee: flo except Exception as e: logger.error("Error setting user auto trading fee for user %s and symbol %s: %s", tg_id, symbol, e) return False + + +async def set_total_fee_user_auto_trading(tg_id: int, symbol: str, total_fee: float) -> bool: + """ + Set the total fee for a user auto trading in the database. + :param tg_id: Telegram user ID + :param symbol: Symbol + :param total_fee: Total fee + :return: bool + """ + try: + async with async_session() as session: + result = await session.execute(select(User).filter_by(tg_id=tg_id)) + user = result.scalars().first() + if user is None: + logger.error(f"User with tg_id={tg_id} not found") + return False + + result = await session.execute( + select(UserAutoTrading).filter_by(user_id=user.id, symbol=symbol) + ) + record = result.scalars().first() + + if record: + record.total_fee = total_fee + else: + user_total_fee = UserAutoTrading( + user_id=user.id, + symbol=symbol, + total_fee=total_fee, + ) + session.add(user_total_fee) + await session.commit() + logger.info("Set total fee for user %s and symbol %s", tg_id, symbol) + return True + except Exception as e: + logger.error("Error setting user auto trading total fee for user %s and symbol %s: %s", tg_id, symbol, e) + return False From ebe2d58975ec4dfa643e9b9d0b949e5177703bee Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:18:43 +0500 Subject: [PATCH 05/43] The trading mode has been moved to the main settings, Position mode, limit order and conditional order have been removed. The number of bids has been renamed to the base rate. The choice of the direction of the first transaction has been moved to the main settings --- app/telegram/handlers/settings.py | 107 ++-------- app/telegram/handlers/start_trading.py | 279 +------------------------ 2 files changed, 25 insertions(+), 361 deletions(-) diff --git a/app/telegram/handlers/settings.py b/app/telegram/handlers/settings.py index b687e0a..ed9a914 100644 --- a/app/telegram/handlers/settings.py +++ b/app/telegram/handlers/settings.py @@ -7,8 +7,8 @@ from aiogram.types import CallbackQuery import app.telegram.keyboards.inline as kbi import database.request as rq from app.bybit import get_bybit_client -from app.bybit.get_functions.get_tickers import get_tickers -from app.helper_functions import calculate_total_budget, get_base_currency, safe_float + +from app.helper_functions import calculate_total_budget, safe_float from logger_helper.logger_helper import LOGGING_CONFIG logging.config.dictConfig(LOGGING_CONFIG) @@ -40,77 +40,30 @@ async def additional_settings(callback_query: CallbackQuery, state: FSMContext) return trade_mode_map = { - "Merged_Single": "Односторонний режим", - "Both_Sides": "Хеджирование", + "Long": "Лонг", + "Short": "Шорт", + "Switch": "Свитч", } margin_type_map = { "ISOLATED_MARGIN": "Изолированная", "REGULAR_MARGIN": "Кросс", } - order_type_map = {"Market": "Рыночный", "Limit": "Лимитный"} trade_mode = additional_data.trade_mode or "" margin_type = additional_data.margin_type or "" - order_type = additional_data.order_type or "" trade_mode_rus = trade_mode_map.get(trade_mode, trade_mode) margin_type_rus = margin_type_map.get(margin_type, margin_type) - order_type_rus = order_type_map.get(order_type, "Условный") + switch_side = additional_data.switch_side def f(x): return safe_float(x) leverage = f(additional_data.leverage) - leverage_to_buy = f(additional_data.leverage_to_buy) - leverage_to_sell = f(additional_data.leverage_to_sell) martingale = f(additional_data.martingale_factor) max_bets = additional_data.max_bets_in_series quantity = f(additional_data.order_quantity) - limit_price = f(additional_data.limit_price) trigger_price = f(additional_data.trigger_price) or 0 - - tickers = await get_tickers(tg_id=tg_id, symbol=symbol) - price_symbol = safe_float(tickers.get("lastPrice")) or 0 - bid = f(tickers.get("bid1Price")) or 0 - ask = f(tickers.get("ask1Price")) or 0 - - sym = get_base_currency(symbol) - - if trade_mode == "Merged_Single": - leverage_str = f"{leverage:.2f}x" - else: - if margin_type == "ISOLATED_MARGIN": - leverage_str = f"{leverage_to_buy:.2f}x:{leverage_to_sell:.2f}x" - else: - leverage_str = f"{leverage:.2f}x" - - conditional_order_type = additional_data.conditional_order_type or "" - conditional_order_type_rus = ( - "Лимитный" - if conditional_order_type == "Limit" - else ( - "Рыночный" - if conditional_order_type == "Market" - else conditional_order_type - ) - ) - - conditional_order_type_text = ( - f"- Тип условного ордера: {conditional_order_type_rus}\n" - if order_type == "Conditional" - else "" - ) - - limit_price_text = "" - trigger_price_text = "" - - if order_type == "Limit": - limit_price_text = f"- Цена лимитного ордера: {limit_price:.4f} USDT\n" - elif order_type == "Conditional": - if conditional_order_type == "Limit": - limit_price_text = f"- Цена лимитного ордера: {limit_price:.4f} USDT\n" - trigger_price_text = f"- Триггер цена: {trigger_price:.4f} USDT\n" - risk_management_data = await rq.get_user_risk_management(tg_id=tg_id) commission_fee = risk_management_data.commission_fee client = await get_bybit_client(tg_id=tg_id) @@ -124,54 +77,32 @@ async def additional_settings(callback_query: CallbackQuery, state: FSMContext) else: commission_fee_percent = 0.0 - if order_type == "Conditional": - if conditional_order_type == "Limit": - entry_price = limit_price - ask_price = limit_price - bid_price = limit_price - else: - ask_price = trigger_price - bid_price = trigger_price - entry_price = trigger_price - else: - if order_type == "Limit": - entry_price = limit_price - ask_price = limit_price - bid_price = limit_price - else: - entry_price = price_symbol - ask_price = ask - bid_price = bid + switch_side_mode = "" + if trade_mode == "Switch": + switch_side_mode = f"- Направление первой сделки: {switch_side}\n" - durability_buy = quantity * bid_price - durability_sell = quantity * ask_price - quantity_price = quantity * entry_price + quantity_price = quantity * trigger_price total_commission = quantity_price * commission_fee_percent total_budget = await calculate_total_budget( - quantity=durability_buy, + quantity=quantity, martingale_factor=martingale, max_steps=max_bets, commission_fee_percent=total_commission, ) text = ( f"Основные настройки:\n\n" - f"- Режим позиции: {trade_mode_rus}\n" + f"- Режим торговли: {trade_mode_rus}\n" + f"{switch_side_mode}" f"- Тип маржи: {margin_type_rus}\n" - f"- Размер кредитного плеча: {leverage_str}\n" - f"- Тип ордера: {order_type_rus}\n" - f"- Количество ордера: {quantity} {sym}\n" + f"- Размер кредитного плеча: {leverage:.2f}\n" + f"- Базовая ставка: {quantity} USDT\n" f"- Коэффициент мартингейла: {martingale:.2f}\n" - f"{conditional_order_type_text}" - f"{trigger_price_text}" - f"{limit_price_text}" + f"- Триггер цена: {trigger_price:.4f} USDT\n" f"- Максимальное кол-во ставок в серии: {max_bets}\n\n" - f"- Стоимость: {durability_buy:.2f}/{durability_sell:.2f} USDT\n" - f"- Рекомендуемый бюджет: {total_budget:.4f} USDT\n" + f"- Бюджет серии: {total_budget:.4f} USDT\n" ) - keyboard = kbi.get_additional_settings_keyboard( - current_order_type=order_type, conditional_order=conditional_order_type - ) + keyboard = kbi.get_additional_settings_keyboard(mode=trade_mode) await callback_query.message.edit_text(text=text, reply_markup=keyboard) logger.debug( "Command additional_settings processed successfully for user: %s", tg_id @@ -202,7 +133,6 @@ async def risk_management(callback_query: CallbackQuery, state: FSMContext) -> N if risk_management_data: take_profit_percent = risk_management_data.take_profit_percent or "" stop_loss_percent = risk_management_data.stop_loss_percent or "" - max_risk_percent = risk_management_data.max_risk_percent or "" commission_fee = risk_management_data.commission_fee or "" commission_fee_rus = ( "Да" if commission_fee == "Yes_commission_fee" else "Нет" @@ -212,7 +142,6 @@ async def risk_management(callback_query: CallbackQuery, state: FSMContext) -> N text=f"Риск-менеджмент:\n\n" f"- Процент изменения цены для фиксации прибыли: {take_profit_percent}%\n" f"- Процент изменения цены для фиксации убытка: {stop_loss_percent}%\n\n" - f"- Максимальный риск на сделку (в % от баланса): {max_risk_percent}%\n\n" f"- Комиссия биржи для расчета прибыли: {commission_fee_rus}\n\n", reply_markup=kbi.risk_management, ) diff --git a/app/telegram/handlers/start_trading.py b/app/telegram/handlers/start_trading.py index 351bb33..36c8801 100644 --- a/app/telegram/handlers/start_trading.py +++ b/app/telegram/handlers/start_trading.py @@ -12,9 +12,7 @@ from app.bybit.open_positions import start_trading_cycle from app.helper_functions import safe_float from app.telegram.tasks.tasks import ( add_start_task_merged, - add_start_task_switch, - cancel_start_task_merged, - cancel_start_task_switch, + cancel_start_task_merged ) from logger_helper.logger_helper import LOGGING_CONFIG @@ -35,97 +33,20 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non """ try: await state.clear() - additional_data = await rq.get_user_additional_settings( - tg_id=callback_query.from_user.id - ) - trade_mode = additional_data.trade_mode symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id) deals = await get_active_positions_by_symbol( tg_id=callback_query.from_user.id, symbol=symbol ) position = next((d for d in deals if d.get("symbol") == symbol), None) + if position: size = position.get("size", 0) - position_idx = position.get("positionIdx") else: size = 0 - position_idx = None - if position_idx != 0 and safe_float(size) > 0 and trade_mode == "Merged_Single": + if safe_float(size) > 0: await callback_query.answer( - text="У вас есть активная позиция в режиме хеджирования. " - "Открытие сделки в одностороннем режиме невозможно.", - ) - return - - if position_idx == 0 and safe_float(size) > 0 and trade_mode == "Both_Sides": - await callback_query.answer( - text="У вас есть активная позиция в одностороннем режиме. " - "Открытие сделки в режиме хеджирования невозможно.", - ) - return - - if trade_mode == "Merged_Single": - await callback_query.message.edit_text( - text="Выберите режим торговли:\n\n" - "Лонг - все сделки серии открываются на покупку.\n" - "Шорт - все сделки серии открываются на продажу.\n" - "Свитч - направление каждой сделки серии меняется по переменно.\n", - reply_markup=kbi.merged_start_trading, - ) - else: # trade_mode == "Both_Sides": - await callback_query.message.edit_text( - text="Выберите режим торговли:\n\n" - "Лонг - все сделки открываются на покупку.\n" - "Шорт - все сделки открываются на продажу.\n", - reply_markup=kbi.both_start_trading, - ) - logger.debug( - "Command start_trading processed successfully for user: %s", - callback_query.from_user.id, - ) - except Exception as e: - await callback_query.answer(text="Произошла ошибка при запуске торговли") - logger.error( - "Error processing command start_trading for user %s: %s", - callback_query.from_user.id, - e, - ) - - -@router_start_trading.callback_query(lambda c: c.data == "long" or c.data == "short") -async def start_trading_long(callback_query: CallbackQuery, state: FSMContext) -> None: - """ - Handles the "long" or "short" callback query. - Clears the FSM state and starts the trading cycle. - :param callback_query: Message - :param state: FSMContext - :return: None - """ - try: - if callback_query.data == "long": - side = "Buy" - elif callback_query.data == "short": - side = "Sell" - else: - await callback_query.answer(text="Произошла ошибка при запуске торговли") - return - - symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id) - deals = await get_active_positions_by_symbol( - tg_id=callback_query.from_user.id, symbol=symbol - ) - position = next((d for d in deals if d.get("symbol") == symbol), None) - if position: - size = position.get("size", 0) - position_idx = position.get("positionIdx") - else: - size = 0 - position_idx = None - - if position_idx == 0 and safe_float(size) > 0: - await callback_query.answer( - text="Торговля уже запущена в одностороннем режиме для данного инструмента" + text="У вас есть активная позиция", ) return @@ -151,12 +72,9 @@ async def start_trading_long(callback_query: CallbackQuery, state: FSMContext) - tg_id=callback_query.from_user.id, symbol=symbol, auto_trading=True, - side=side, ) res = await start_trading_cycle( tg_id=callback_query.from_user.id, - side=side, - switch_side_mode=False, ) error_messages = { @@ -167,9 +85,10 @@ async def start_trading_long(callback_query: CallbackQuery, state: FSMContext) - "ab not enough for new order": "Недостаточно средств для создания нового ордера", "InvalidRequestError": "Произошла ошибка при запуске торговли.", "Order does not meet minimum order value": "Сумма ордера не достаточна для запуска торговли", - "position idx not match position mode": "Торговля уже запущена в режиме хеджирования на продажу для данного инструмента", + "position idx not match position mode": "Ошибка режима позиции для данного инструмента", "Qty invalid": "Некорректное значение ордера для данного инструмента", "The number of contracts exceeds maximum limit allowed": "️️Количество контрактов превышает допустимое максимальное количество контрактов", + "The number of contracts exceeds minimum limit allowed": "️️Количество контрактов превышает допустимое минимальное количество контрактов", } if res == "OK": @@ -180,7 +99,6 @@ async def start_trading_long(callback_query: CallbackQuery, state: FSMContext) - tg_id=callback_query.from_user.id, symbol=symbol, auto_trading=False, - side=side, ) text = error_messages.get(res, "Произошла ошибка при запуске торговли") await callback_query.message.edit_text( @@ -202,189 +120,8 @@ async def start_trading_long(callback_query: CallbackQuery, state: FSMContext) - logger.error("Cancelled timer for user %s", callback_query.from_user.id) -@router_start_trading.callback_query(lambda c: c.data == "switch") -async def start_trading_switch( - callback_query: CallbackQuery, state: FSMContext -) -> None: - """ - Handles the "switch" callback query. - Clears the FSM state and sends a message to the user to select the switch side. - :param callback_query: Message - :param state: FSMContext - :return: None - """ - try: - await state.clear() - await callback_query.message.edit_text( - text="Выберите направление первой сделки серии:\n\n" - "Лонг - открывается первая сделка на покупку.\n" - "Шорт - открывается первая сделка на продажу.\n" - "По направлению - сделка открывается в направлении последней сделки предыдущей серии.\n" - "Противоположно - сделка открывается в противоположном направлении последней сделки предыдущей серии.\n", - reply_markup=kbi.switch_side, - ) - except Exception as e: - await callback_query.answer(text="Произошла ошибка при запуске торговли") - logger.error( - "Error processing command start trading switch for user %s: %s", - callback_query.from_user.id, - e, - ) - - @router_start_trading.callback_query( - lambda c: c.data - in {"switch_long", "switch_short", "switch_direction", "switch_opposite"} -) -async def start_switch(callback_query: CallbackQuery, state: FSMContext) -> None: - """ - Starts the trading cycle with the selected side. - :param callback_query: - :param state: - :return: - """ - try: - symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id) - user_deals_data = await rq.get_user_deal_by_symbol( - tg_id=callback_query.from_user.id, symbol=symbol - ) - - get_side = "Buy" - - if user_deals_data: - get_side = user_deals_data.last_side or "Buy" - - if callback_query.data == "switch_long": - side = "Buy" - elif callback_query.data == "switch_short": - side = "Sell" - elif callback_query.data == "switch_direction": - side = get_side - elif callback_query.data == "switch_opposite": - if get_side == "Buy": - side = "Sell" - else: - side = "Buy" - else: - await callback_query.answer(text="Произошла ошибка при запуске торговли") - return - - deals = await get_active_positions_by_symbol( - tg_id=callback_query.from_user.id, symbol=symbol - ) - position = next((d for d in deals if d.get("symbol") == symbol), None) - if position: - size = position.get("size", 0) - position_idx = position.get("positionIdx") - else: - size = 0 - position_idx = None - - if position_idx == 1 and safe_float(size) > 0 and side == "Buy": - await callback_query.answer( - text="Торговля уже запущена в режиме хеджирования на покупку для данного инструмента" - ) - return - - if position_idx == 2 and safe_float(size) > 0 and side == "Sell": - await callback_query.answer( - text="Торговля уже запущена в режиме хеджирования на продажу для данного инструмента" - ) - return - - conditional_data = await rq.get_user_conditional_settings( - tg_id=callback_query.from_user.id - ) - timer_start = conditional_data.timer_start - - cancel_start_task_switch(user_id=callback_query.from_user.id) - - async def delay_start(): - if timer_start > 0: - await callback_query.message.edit_text( - text=f"Торговля будет запущена с задержкой {timer_start} мин.", - reply_markup=kbi.cancel_timer_switch, - ) - await rq.set_start_timer( - tg_id=callback_query.from_user.id, timer_start=0 - ) - await asyncio.sleep(timer_start * 60) - await rq.set_auto_trading( - tg_id=callback_query.from_user.id, - symbol=symbol, - auto_trading=True, - side=side, - ) - if side == "Buy": - r_side = "Sell" - else: - r_side = "Buy" - await rq.set_auto_trading( - tg_id=callback_query.from_user.id, - symbol=symbol, - auto_trading=True, - side=r_side, - ) - res = await start_trading_cycle( - tg_id=callback_query.from_user.id, - side=side, - switch_side_mode=True, - ) - - error_messages = { - "Limit price is out min price": "Цена лимитного ордера меньше минимального", - "Limit price is out max price": "Цена лимитного ордера больше максимального", - "Risk is too high for this trade": "Риск сделки превышает допустимый убыток", - "estimated will trigger liq": "Лимитный ордер может вызвать мгновенную ликвидацию. Проверьте параметры ордера.", - "ab not enough for new order": "Недостаточно средств для создания нового ордера", - "InvalidRequestError": "Произошла ошибка при запуске торговли.", - "Order does not meet minimum order value": "Сумма ордера не достаточна для запуска торговли", - "position idx not match position mode": "Торговля уже запущена в режиме хеджирования на продажу для данного инструмента", - "Qty invalid": "Некорректное значение ордера для данного инструмента", - "The number of contracts exceeds maximum limit allowed": "️ ️️Количество контрактов превышает допустимое максимальное количество контрактов", - } - - if res == "OK": - await callback_query.message.edit_text(text="Торговля запущена") - await state.clear() - else: - await rq.set_auto_trading( - tg_id=callback_query.from_user.id, - symbol=symbol, - auto_trading=False, - side=side, - ) - if side == "Buy": - r_side = "Sell" - else: - r_side = "Buy" - await rq.set_auto_trading( - tg_id=callback_query.from_user.id, - symbol=symbol, - auto_trading=False, - side=r_side, - ) - text = error_messages.get(res, "Произошла ошибка при запуске торговли") - await callback_query.message.edit_text( - text=text, reply_markup=kbi.profile_bybit - ) - - await callback_query.message.edit_text("Запуск торговли...") - task = asyncio.create_task(delay_start()) - await add_start_task_switch(user_id=callback_query.from_user.id, task=task) - except asyncio.CancelledError: - logger.error("Cancelled timer for user %s", callback_query.from_user.id) - except Exception as e: - await callback_query.answer(text="Произошла ошибка при запуске торговли") - logger.error( - "Error processing command start switch for user %s: %s", - callback_query.from_user.id, - e, - ) - - -@router_start_trading.callback_query( - lambda c: c.data == "cancel_timer_merged" or c.data == "cancel_timer_switch" + lambda c: c.data == "cancel_timer_merged" ) async def cancel_start_trading( callback_query: CallbackQuery, state: FSMContext @@ -400,8 +137,6 @@ async def cancel_start_trading( await state.clear() if callback_query.data == "cancel_timer_merged": cancel_start_task_merged(user_id=callback_query.from_user.id) - elif callback_query.data == "cancel_timer_switch": - cancel_start_task_switch(user_id=callback_query.from_user.id) await callback_query.message.edit_text( text="Запуск торговли отменен", reply_markup=kbi.profile_bybit ) From 89ab106992bdb1566ffb48349ab6f08fda4c1e06 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:20:24 +0500 Subject: [PATCH 06/43] Fixed percentages of TP and SL from integers to floats --- app/telegram/handlers/handlers_main.py | 2 +- .../handlers/main_settings/risk_management.py | 138 +----------------- 2 files changed, 8 insertions(+), 132 deletions(-) diff --git a/app/telegram/handlers/handlers_main.py b/app/telegram/handlers/handlers_main.py index 5efb478..fa5a125 100644 --- a/app/telegram/handlers/handlers_main.py +++ b/app/telegram/handlers/handlers_main.py @@ -53,7 +53,7 @@ async def cmd_start(message: Message, state: FSMContext) -> None: await rq.create_user_conditional_settings(tg_id=tg_id) await message.answer( text=f"Добро пожаловать, {full_name}!\n\n" - "PHANTOM TRADING - ваш надежный помощник для автоматизации трейдинга😉", + "Чат-робот для трейдинга - ваш надежный помощник для анализа рынка и принятия взвешенных решений.😉", reply_markup=kbi.connect_the_platform, ) logger.debug( diff --git a/app/telegram/handlers/main_settings/risk_management.py b/app/telegram/handlers/main_settings/risk_management.py index ad17564..86bb052 100644 --- a/app/telegram/handlers/main_settings/risk_management.py +++ b/app/telegram/handlers/main_settings/risk_management.py @@ -6,7 +6,7 @@ from aiogram.types import CallbackQuery, Message import app.telegram.keyboards.inline as kbi import database.request as rq -from app.helper_functions import is_int +from app.helper_functions import is_number, safe_float from app.telegram.states.states import RiskManagementState from logger_helper.logger_helper import LOGGING_CONFIG @@ -86,7 +86,7 @@ async def set_take_profit_percent(message: Message, state: FSMContext) -> None: take_profit_percent_value = message.text - if not is_int(take_profit_percent_value): + if not is_number(take_profit_percent_value): await message.answer( text="Ошибка: введите валидное число.", reply_markup=kbi.back_to_risk_management, @@ -98,7 +98,7 @@ async def set_take_profit_percent(message: Message, state: FSMContext) -> None: ) return - if int(take_profit_percent_value) < 1 or int(take_profit_percent_value) > 100: + if safe_float(take_profit_percent_value) < 1 or safe_float(take_profit_percent_value) > 100: await message.answer( text="Ошибка: введите число от 1 до 100.", reply_markup=kbi.back_to_risk_management, @@ -112,7 +112,7 @@ async def set_take_profit_percent(message: Message, state: FSMContext) -> None: req = await rq.set_take_profit_percent( tg_id=message.from_user.id, - take_profit_percent=int(take_profit_percent_value), + take_profit_percent=safe_float(take_profit_percent_value), ) if req: @@ -207,7 +207,7 @@ async def set_stop_loss_percent(message: Message, state: FSMContext) -> None: stop_loss_percent_value = message.text - if not is_int(stop_loss_percent_value): + if not is_number(stop_loss_percent_value): await message.answer( text="Ошибка: введите валидное число.", reply_markup=kbi.back_to_risk_management, @@ -219,7 +219,7 @@ async def set_stop_loss_percent(message: Message, state: FSMContext) -> None: ) return - if int(stop_loss_percent_value) < 1 or int(stop_loss_percent_value) > 100: + if safe_float(stop_loss_percent_value) < 1 or safe_float(stop_loss_percent_value) > 100: await message.answer( text="Ошибка: введите число от 1 до 100.", reply_markup=kbi.back_to_risk_management, @@ -232,7 +232,7 @@ async def set_stop_loss_percent(message: Message, state: FSMContext) -> None: return req = await rq.set_stop_loss_percent( - tg_id=message.from_user.id, stop_loss_percent=int(stop_loss_percent_value) + tg_id=message.from_user.id, stop_loss_percent=safe_float(stop_loss_percent_value) ) if req: @@ -262,130 +262,6 @@ async def set_stop_loss_percent(message: Message, state: FSMContext) -> None: ) -@router_risk_management.callback_query(F.data == "max_risk_percent") -async def max_risk_percent(callback_query: CallbackQuery, state: FSMContext) -> None: - """ - Handles the 'max_risk_percent' callback query. - - Clears the current FSM state, edits the message text to display the maximum risk percentage options, - and shows an inline keyboard for selection. - - Args: - callback_query (CallbackQuery): Incoming callback query from Telegram inline keyboard. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - await state.clear() - await state.set_state(RiskManagementState.max_risk_percent_state) - msg = await callback_query.message.edit_text( - text="Введите максимальный процент риска: ", - reply_markup=kbi.back_to_risk_management, - ) - await state.update_data(prompt_message_id=msg.message_id) - logger.debug( - "Command max_risk_percent processed successfully for user: %s", - callback_query.from_user.id, - ) - except Exception as e: - await callback_query.answer( - text="Произошла ошибка. Пожалуйста, попробуйте позже." - ) - logger.error( - "Error processing command max_risk_percent for user %s: %s", - callback_query.from_user.id, - e, - ) - - -@router_risk_management.message(RiskManagementState.max_risk_percent_state) -async def set_max_risk_percent(message: Message, state: FSMContext) -> None: - """ - Handles user input for setting the maximum risk percentage. - - Updates FSM context with the selected percentage and persists the choice in database. - Sends an acknowledgement to user and clears FSM state afterward. - - Args: - message (Message): Incoming message from user containing the maximum risk percentage. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - try: - data = await state.get_data() - if "prompt_message_id" in data: - prompt_message_id = data["prompt_message_id"] - await message.bot.delete_message( - chat_id=message.chat.id, message_id=prompt_message_id - ) - await message.delete() - except Exception as e: - if "message to delete not found" in str(e).lower(): - pass # Ignore this error - else: - raise e - - max_risk_percent_value = message.text - - if not is_int(max_risk_percent_value): - await message.answer( - text="Ошибка: введите валидное число.", - reply_markup=kbi.back_to_risk_management, - ) - logger.debug( - "User %s input invalid (not an valid number): %s", - message.from_user.id, - max_risk_percent_value, - ) - return - - if int(max_risk_percent_value) < 1 or int(max_risk_percent_value) > 100: - await message.answer( - text="Ошибка: введите число от 1 до 100.", - reply_markup=kbi.back_to_risk_management, - ) - logger.debug( - "User %s input invalid (not an valid number): %s", - message.from_user.id, - max_risk_percent_value, - ) - return - - req = await rq.set_max_risk_percent( - tg_id=message.from_user.id, max_risk_percent=int(max_risk_percent_value) - ) - - if req: - await message.answer( - text=f"Максимальный процент риска установлен на {max_risk_percent_value}%.", - reply_markup=kbi.back_to_risk_management, - ) - else: - await message.answer( - text="Произошла ошибка при установке максимального процента риска. " - "Пожалуйста, попробуйте позже.", - reply_markup=kbi.back_to_risk_management, - ) - - await state.clear() - except Exception as e: - await message.answer( - text="Произошла ошибка при установке максимального процента риска. " - "Пожалуйста, попробуйте позже.", - reply_markup=kbi.back_to_risk_management, - ) - logger.error( - "Error processing command max_risk_percent for user %s: %s", - message.from_user.id, - e, - ) - - @router_risk_management.callback_query(F.data == "commission_fee") async def commission_fee(callback_query: CallbackQuery, state: FSMContext) -> None: """ From aa9f04c27e7c240526ea56dea21db73a7eefb84f Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:21:15 +0500 Subject: [PATCH 07/43] The stop trading button has been removed. --- app/telegram/handlers/stop_trading.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/telegram/handlers/stop_trading.py b/app/telegram/handlers/stop_trading.py index e9fc6c4..64e21e0 100644 --- a/app/telegram/handlers/stop_trading.py +++ b/app/telegram/handlers/stop_trading.py @@ -45,12 +45,10 @@ async def stop_all_trading(callback_query: CallbackQuery, state: FSMContext): for active_auto_trading in user_auto_trading_list: if active_auto_trading.auto_trading: symbol = active_auto_trading.symbol - get_side = active_auto_trading.side req = await rq.set_auto_trading( tg_id=callback_query.from_user.id, symbol=symbol, auto_trading=False, - side=get_side, ) if not req: await callback_query.message.edit_text( From fcdc9d7483516cc720b4d0b37257e3a2d81e72de Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:22:13 +0500 Subject: [PATCH 08/43] The function allows you to write the number 0 --- app/helper_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helper_functions.py b/app/helper_functions.py index d12f42f..04db1ce 100644 --- a/app/helper_functions.py +++ b/app/helper_functions.py @@ -34,7 +34,7 @@ def is_number(value: str) -> bool: # Convert the string to a float num = float(value) # Check if the number is positive - if num <= 0: + if num < 0: return False # Check if the string contains "+" or "-" if "+" in value or "-" in value: From 81145334756e5d4706eaa5503a891f9367f5d4b8 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:24:32 +0500 Subject: [PATCH 09/43] When adjusting the leverage, the SL changes according to the criteria. In place of the position mode, there is now a trading mode. All unnecessary functions are also removed. --- .../main_settings/additional_settings.py | 934 +++--------------- 1 file changed, 148 insertions(+), 786 deletions(-) diff --git a/app/telegram/handlers/main_settings/additional_settings.py b/app/telegram/handlers/main_settings/additional_settings.py index 1d1dd4e..df2d1d9 100644 --- a/app/telegram/handlers/main_settings/additional_settings.py +++ b/app/telegram/handlers/main_settings/additional_settings.py @@ -7,14 +7,9 @@ from aiogram.types import CallbackQuery, Message import app.telegram.keyboards.inline as kbi import database.request as rq from app.bybit.get_functions.get_instruments_info import get_instruments_info -from app.bybit.get_functions.get_tickers import get_tickers -from app.bybit.set_functions.set_leverage import ( - set_leverage, - set_leverage_to_buy_and_sell, -) +from app.bybit.set_functions.set_leverage import set_leverage from app.bybit.set_functions.set_margin_mode import set_margin_mode -from app.bybit.set_functions.set_switch_position_mode import set_switch_position_mode -from app.helper_functions import get_base_currency, is_int, is_number, safe_float +from app.helper_functions import is_int, is_number, safe_float from app.telegram.states.states import AdditionalSettingsState from logger_helper.logger_helper import LOGGING_CONFIG @@ -26,7 +21,7 @@ router_additional_settings = Router(name="additional_settings") @router_additional_settings.callback_query(F.data == "trade_mode") async def settings_for_trade_mode( - callback_query: CallbackQuery, state: FSMContext + callback_query: CallbackQuery, state: FSMContext ) -> None: """ Handles the 'trade_mode' callback query. @@ -44,9 +39,10 @@ async def settings_for_trade_mode( try: await state.clear() await callback_query.message.edit_text( - text="Выберите режим позиции:\n\n" - "Односторонний режим — возможно удержание Лонг или же Шорт позиции в контракте.\n\n" - "Хеджирование — возможно удержание обеих Лонг и Шорт позиций в контракте одновременно.", + text="Выберите режим торговли:\n\n" + "Лонг - все сделки серии открываются на покупку.\n" + "Шорт - все сделки серии открываются на продажу.\n" + "Свитч - направление каждой сделки серии меняется по переменно.\n", reply_markup=kbi.trade_mode, ) logger.debug( @@ -66,7 +62,7 @@ async def settings_for_trade_mode( @router_additional_settings.callback_query( - lambda c: c.data == "Merged_Single" or c.data == "Both_Sides" + lambda c: c.data == "Long" or c.data == "Short" or c.data == "Switch" ) async def trade_mode(callback_query: CallbackQuery, state: FSMContext) -> None: """ @@ -83,78 +79,20 @@ async def trade_mode(callback_query: CallbackQuery, state: FSMContext) -> None: Success or error messages with user identification. """ try: - symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id) - additional_settings = await rq.get_user_additional_settings( - tg_id=callback_query.from_user.id - ) - get_leverage = additional_settings.leverage or "10" - get_leverage_to_buy = additional_settings.leverage_to_buy or "10" - get_leverage_to_sell = additional_settings.leverage_to_sell or "10" - leverage_to_float = safe_float(get_leverage) - leverage_to_buy_float = safe_float(get_leverage_to_buy) - leverage_to_sell_float = safe_float(get_leverage_to_sell) - margin_type = additional_settings.margin_type or "ISOLATED_MARGIN" - mode = 0 if callback_query.data.startswith("Merged_Single") else 3 - response = await set_switch_position_mode( - tg_id=callback_query.from_user.id, symbol=symbol, mode=mode - ) - - if not response: - await callback_query.answer( - text="Произошла ошибка при обновлении режима позиции." - ) - return - req = await rq.set_trade_mode( tg_id=callback_query.from_user.id, trade_mode=callback_query.data ) + if not req: await callback_query.answer( - text="Произошла ошибка при обновлении режима позиции." + text="Произошла ошибка при установке режима торговли" ) return - if ( - response - == "You have an existing position, so position mode cannot be switched" - ): - await callback_query.answer( - text="У вас уже есть позиция по паре, " - "поэтому режим позиции не может быть изменен." - ) - return - - if response == "Open orders exist, so you cannot change position mode": - await callback_query.answer( - text="У вас есть открытые ордера, " - "поэтому режим позиции не может быть изменен." - ) - return - - if callback_query.data.startswith("Merged_Single"): - await callback_query.answer(text="Выбран режим позиции: Односторонний") - await set_leverage( - tg_id=callback_query.from_user.id, - symbol=symbol, - leverage=str(leverage_to_float), - ) - - elif callback_query.data.startswith("Both_Sides"): - await callback_query.answer(text="Выбран режим позиции: Хеджирование") - if margin_type == "ISOLATED_MARGIN": - await set_leverage_to_buy_and_sell( - tg_id=callback_query.from_user.id, - symbol=symbol, - leverage_to_buy=str(leverage_to_buy_float), - leverage_to_sell=str(leverage_to_sell_float), - ) - else: - await set_leverage( - tg_id=callback_query.from_user.id, - symbol=symbol, - leverage=str(leverage_to_float), - ) - + await callback_query.answer(text="Режим торговли успешно изменен") + logger.debug( + "Trade mode changed successfully for user: %s", callback_query.from_user.id + ) except Exception as e: await callback_query.answer(text="Произошла ошибка при смене режима позиции.") logger.error( @@ -166,9 +104,97 @@ async def trade_mode(callback_query: CallbackQuery, state: FSMContext) -> None: await state.clear() +@router_additional_settings.callback_query(F.data == "switch_side_start") +async def switch_side_start(callback_query: CallbackQuery, state: FSMContext) -> None: + """ + Handles the 'switch_side_start' callback query. + + Clears the current FSM state, edits the message text to display the switch side start message, + and shows an inline keyboard for selection. + + Args: + callback_query (CallbackQuery): Incoming callback query from Telegram inline keyboard. + state (FSMContext): Finite State Machine context for the current user session. + + Logs: + Success or error messages with user identification. + """ + try: + await state.clear() + await callback_query.message.edit_text( + text="Выберите направление первой сделки серии:\n\n" + "По направлению - сделка открывается в направлении последней сделки предыдущей серии.\n" + "Противоположно - сделка открывается в противоположном направлении последней сделки предыдущей серии.\n", + reply_markup=kbi.switch_side, + ) + logger.debug( + "Command switch_side_start processed successfully for user: %s", + callback_query.from_user.id, + ) + except Exception as e: + await callback_query.answer( + text="Произошла ошибка. Пожалуйста, попробуйте позже." + ) + logger.error( + "Error processing command switch_side_start for user %s: %s", + callback_query.from_user.id, + e, + ) + + +@router_additional_settings.callback_query(lambda c: c.data == "switch_direction" or c.data == "switch_opposite") +async def switch_side_handler(callback_query: CallbackQuery, state: FSMContext) -> None: + """ + Handles callback queries related to switch side selection. + + Updates FSM context with selected switch side and persists the choice in database. + Sends an acknowledgement to user and clears FSM state afterward. + + Args: + callback_query (CallbackQuery): Incoming callback query indicating selected switch side. + state (FSMContext): Finite State Machine context for the current user session. + + Logs: + Success or error messages with user identification. + """ + try: + if callback_query.data == "switch_direction": + switch_side = "По направлению" + elif callback_query.data == "switch_opposite": + switch_side = "Противоположно" + else: + switch_side = None + + req = await rq.set_switch_side( + tg_id=callback_query.from_user.id, switch_side=switch_side + ) + + if not req: + await callback_query.answer( + text="Произошла ошибка при установке направления переключения" + ) + return + + await callback_query.answer(text=f"Выбрано: {switch_side}") + logger.debug( + "Switch side changed successfully for user: %s", callback_query.from_user.id + ) + except Exception as e: + await callback_query.answer( + text="Произошла ошибка при смене направления переключения" + ) + logger.error( + "Error processing set switch_side for user %s: %s", + callback_query.from_user.id, + e, + ) + finally: + await state.clear() + + @router_additional_settings.callback_query(F.data == "margin_type") async def settings_for_margin_type( - callback_query: CallbackQuery, state: FSMContext + callback_query: CallbackQuery, state: FSMContext ) -> None: """ Handles the 'margin_type' callback query. @@ -228,11 +254,8 @@ async def set_margin_type(callback_query: CallbackQuery, state: FSMContext) -> N tg_id=callback_query.from_user.id ) get_leverage = additional_settings.leverage or "10" - get_leverage_to_buy = additional_settings.leverage_to_buy or "10" - get_leverage_to_sell = additional_settings.leverage_to_sell or "10" + leverage_to_float = safe_float(get_leverage) - leverage_to_buy_float = safe_float(get_leverage_to_buy) - leverage_to_sell_float = safe_float(get_leverage_to_sell) bybit_margin_mode = callback_query.data response = await set_margin_mode( tg_id=callback_query.from_user.id, margin_mode=bybit_margin_mode @@ -254,21 +277,16 @@ async def set_margin_type(callback_query: CallbackQuery, state: FSMContext) -> N ) return + await set_leverage( + tg_id=callback_query.from_user.id, + symbol=symbol, + leverage=str(leverage_to_float), + ) + if callback_query.data.startswith("ISOLATED_MARGIN"): await callback_query.answer(text="Выбран тип маржи: Изолированная") - await set_leverage_to_buy_and_sell( - tg_id=callback_query.from_user.id, - symbol=symbol, - leverage_to_buy=str(leverage_to_buy_float), - leverage_to_sell=str(leverage_to_sell_float), - ) elif callback_query.data.startswith("REGULAR_MARGIN"): await callback_query.answer(text="Выбран тип маржи: Кросс") - await set_leverage( - tg_id=callback_query.from_user.id, - symbol=symbol, - leverage=str(leverage_to_float), - ) else: await callback_query.answer( text="Произошла ошибка при установке типа маржи" @@ -285,394 +303,6 @@ async def set_margin_type(callback_query: CallbackQuery, state: FSMContext) -> N await state.clear() -@router_additional_settings.callback_query(F.data == "order_type") -async def settings_for_order_type( - callback_query: CallbackQuery, state: FSMContext -) -> None: - """ - Handles the 'order_type' callback query. - - Clears the current FSM state, edits the message text to display order type options, - and shows an inline keyboard for selection. - - Args: - callback_query (CallbackQuery): Incoming callback query from Telegram inline keyboard. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - await state.clear() - await callback_query.message.edit_text( - text="Выберите тип ордера:\n\n" - "Рыночный ордер - исполняется немедленно по лучшей доступной цене.\n\n" - "Лимитный ордер - это ордер на покупку или продажу по указанной цене или лучше.\n\n" - "Условный ордер - активируются при достижении триггерной цены.", - reply_markup=kbi.order_type, - ) - logger.debug( - "Command order_type processed successfully for user: %s", - callback_query.from_user.id, - ) - except Exception as e: - await callback_query.answer( - text="Произошла ошибка. Пожалуйста, попробуйте позже." - ) - logger.error( - "Error processing command order_type for user %s: %s", - callback_query.from_user.id, - e, - ) - - -@router_additional_settings.callback_query( - lambda c: c.data == "Market" or c.data == "Limit" or c.data == "Conditional" -) -async def set_order_type(callback_query: CallbackQuery, state: FSMContext) -> None: - """ - Handles callback queries starting with 'Market', 'Limit', or 'Conditional'. - - Updates FSM context with selected order type and persists the choice in database. - Sends an acknowledgement to user and clears FSM state afterward. - - Args: - callback_query (CallbackQuery): Incoming callback query indicating selected order type. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - await state.update_data(order_type=callback_query.data) - req = await rq.set_order_type( - tg_id=callback_query.from_user.id, order_type=callback_query.data - ) - - if not req: - await callback_query.answer( - text="Произошла ошибка при установке типа ордера" - ) - return - - if callback_query.data.startswith("Market"): - await callback_query.answer(text="Выбран тип ордера: Рыночный") - elif callback_query.data.startswith("Limit"): - await callback_query.answer(text="Выбран тип ордера: Лимитный") - elif callback_query.data.startswith("Conditional"): - await callback_query.answer(text="Выбран тип ордера: Условный") - else: - await callback_query.answer( - text="Произошла ошибка при установке типа ордера" - ) - - except Exception as e: - await callback_query.answer(text="Произошла ошибка при установке типа ордера") - logger.error( - "Error processing command order_type for user %s: %s", - callback_query.from_user.id, - e, - ) - finally: - await state.clear() - - -@router_additional_settings.callback_query(F.data == "conditional_order_type") -async def settings_for_conditional_order_type( - callback_query: CallbackQuery, state: FSMContext -) -> None: - """ - Handles the 'conditional_order_type' callback query. - - Clears the current FSM state, edits the message text to display conditional order type options, - and shows an inline keyboard for selection. - - Args: - callback_query (CallbackQuery): Incoming callback query from Telegram inline keyboard. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - await state.clear() - await callback_query.message.edit_text( - text="Выберите тип условного ордера:\n\n" - "Рыночный ордер - исполняется немедленно по лучшей доступной цене при достижении триггерной цены.\n\n" - "Лимитный ордер - это ордер на покупку или продажу по указанной цене или лучше.\n\n", - reply_markup=kbi.conditional_order_type, - ) - logger.debug( - "Command conditional_order_type processed successfully for user: %s", - callback_query.from_user.id, - ) - except Exception as e: - await callback_query.answer( - text="Произошла ошибка. Пожалуйста, попробуйте позже." - ) - logger.error( - "Error processing command conditional_order_type for user %s: %s", - callback_query.from_user.id, - e, - ) - - -@router_additional_settings.callback_query( - lambda c: c.data == "set_market" or c.data == "set_limit" -) -async def conditional_order_type( - callback_query: CallbackQuery, state: FSMContext -) -> None: - """ - Handles callback queries starting with 'set_market' or 'set_limit'. - - Updates FSM context with selected conditional order type and persists the choice in database. - Sends an acknowledgement to user and clears FSM state afterward. - - Args: - callback_query (CallbackQuery): Incoming callback query indicating selected conditional order type. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - await state.update_data(conditional_order_type=callback_query.data) - - if callback_query.data.startswith("set_market"): - await callback_query.answer(text="Выбран тип условного ордера: Рыночный") - order_type = "Market" - elif callback_query.data.startswith("set_limit"): - await callback_query.answer(text="Выбран тип условного ордера: Лимитный") - order_type = "Limit" - else: - await callback_query.answer( - text="Произошла ошибка при обновлении типа условного ордера" - ) - return - - req = await rq.set_conditional_order_type( - tg_id=callback_query.from_user.id, conditional_order_type=order_type - ) - - if not req: - await callback_query.answer( - text="Произошла ошибка при обновлении типа условного ордера" - ) - return - except Exception as e: - await callback_query.answer( - text="Произошла ошибка при обновлении типа условного ордера." - ) - logger.error( - "Error processing conditional_order_type for user %s: %s", - callback_query.from_user.id, - e, - ) - finally: - await state.clear() - - -@router_additional_settings.callback_query(F.data == "limit_price") -async def limit_price(callback_query: CallbackQuery, state: FSMContext) -> None: - """ - Handles the 'limit_price' callback query. - - Clears the current FSM state, edits the message text to display the limit price options, - and shows an inline keyboard for selection. - - Args: - callback_query (CallbackQuery): Incoming callback query from Telegram inline keyboard. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - await state.clear() - await callback_query.message.edit_text( - text="Выберите цену лимита:\n\n" - "1. Установить цену - указать цену\n" - "2. Последняя цена - использовать последнюю цену\n", - reply_markup=kbi.change_limit_price, - ) - logger.debug( - "Command limit_price processed successfully for user: %s", - callback_query.from_user.id, - ) - except Exception as e: - await callback_query.answer( - text="Произошла ошибка. Пожалуйста, попробуйте позже." - ) - logger.error( - "Error processing command limit_price for user %s: %s", - callback_query.from_user.id, - e, - ) - - -@router_additional_settings.callback_query(lambda c: c.data == "last_price") -async def last_price(callback_query: CallbackQuery, state: FSMContext) -> None: - """ - Handles the 'last_price' callback query. - - Clears the current FSM state, edits the message text to display the last price option, - and shows an inline keyboard for selection. - - Args: - callback_query (CallbackQuery): Incoming callback query from Telegram inline keyboard. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id) - get_tickers_info = await get_tickers( - tg_id=callback_query.from_user.id, symbol=symbol - ) - if get_tickers_info is None: - await callback_query.answer( - text="Произошла ошибка при установке цены лимита." - ) - return - - mark_price = get_tickers_info.get("lastPrice") or 0 - req = await rq.set_limit_price( - tg_id=callback_query.from_user.id, limit_price=safe_float(mark_price) - ) - if req: - await callback_query.answer( - text=f"Цена лимита установлена на последнюю цену: {mark_price}" - ) - else: - await callback_query.answer( - text="Произошла ошибка при установке цены лимита." - ) - - except Exception as e: - await callback_query.answer(text="Произошла ошибка при установке цены лимита.") - logger.error( - "Error processing last_price for user %s: %s", - callback_query.from_user.id, - e, - ) - finally: - await state.clear() - - -@router_additional_settings.callback_query(lambda c: c.data == "set_limit_price") -async def set_limit_price_handler( - callback_query: CallbackQuery, state: FSMContext -) -> None: - """ - Handles the 'set_limit_price_handler' callback query. - - Clears the current FSM state, edits the message text to prompt for the limit price, - and shows an inline keyboard for input. - - Args: - callback_query (CallbackQuery): Incoming callback query from Telegram inline keyboard. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - await state.clear() - await state.set_state(AdditionalSettingsState.limit_price_state) - await callback_query.answer() - await state.update_data(prompt_message_id=callback_query.message.message_id) - msg = await callback_query.message.edit_text( - text="Введите цену:", reply_markup=kbi.back_to_change_limit_price - ) - await state.update_data(prompt_message_id=msg.message_id) - logger.debug( - "Command set_limit_price processed successfully for user: %s", - callback_query.from_user.id, - ) - except Exception as e: - await callback_query.answer( - text="Произошла ошибка. Пожалуйста, попробуйте позже." - ) - logger.error( - "Error processing command set_limit_price for user %s: %s", - callback_query.from_user.id, - e, - ) - - -@router_additional_settings.message(AdditionalSettingsState.limit_price_state) -async def set_limit_price(message: Message, state: FSMContext) -> None: - """ - Handles user input for setting the limit price. - - Updates FSM context with the selected limit price and persists the choice in database. - Sends an acknowledgement to user and clears FSM state afterward. - - Args: - message (Message): Incoming message from user containing the selected limit price. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - try: - data = await state.get_data() - if "prompt_message_id" in data: - prompt_message_id = data["prompt_message_id"] - await message.bot.delete_message( - chat_id=message.chat.id, message_id=prompt_message_id - ) - await message.delete() - except Exception as e: - if "message to delete not found" in str(e).lower(): - pass # Ignore this error - else: - raise e - - limit_price_value = message.text - - if not is_number(limit_price_value): - await message.answer( - "Ошибка: введите валидное число.", - reply_markup=kbi.back_to_additional_settings, - ) - logger.debug( - "User %s input invalid (not an valid number): %s", - message.from_user.id, - limit_price_value, - ) - return - - req = await rq.set_limit_price( - tg_id=message.from_user.id, limit_price=safe_float(limit_price_value) - ) - if req: - await message.answer( - text=f"Цена лимита установлена на: {limit_price_value}", - reply_markup=kbi.back_to_additional_settings, - ) - else: - await message.answer( - text="Произошла ошибка при установке цены лимита.", - reply_markup=kbi.back_to_change_limit_price, - ) - - await state.clear() - except Exception as e: - await message.answer( - text="Произошла ошибка при установке цены лимита.", - reply_markup=kbi.back_to_additional_settings, - ) - logger.error( - "Error processing set_limit_price for user %s: %s", - message.from_user.id, - e, - ) - - @router_additional_settings.callback_query(lambda c: c.data == "trigger_price") async def trigger_price(callback_query: CallbackQuery, state: FSMContext) -> None: """ @@ -784,7 +414,7 @@ async def set_trigger_price(message: Message, state: FSMContext) -> None: @router_additional_settings.callback_query(F.data == "leverage") -async def leverage_to_buy(callback_query: CallbackQuery, state: FSMContext) -> None: +async def leverage_handler(callback_query: CallbackQuery, state: FSMContext) -> None: """ Handles the 'leverage' callback query. @@ -801,25 +431,12 @@ async def leverage_to_buy(callback_query: CallbackQuery, state: FSMContext) -> N try: await state.clear() await callback_query.answer() - additional_settings = await rq.get_user_additional_settings( - callback_query.from_user.id + await state.set_state(AdditionalSettingsState.leverage_state) + msg = await callback_query.message.edit_text( + text="Введите размер кредитного плеча:", + reply_markup=kbi.back_to_additional_settings, ) - get_trade_mode = additional_settings.trade_mode or "Both_Sides" - get_margin_type = additional_settings.margin_type or "ISOLATED_MARGIN" - if get_trade_mode == "Both_Sides" and get_margin_type == "ISOLATED_MARGIN": - await state.set_state(AdditionalSettingsState.leverage_to_buy_state) - msg = await callback_query.message.edit_text( - text="Введите размер кредитного плеча для Лонг:", - reply_markup=kbi.back_to_additional_settings, - ) - await state.update_data(prompt_message_id=msg.message_id) - else: - await state.set_state(AdditionalSettingsState.leverage_state) - msg = await callback_query.message.edit_text( - text="Введите размер кредитного плеча:", - reply_markup=kbi.back_to_additional_settings, - ) - await state.update_data(prompt_message_id=msg.message_id) + await state.update_data(prompt_message_id=msg.message_id) logger.debug( "Command leverage processed successfully for user: %s", callback_query.from_user.id, @@ -836,7 +453,7 @@ async def leverage_to_buy(callback_query: CallbackQuery, state: FSMContext) -> N @router_additional_settings.message(AdditionalSettingsState.leverage_state) -async def leverage(message: Message, state: FSMContext) -> None: +async def set_leverage_handler(message: Message, state: FSMContext) -> None: """ Handles user input for setting the leverage. @@ -880,29 +497,18 @@ async def leverage(message: Message, state: FSMContext) -> None: return leverage_float = safe_float(get_leverage) - if leverage_float < 1 or leverage_float > 100: - await message.answer( - text="Ошибка: число должно быть от 1 до 100.", - reply_markup=kbi.back_to_additional_settings, - ) - logger.debug( - "User %s input invalid (out of range): %s", - message.from_user.id, - leverage_float, - ) - return symbol = await rq.get_user_symbol(tg_id=tg_id) instruments_info = await get_instruments_info(tg_id=tg_id, symbol=symbol) if instruments_info is not None: min_leverage = ( - safe_float(instruments_info.get("leverageFilter").get("minLeverage")) - or 1 + safe_float(instruments_info.get("leverageFilter").get("minLeverage")) + or 1 ) max_leverage = ( - safe_float(instruments_info.get("leverageFilter").get("maxLeverage")) - or 100 + safe_float(instruments_info.get("leverageFilter").get("maxLeverage")) + or 100 ) if leverage_float > max_leverage or leverage_float < min_leverage: @@ -939,16 +545,15 @@ async def leverage(message: Message, state: FSMContext) -> None: req_leverage = await rq.set_leverage( tg_id=message.from_user.id, leverage=str(leverage_float) ) - req_leverage_to_buy_and_sell = await rq.set_leverage_to_buy_and_sell( - tg_id=message.from_user.id, - leverage_to_buy=str(leverage_float), - leverage_to_sell=str(leverage_float), - ) - if req_leverage and req_leverage_to_buy_and_sell: + + if req_leverage: await message.answer( text=f"Кредитное плечо успешно установлено на {leverage_float}", reply_markup=kbi.back_to_additional_settings, ) + risk_percent = 100 / safe_float(leverage_float) + await rq.set_stop_loss_percent( + tg_id=message.from_user.id, stop_loss_percent=risk_percent) logger.info( "User %s set leverage: %s", message.from_user.id, leverage_float ) @@ -969,247 +574,6 @@ async def leverage(message: Message, state: FSMContext) -> None: ) -@router_additional_settings.message(AdditionalSettingsState.leverage_to_buy_state) -async def set_leverage_to_buy(message: Message, state: FSMContext) -> None: - """ - Handles user input for setting the leverage to buy. - - Updates FSM context with the selected leverage to buy and persists the choice in database. - Sends an acknowledgement to user and clears FSM state afterward. - - Args: - message (Message): Incoming message from user containing the selected leverage to buy. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - try: - data = await state.get_data() - if "prompt_message_id" in data: - prompt_message_id = data["prompt_message_id"] - await message.bot.delete_message( - chat_id=message.chat.id, message_id=prompt_message_id - ) - await message.delete() - except Exception as e: - if "message to delete not found" in str(e).lower(): - pass # Ignore this error - else: - raise e - - get_leverage_to_buy = message.text - tg_id = message.from_user.id - - if not is_number(get_leverage_to_buy): - await message.answer( - "Ошибка: введите валидное число.", - reply_markup=kbi.back_to_additional_settings, - ) - logger.debug( - "User %s input invalid (not an valid number): %s", - message.from_user.id, - get_leverage_to_buy, - ) - return - - leverage_to_buy_float = safe_float(get_leverage_to_buy) - if leverage_to_buy_float < 1 or leverage_to_buy_float > 100: - await message.answer( - text="Ошибка: число должно быть от 1 до 100.", - reply_markup=kbi.back_to_additional_settings, - ) - logger.debug( - "User %s input invalid (out of range): %s", - message.from_user.id, - get_leverage_to_buy, - ) - return - - symbol = await rq.get_user_symbol(tg_id=tg_id) - instruments_info = await get_instruments_info(tg_id=tg_id, symbol=symbol) - - if instruments_info is not None: - max_leverage = safe_float( - instruments_info.get("leverageFilter").get("maxLeverage") - ) - if leverage_to_buy_float > max_leverage: - await message.answer( - text=f"Кредитное плечо {leverage_to_buy_float} превышает максимальное {max_leverage} для {symbol}", - reply_markup=kbi.back_to_additional_settings, - ) - logger.info( - "The requested leverage %s exceeds the maximum %s for %s for user: %s: %s", - leverage_to_buy_float, - max_leverage, - symbol, - message.from_user.id, - ) - return - else: - await message.answer( - text="Произошла ошибка при установке кредитного плеча. Пожалуйста, попробуйте позже.", - reply_markup=kbi.back_to_additional_settings, - ) - logger.error( - "Error processing command leverage_to_buy for user %s", - message.from_user.id, - ) - - await state.update_data(leverage_to_buy=leverage_to_buy_float) - await state.set_state(AdditionalSettingsState.leverage_to_sell_state) - msg = await message.answer( - text="Введите размер кредитного плеча для Шорт:", - reply_markup=kbi.back_to_additional_settings, - ) - await state.update_data(prompt_message_id=msg.message_id) - logger.debug( - "Command leverage_to_buy processed successfully for user: %s", - message.from_user.id, - ) - except Exception as e: - await message.answer( - text="Произошла ошибка при установке кредитного плеча.. Пожалуйста, попробуйте позже.", - reply_markup=kbi.back_to_additional_settings, - ) - logger.error( - "Error processing command leverage_to_buy for user %s: %s", - message.from_user.id, - e, - ) - - -@router_additional_settings.message(AdditionalSettingsState.leverage_to_sell_state) -async def set_leverage_to_sell(message: Message, state: FSMContext) -> None: - """ - Handles user input for setting the leverage to sell. - - Updates FSM context with the selected leverage and persists the choice in database. - Sends an acknowledgement to user and clears FSM state afterward. - - Args: - message (Message): Incoming message from user containing the selected leverage. - state (FSMContext): Finite State Machine context for the current user session. - - Logs: - Success or error messages with user identification. - """ - try: - try: - data = await state.get_data() - if "prompt_message_id" in data: - prompt_message_id = data["prompt_message_id"] - await message.bot.delete_message( - chat_id=message.chat.id, message_id=prompt_message_id - ) - await message.delete() - except Exception as e: - if "message to delete not found" in str(e).lower(): - pass # Ignore this error - else: - raise e - - get_leverage_to_sell = message.text - get_leverage_to_buy = (await state.get_data()).get("leverage_to_buy") - tg_id = message.from_user.id - - if not is_number(get_leverage_to_sell): - await message.answer( - "Ошибка: введите валидное число.", - reply_markup=kbi.back_to_additional_settings, - ) - logger.debug( - "User %s input invalid (not an valid number): %s", - message.from_user.id, - leverage_to_buy or get_leverage_to_sell, - ) - return - - leverage_to_buy_float = safe_float(get_leverage_to_buy) - leverage_to_sell_float = safe_float(get_leverage_to_sell) - if leverage_to_sell_float < 1 or leverage_to_sell_float > 100: - await message.answer( - text="Ошибка: число должно быть от 1 до 100.", - reply_markup=kbi.back_to_additional_settings, - ) - logger.debug( - "User %s input invalid (out of range): %s", - message.from_user.id, - get_leverage_to_sell, - ) - return - - symbol = await rq.get_user_symbol(tg_id=tg_id) - instruments_info = await get_instruments_info(tg_id=tg_id, symbol=symbol) - - if instruments_info is not None: - min_leverage = safe_float( - instruments_info.get("leverageFilter").get("minLeverage") - ) - if leverage_to_sell_float < min_leverage: - await message.answer( - text=f"Кредитное плечо {leverage_to_sell_float} ниже минимального {min_leverage} для {symbol}", - reply_markup=kbi.back_to_additional_settings, - ) - logger.info( - "The requested leverage %s is below the minimum %s for %s for user: %s", - leverage_to_sell_float, - min_leverage, - symbol, - message.from_user.id, - ) - return - else: - await message.answer( - text="Произошла ошибка при установке кредитного плеча. Пожалуйста, попробуйте позже.", - reply_markup=kbi.back_to_additional_settings, - ) - - response = await set_leverage_to_buy_and_sell( - tg_id=message.from_user.id, - symbol=symbol, - leverage_to_buy=str(leverage_to_buy_float), - leverage_to_sell=str(leverage_to_sell_float), - ) - if not response: - await message.answer( - text="Невозможно установить кредитное плечо для текущего режима торговли.", - reply_markup=kbi.back_to_additional_settings, - ) - return - - req = await rq.set_leverage_to_buy_and_sell( - tg_id=message.from_user.id, - leverage_to_buy=str(leverage_to_buy_float), - leverage_to_sell=str(leverage_to_sell_float), - ) - - if req: - await message.answer( - text=f"Размер кредитного плеча установлен на {leverage_to_buy_float} для Лонга " - f"и {leverage_to_sell_float} для Шорта", - reply_markup=kbi.back_to_additional_settings, - ) - else: - await message.answer( - text="Произошла ошибка при установке кредитного плеча. Пожалуйста, попробуйте позже.", - reply_markup=kbi.back_to_additional_settings, - ) - - await state.clear() - except Exception as e: - await message.answer( - text="Произошла ошибка при установке кредитного плеча. Пожалуйста, попробуйте позже.", - reply_markup=kbi.back_to_additional_settings, - ) - logger.error( - "Error processing command set_leverage for user %s: %s", - message.from_user.id, - e, - ) - - @router_additional_settings.callback_query(F.data == "order_quantity") async def order_quantity(callback_query: CallbackQuery, state: FSMContext) -> None: """ @@ -1228,10 +592,8 @@ async def order_quantity(callback_query: CallbackQuery, state: FSMContext) -> No try: await state.clear() await state.set_state(AdditionalSettingsState.quantity_state) - symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id) - name_symbol = get_base_currency(symbol) msg = await callback_query.message.edit_text( - text=f"Укажите размер для ордера в следующей валюте: {name_symbol}.", + text=f"Введите базовую ставку в USDT:", reply_markup=kbi.back_to_additional_settings, ) await state.update_data(prompt_message_id=msg.message_id) @@ -1295,25 +657,6 @@ async def set_order_quantity(message: Message, state: FSMContext) -> None: return quantity = safe_float(order_quantity_value) - symbol = await rq.get_user_symbol(tg_id=message.from_user.id) - instruments_info = await get_instruments_info( - tg_id=message.from_user.id, symbol=symbol - ) - - if instruments_info is not None: - max_order_qty = safe_float( - instruments_info.get("lotSizeFilter").get("maxOrderQty") - ) - min_order_qty = safe_float( - instruments_info.get("lotSizeFilter").get("minOrderQty") - ) - - if quantity < min_order_qty or quantity > max_order_qty: - await message.answer( - text=f"Количество ордера должно быть от {min_order_qty} до {max_order_qty}", - reply_markup=kbi.back_to_additional_settings, - ) - return req = await rq.set_order_quantity( tg_id=message.from_user.id, order_quantity=quantity @@ -1321,7 +664,7 @@ async def set_order_quantity(message: Message, state: FSMContext) -> None: if req: await message.answer( - text=f"Количество ордера установлено на {message.text}", + text=f"Базовая ставка установлена на {message.text} USDT", reply_markup=kbi.back_to_additional_settings, ) else: @@ -1333,7 +676,7 @@ async def set_order_quantity(message: Message, state: FSMContext) -> None: await state.clear() except Exception as e: await message.answer( - text="Произошла ошибка при установке кол-ва ордера. Пожалуйста, попробуйте позже.", + text="Произошла ошибка при установке базовой ставки. Пожалуйста, попробуйте позже.", reply_markup=kbi.back_to_additional_settings, ) logger.error("Error processing command set_order_quantity: %s", e) @@ -1422,6 +765,13 @@ async def set_martingale_factor(message: Message, state: FSMContext) -> None: return martingale_factor_value_float = safe_float(martingale_factor_value) + + if martingale_factor_value_float < 0.1 or martingale_factor_value_float > 10: + await message.answer(text="Ошибка: коэффициент мартингейла должен быть в диапазоне от 0.1 до 10") + logger.debug("User %s input invalid (not in range 0.1 to 10): %s", message.from_user.id, + martingale_factor_value_float) + return + req = await rq.set_martingale_factor( tg_id=message.from_user.id, martingale_factor=martingale_factor_value_float ) @@ -1528,6 +878,18 @@ async def set_max_bets_in_series(message: Message, state: FSMContext) -> None: ) return + if safe_float(max_bets_in_series_value) < 1 or safe_float(max_bets_in_series_value) > 100: + await message.answer( + "Ошибка: число должно быть в диапазоне от 1 до 100.", + reply_markup=kbi.back_to_additional_settings, + ) + logger.debug( + "User %s input invalid (not in range 1 to 100): %s", + message.from_user.id, + max_bets_in_series_value, + ) + return + req = await rq.set_max_bets_in_series( tg_id=message.from_user.id, max_bets_in_series=int(max_bets_in_series_value) ) From 3533e7e99ac6830c93f9a03e5cf6903a7f43d5c7 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:25:30 +0500 Subject: [PATCH 10/43] Unnecessary buttons have been removed, the buttons of the trading mode and the direction of the first transaction of the series have been moved. --- app/telegram/keyboards/inline.py | 166 ++++++------------------------- 1 file changed, 33 insertions(+), 133 deletions(-) diff --git a/app/telegram/keyboards/inline.py b/app/telegram/keyboards/inline.py index 7c1f559..8635eb2 100644 --- a/app/telegram/keyboards/inline.py +++ b/app/telegram/keyboards/inline.py @@ -35,13 +35,7 @@ main_menu = InlineKeyboardMarkup( text="Сменить торговую пару", callback_data="change_symbol" ) ], - [InlineKeyboardButton(text="Мои сделки", callback_data="my_deals")], [InlineKeyboardButton(text="Начать торговлю", callback_data="start_trading")], - [ - InlineKeyboardButton( - text="Остановить торговлю", callback_data="stop_trading" - ) - ], ] ) @@ -67,62 +61,39 @@ main_settings = InlineKeyboardMarkup( # additional_settings -def get_additional_settings_keyboard( - current_order_type: str, conditional_order: str +def get_additional_settings_keyboard(mode: str ) -> InlineKeyboardMarkup: """ Create keyboard for additional settings - :param current_order_type: Market, Limit or Conditional - :param conditional_order: Market or Limit + :param mode: Trade mode :return: InlineKeyboardMarkup """ buttons = [ [ - InlineKeyboardButton(text="Режим позиции", callback_data="trade_mode"), + InlineKeyboardButton(text="Режим торговли", callback_data="trade_mode"), InlineKeyboardButton(text="Тип маржи", callback_data="margin_type"), ], [ InlineKeyboardButton( text="Размер кредитного плеча", callback_data="leverage" ), - InlineKeyboardButton(text="Тип ордера", callback_data="order_type"), + InlineKeyboardButton( + text="Базовая ставка", callback_data="order_quantity"), ], + [ - InlineKeyboardButton( - text="Количество ордера", callback_data="order_quantity" - ), InlineKeyboardButton( text="Коэффициент мартингейла", callback_data="martingale_factor" ), + InlineKeyboardButton(text="Триггер цена", callback_data="trigger_price" + + ), ], ] - if current_order_type == "Conditional": + if mode == "Switch": buttons.append( - [ - InlineKeyboardButton( - text="Тип условного ордера", callback_data="conditional_order_type" - ) - ] - ) - buttons.append( - [InlineKeyboardButton(text="Триггер цена", callback_data="trigger_price")] - ) - if conditional_order == "Limit": - buttons.append( - [ - InlineKeyboardButton( - text="Цена лимитного ордера", callback_data="limit_price" - ) - ] - ) - elif current_order_type == "Limit": - buttons.append( - [ - InlineKeyboardButton( - text="Цена лимитного ордера", callback_data="limit_price" - ) - ] + [InlineKeyboardButton(text="Направление первой сделки", callback_data="switch_side_start")] ) buttons.append( @@ -143,40 +114,31 @@ def get_additional_settings_keyboard( return InlineKeyboardMarkup(inline_keyboard=buttons) -order_type = InlineKeyboardMarkup( - inline_keyboard=[ - [ - InlineKeyboardButton(text="Рыночный", callback_data="Market"), - InlineKeyboardButton(text="Лимитный", callback_data="Limit"), - ], - [InlineKeyboardButton(text="Условный", callback_data="Conditional")], - [ - InlineKeyboardButton(text="Назад", callback_data="additional_settings"), - InlineKeyboardButton(text="На главную", callback_data="profile_bybit"), - ], - ] -) - -conditional_order_type = InlineKeyboardMarkup( - inline_keyboard=[ - [ - InlineKeyboardButton(text="Рыночный", callback_data="set_market"), - InlineKeyboardButton(text="Лимитный", callback_data="set_limit"), - ], - [ - InlineKeyboardButton(text="Назад", callback_data="additional_settings"), - InlineKeyboardButton(text="На главную", callback_data="profile_bybit"), - ], - ] -) - trade_mode = InlineKeyboardMarkup( inline_keyboard=[ [ InlineKeyboardButton( - text="Односторонний режим", callback_data="Merged_Single" + text="Лонг", callback_data="Long" + ), + InlineKeyboardButton(text="Шорт", callback_data="Short"), + InlineKeyboardButton(text="Свитч", callback_data="Switch"), + ], + [ + InlineKeyboardButton(text="Назад", callback_data="additional_settings"), + InlineKeyboardButton(text="На главную", callback_data="profile_bybit"), + ], + ] +) + +switch_side = InlineKeyboardMarkup( + inline_keyboard=[ + [ + InlineKeyboardButton( + text="По направлению", callback_data="switch_direction" + ), + InlineKeyboardButton( + text="Противоположно", callback_data="switch_opposite" ), - InlineKeyboardButton(text="Хеджирование", callback_data="Both_Sides"), ], [ InlineKeyboardButton(text="Назад", callback_data="additional_settings"), @@ -207,21 +169,6 @@ back_to_additional_settings = InlineKeyboardMarkup( ] ) -change_limit_price = InlineKeyboardMarkup( - inline_keyboard=[ - [ - InlineKeyboardButton( - text="Установить цену", callback_data="set_limit_price" - ), - InlineKeyboardButton(text="Последняя цена", callback_data="last_price"), - ], - [ - InlineKeyboardButton(text="Назад", callback_data="additional_settings"), - InlineKeyboardButton(text="На главную", callback_data="profile_bybit"), - ], - ] -) - back_to_change_limit_price = InlineKeyboardMarkup( inline_keyboard=[ [ @@ -239,17 +186,12 @@ risk_management = InlineKeyboardMarkup( inline_keyboard=[ [ InlineKeyboardButton( - text="Изм. цены прибыли", callback_data="take_profit_percent" + text="Тейк-профит", callback_data="take_profit_percent" ), InlineKeyboardButton( - text="Изм. цены убытка", callback_data="stop_loss_percent" + text="Стоп-лосс", callback_data="stop_loss_percent" ), ], - [ - InlineKeyboardButton( - text="Максимальный риск", callback_data="max_risk_percent" - ) - ], [InlineKeyboardButton(text="Комиссия биржи", callback_data="commission_fee")], [ InlineKeyboardButton(text="Назад", callback_data="main_settings"), @@ -391,48 +333,6 @@ def make_close_orders_keyboard(symbol_order: str, order_id: str): # START TRADING -merged_start_trading = InlineKeyboardMarkup( - inline_keyboard=[ - [ - InlineKeyboardButton(text="Лонг", callback_data="long"), - InlineKeyboardButton(text="Шорт", callback_data="short"), - ], - [InlineKeyboardButton(text="Свитч", callback_data="switch")], - [InlineKeyboardButton(text="Назад", callback_data="profile_bybit")], - ] -) - -both_start_trading = InlineKeyboardMarkup( - inline_keyboard=[ - [ - InlineKeyboardButton(text="Лонг", callback_data="long"), - InlineKeyboardButton(text="Шорт", callback_data="short"), - ], - [InlineKeyboardButton(text="Назад", callback_data="profile_bybit")], - ] -) - -switch_side = InlineKeyboardMarkup( - inline_keyboard=[ - [ - InlineKeyboardButton(text="Лонг", callback_data="switch_long"), - InlineKeyboardButton(text="Шорт", callback_data="switch_short"), - ], - [ - InlineKeyboardButton( - text="По направлению", callback_data="switch_direction" - ), - InlineKeyboardButton( - text="Противоположно", callback_data="switch_opposite" - ), - ], - [ - InlineKeyboardButton(text="Назад", callback_data="start_trading"), - InlineKeyboardButton(text="На главную", callback_data="profile_bybit"), - ], - ] -) - back_to_start_trading = InlineKeyboardMarkup( inline_keyboard=[ [ From 9c1f2898700acb244d26953ddcdf56dc1a1bba49 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:26:56 +0500 Subject: [PATCH 11/43] The currency of the coin is treason on USDT, unnecessary parameters are removed --- app/bybit/open_positions.py | 301 ++++++++++++++---------------------- 1 file changed, 114 insertions(+), 187 deletions(-) diff --git a/app/bybit/open_positions.py b/app/bybit/open_positions.py index 5e5eed5..7b8d3b4 100644 --- a/app/bybit/open_positions.py +++ b/app/bybit/open_positions.py @@ -1,89 +1,113 @@ import logging.config +import math from pybit.exceptions import InvalidRequestError import database.request as rq from app.bybit import get_bybit_client -from app.bybit.get_functions.get_balance import get_balance from app.bybit.get_functions.get_instruments_info import get_instruments_info from app.bybit.get_functions.get_tickers import get_tickers from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG -from app.bybit.set_functions.set_leverage import ( - set_leverage, - set_leverage_to_buy_and_sell, -) +from app.bybit.set_functions.set_leverage import set_leverage from app.bybit.set_functions.set_margin_mode import set_margin_mode -from app.bybit.set_functions.set_switch_position_mode import set_switch_position_mode -from app.helper_functions import check_limit_price, get_liquidation_price, safe_float +from app.helper_functions import get_liquidation_price, safe_float logging.config.dictConfig(LOGGING_CONFIG) logger = logging.getLogger("open_positions") async def start_trading_cycle( - tg_id: int, side: str, switch_side_mode: bool + tg_id: int ) -> str | None: """ Start trading cycle :param tg_id: Telegram user ID - :param side: Buy or Sell - :param switch_side_mode: switch_side_mode """ try: + client = await get_bybit_client(tg_id=tg_id) symbol = await rq.get_user_symbol(tg_id=tg_id) additional_data = await rq.get_user_additional_settings(tg_id=tg_id) risk_management_data = await rq.get_user_risk_management(tg_id=tg_id) - + commission_fee = risk_management_data.commission_fee + user_deals_data = await rq.get_user_deal_by_symbol( + tg_id=tg_id, symbol=symbol + ) trade_mode = additional_data.trade_mode + switch_side = additional_data.switch_side margin_type = additional_data.margin_type leverage = additional_data.leverage - leverage_to_buy = additional_data.leverage_to_buy - leverage_to_sell = additional_data.leverage_to_sell - order_type = additional_data.order_type - conditional_order_type = additional_data.conditional_order_type order_quantity = additional_data.order_quantity - limit_price = additional_data.limit_price trigger_price = additional_data.trigger_price martingale_factor = additional_data.martingale_factor max_bets_in_series = additional_data.max_bets_in_series take_profit_percent = risk_management_data.take_profit_percent stop_loss_percent = risk_management_data.stop_loss_percent - max_risk_percent = risk_management_data.max_risk_percent - mode = 3 if trade_mode == "Both_Sides" else 0 - await set_switch_position_mode(tg_id=tg_id, symbol=symbol, mode=mode) - await set_margin_mode(tg_id=tg_id, margin_mode=margin_type) - if trade_mode == "Both_Sides" and margin_type == "ISOLATED_MARGIN": - await set_leverage_to_buy_and_sell( - tg_id=tg_id, - symbol=symbol, - leverage_to_buy=leverage_to_buy, - leverage_to_sell=leverage_to_sell, - ) + get_side = "Buy" + + if user_deals_data: + get_side = user_deals_data.last_side or "Buy" + + if trade_mode == "Switch": + if switch_side == "По направлению": + side = get_side + else: + if get_side == "Buy": + side = "Sell" + else: + side = "Buy" else: - await set_leverage( - tg_id=tg_id, - symbol=symbol, - leverage=leverage, + if trade_mode == "Long": + side = "Buy" + else: + side = "Sell" + + # Get fee rates + fee_info = client.get_fee_rates(category="linear", symbol=symbol) + + # Check if commission fee is enabled + commission_fee_percent = 0.0 + if commission_fee == "Yes_commission_fee": + commission_fee_percent = safe_float( + fee_info["result"]["list"][0]["takerFeeRate"] ) + get_ticker = await get_tickers(tg_id, symbol=symbol) + price_symbol = safe_float(get_ticker.get("lastPrice")) or 0 + instruments_info = await get_instruments_info(tg_id=tg_id, symbol=symbol) + qty_step_str = instruments_info.get("lotSizeFilter").get("qtyStep") + qty_step = safe_float(qty_step_str) + qty = safe_float(order_quantity) / safe_float(price_symbol) + decimals = abs(int(round(math.log10(qty_step)))) + qty_formatted = math.floor(qty / qty_step) * qty_step + qty_formatted = round(qty_formatted, decimals) + + if trigger_price > 0: + po_trigger_price = str(trigger_price) + else: + po_trigger_price = None + + price_for_cals = trigger_price if po_trigger_price is not None else price_symbol + total_commission = price_for_cals * qty_formatted * commission_fee_percent + + await set_margin_mode(tg_id=tg_id, margin_mode=margin_type) + await set_leverage( + tg_id=tg_id, + symbol=symbol, + leverage=leverage, + ) + res = await open_positions( tg_id=tg_id, symbol=symbol, side=side, - order_type=order_type, - conditional_order_type=conditional_order_type, order_quantity=order_quantity, - limit_price=limit_price, trigger_price=trigger_price, - trade_mode=trade_mode, margin_type=margin_type, leverage=leverage, - leverage_to_buy=leverage_to_buy, - leverage_to_sell=leverage_to_sell, take_profit_percent=take_profit_percent, stop_loss_percent=stop_loss_percent, - max_risk_percent=max_risk_percent, + commission_fee_percent=total_commission ) if res == "OK": @@ -95,19 +119,13 @@ async def start_trading_cycle( trade_mode=trade_mode, margin_type=margin_type, leverage=leverage, - leverage_to_buy=leverage_to_buy, - leverage_to_sell=leverage_to_sell, - order_type="Market", - conditional_order_type=conditional_order_type, order_quantity=order_quantity, - limit_price=limit_price, trigger_price=trigger_price, martingale_factor=martingale_factor, max_bets_in_series=max_bets_in_series, take_profit_percent=take_profit_percent, stop_loss_percent=stop_loss_percent, - max_risk_percent=max_risk_percent, - switch_side_mode=switch_side_mode, + base_quantity=order_quantity ) return "OK" return ( @@ -124,6 +142,7 @@ async def start_trading_cycle( "position idx not match position mode", "Qty invalid", "The number of contracts exceeds maximum limit allowed", + "The number of contracts exceeds minimum limit allowed" } else None ) @@ -134,43 +153,30 @@ async def start_trading_cycle( async def trading_cycle( - tg_id: int, symbol: str, reverse_side: str, size: str + tg_id: int, symbol: str, reverse_side: str ) -> str | None: try: user_deals_data = await rq.get_user_deal_by_symbol(tg_id=tg_id, symbol=symbol) + user_auto_trading_data = await rq.get_user_auto_trading(tg_id=tg_id, symbol=symbol) + total_fee = user_auto_trading_data.total_fee trade_mode = user_deals_data.trade_mode - order_type = user_deals_data.order_type - conditional_order_type = user_deals_data.conditional_order_type margin_type = user_deals_data.margin_type leverage = user_deals_data.leverage - leverage_to_buy = user_deals_data.leverage_to_buy - leverage_to_sell = user_deals_data.leverage_to_sell - limit_price = user_deals_data.limit_price - trigger_price = user_deals_data.trigger_price + trigger_price = 0 take_profit_percent = user_deals_data.take_profit_percent stop_loss_percent = user_deals_data.stop_loss_percent - max_risk_percent = user_deals_data.max_risk_percent max_bets_in_series = user_deals_data.max_bets_in_series martingale_factor = user_deals_data.martingale_factor current_step = user_deals_data.current_step - switch_side_mode = user_deals_data.switch_side_mode + order_quantity = user_deals_data.order_quantity + base_quantity = user_deals_data.base_quantity - mode = 3 if trade_mode == "Both_Sides" else 0 - await set_switch_position_mode(tg_id=tg_id, symbol=symbol, mode=mode) await set_margin_mode(tg_id=tg_id, margin_mode=margin_type) - if trade_mode == "Both_Sides" and margin_type == "ISOLATED_MARGIN": - await set_leverage_to_buy_and_sell( - tg_id=tg_id, - symbol=symbol, - leverage_to_buy=leverage_to_buy, - leverage_to_sell=leverage_to_sell, - ) - else: - await set_leverage( - tg_id=tg_id, - symbol=symbol, - leverage=leverage, - ) + await set_leverage( + tg_id=tg_id, + symbol=symbol, + leverage=leverage, + ) if reverse_side == "Buy": real_side = "Sell" @@ -179,11 +185,11 @@ async def trading_cycle( side = real_side - if switch_side_mode: + if trade_mode == "Switch": side = "Sell" if real_side == "Buy" else "Buy" - next_quantity = safe_float(size) * ( - safe_float(martingale_factor) ** current_step + next_quantity = safe_float(order_quantity) * ( + safe_float(martingale_factor) ) current_step += 1 @@ -194,19 +200,13 @@ async def trading_cycle( tg_id=tg_id, symbol=symbol, side=side, - order_type="Market", - conditional_order_type=conditional_order_type, order_quantity=next_quantity, - limit_price=limit_price, trigger_price=trigger_price, - trade_mode=trade_mode, margin_type=margin_type, leverage=leverage, - leverage_to_buy=leverage_to_buy, - leverage_to_sell=leverage_to_sell, take_profit_percent=take_profit_percent, stop_loss_percent=stop_loss_percent, - max_risk_percent=max_risk_percent, + commission_fee_percent=total_fee ) if res == "OK": @@ -218,19 +218,13 @@ async def trading_cycle( trade_mode=trade_mode, margin_type=margin_type, leverage=leverage, - leverage_to_buy=leverage_to_buy, - leverage_to_sell=leverage_to_sell, - order_type=order_type, - conditional_order_type=conditional_order_type, order_quantity=next_quantity, - limit_price=limit_price, trigger_price=trigger_price, martingale_factor=martingale_factor, max_bets_in_series=max_bets_in_series, take_profit_percent=take_profit_percent, stop_loss_percent=stop_loss_percent, - max_risk_percent=max_risk_percent, - switch_side_mode=switch_side_mode, + base_quantity=base_quantity ) return "OK" @@ -255,123 +249,56 @@ async def open_positions( tg_id: int, side: str, symbol: str, - order_type: str, - conditional_order_type: str, order_quantity: float, - limit_price: float, trigger_price: float, - trade_mode: str, margin_type: str, leverage: str, - leverage_to_buy: str, - leverage_to_sell: str, take_profit_percent: float, stop_loss_percent: float, - max_risk_percent: float, + commission_fee_percent: float ) -> str | None: try: client = await get_bybit_client(tg_id=tg_id) - - risk_management_data = await rq.get_user_risk_management(tg_id=tg_id) - commission_fee = risk_management_data.commission_fee - wallet = await get_balance(tg_id=tg_id) - user_balance = wallet.get("totalWalletBalance", 0) - instruments_resp = await get_instruments_info(tg_id=tg_id, symbol=symbol) - get_order_prices = instruments_resp.get("priceFilter") - min_price = safe_float(get_order_prices.get("minPrice")) - max_price = safe_float(get_order_prices.get("maxPrice")) get_ticker = await get_tickers(tg_id, symbol=symbol) price_symbol = safe_float(get_ticker.get("lastPrice")) or 0 + instruments_info = await get_instruments_info(tg_id=tg_id, symbol=symbol) + qty_step_str = instruments_info.get("lotSizeFilter").get("qtyStep") + qty_step = safe_float(qty_step_str) + qty = safe_float(order_quantity) / safe_float(price_symbol) + decimals = abs(int(round(math.log10(qty_step)))) + qty_formatted = math.floor(qty / qty_step) * qty_step + qty_formatted = round(qty_formatted, decimals) - if order_type == "Conditional": + if trigger_price > 0: po_trigger_price = str(trigger_price) trigger_direction = 1 if trigger_price > price_symbol else 2 - if conditional_order_type == "Limit": - error = check_limit_price(limit_price, min_price, max_price) - if error in { - "Limit price is out min price", - "Limit price is out max price", - }: - return error - - order_type = "Limit" - price_for_calc = limit_price - tpsl_mode = "Partial" - else: - order_type = "Market" - price_for_calc = trigger_price - tpsl_mode = "Full" else: - if order_type == "Limit": - error = check_limit_price(limit_price, min_price, max_price) - if error in { - "Limit price is out min price", - "Limit price is out max price", - }: - return error - - price_for_calc = limit_price - tpsl_mode = "Partial" - else: - order_type = "Market" - price_for_calc = price_symbol - tpsl_mode = "Full" po_trigger_price = None trigger_direction = None - if trade_mode == "Both_Sides": - po_position_idx = 1 if side == "Buy" else 2 - if margin_type == "ISOLATED_MARGIN": - get_leverage = safe_float( - leverage_to_buy if side == "Buy" else leverage_to_sell - ) - else: - get_leverage = safe_float(leverage) - else: - po_position_idx = 0 - get_leverage = safe_float(leverage) + get_leverage = safe_float(leverage) - potential_loss = ( - safe_float(order_quantity) - * safe_float(price_for_calc) - * (stop_loss_percent / 100) - ) - adjusted_loss = potential_loss / get_leverage - allowed_loss = safe_float(user_balance) * (max_risk_percent / 100) + price_for_cals = trigger_price if po_trigger_price is not None else price_symbol - if adjusted_loss > allowed_loss: - return "Risk is too high for this trade" - - # Get fee rates - fee_info = client.get_fee_rates(category="linear", symbol=symbol) - - # Check if commission fee is enabled - commission_fee_percent = 0.0 - if commission_fee == "Yes_commission_fee": - commission_fee_percent = safe_float( - fee_info["result"]["list"][0]["takerFeeRate"] - ) - - total_commission = price_for_calc * order_quantity * commission_fee_percent tp_multiplier = 1 + (take_profit_percent / 100) - if total_commission > 0: - tp_multiplier += total_commission + if commission_fee_percent > 0: + tp_multiplier += commission_fee_percent if margin_type == "ISOLATED_MARGIN": liq_long, liq_short = await get_liquidation_price( tg_id=tg_id, - entry_price=price_for_calc, + entry_price=price_for_cals, symbol=symbol, leverage=get_leverage, ) - if (liq_long > 0 or liq_short > 0) and price_for_calc > 0: + if (liq_long > 0 or liq_short > 0) and price_for_cals > 0: if side == "Buy": - base_tp = price_for_calc + (price_for_calc - liq_long) - take_profit_price = base_tp + total_commission + base_tp = price_for_cals + (price_for_cals - liq_long) + take_profit_price = base_tp + commission_fee_percent else: - base_tp = price_for_calc - (liq_short - price_for_calc) - take_profit_price = base_tp - total_commission + base_tp = price_for_cals - (liq_short - price_for_cals) + take_profit_price = base_tp - commission_fee_percent take_profit_price = max(take_profit_price, 0) else: take_profit_price = None @@ -379,40 +306,38 @@ async def open_positions( stop_loss_price = None else: if side == "Buy": - take_profit_price = price_for_calc * tp_multiplier - stop_loss_price = price_for_calc * (1 - stop_loss_percent / 100) + take_profit_price = price_for_cals * tp_multiplier + stop_loss_price = price_for_cals * (1 - stop_loss_percent / 100) else: - take_profit_price = price_for_calc * ( - 1 - (take_profit_percent / 100) - total_commission + take_profit_price = price_for_cals * ( + 1 - (take_profit_percent / 100) - commission_fee_percent ) - stop_loss_price = price_for_calc * (1 + stop_loss_percent / 100) + stop_loss_price = trigger_price * (1 + stop_loss_percent / 100) take_profit_price = max(take_profit_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 order_params = { "category": "linear", "symbol": symbol, "side": side, - "orderType": order_type, - "qty": str(order_quantity), + "orderType": "Market", + "qty": str(qty_formatted), "triggerDirection": trigger_direction, "triggerPrice": po_trigger_price, "triggerBy": "LastPrice", "timeInForce": "GTC", - "positionIdx": po_position_idx, - "tpslMode": tpsl_mode, + "positionIdx": 0, + "tpslMode": "Full", "takeProfit": str(take_profit_price) if take_profit_price else None, "stopLoss": str(stop_loss_price) if stop_loss_price else None, } - if order_type == "Conditional": - if conditional_order_type == "Limit": - order_params["price"] = str(limit_price) - if order_type == "Limit": - order_params["price"] = str(limit_price) - response = client.place_order(**order_params) if response["retCode"] == 0: @@ -430,6 +355,8 @@ async def open_positions( "ab not enough for new order": "ab not enough for new order", "position idx not match position mode": "position idx not match position mode", "Qty invalid": "Qty invalid", + "The number of contracts exceeds maximum limit allowed": "The number of contracts exceeds maximum limit allowed", + "The number of contracts exceeds minimum limit allowed": "The number of contracts exceeds minimum limit allowed", } for key, msg in known_errors.items(): if key in error_text: From 9d06412605eb2d89705a7937a96b0f822b68eca4 Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:28:12 +0500 Subject: [PATCH 12/43] Added the ability to summarize all commissions within a series.Minor bugs have been fixed --- app/bybit/telegram_message_handler.py | 108 +++++++++----------------- 1 file changed, 35 insertions(+), 73 deletions(-) diff --git a/app/bybit/telegram_message_handler.py b/app/bybit/telegram_message_handler.py index 6ac14f1..9662eab 100644 --- a/app/bybit/telegram_message_handler.py +++ b/app/bybit/telegram_message_handler.py @@ -4,7 +4,7 @@ import app.telegram.keyboards.inline as kbi import database.request as rq from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG from app.bybit.open_positions import trading_cycle -from app.helper_functions import format_value, safe_float, safe_int +from app.helper_functions import format_value, safe_float logging.config.dictConfig(LOGGING_CONFIG) logger = logging.getLogger("telegram_message_handler") @@ -22,12 +22,6 @@ class TelegramMessageHandler: order_data = message.get("data", [{}])[0] symbol = format_value(order_data.get("symbol")) qty = format_value(order_data.get("qty")) - order_type = format_value(order_data.get("orderType")) - order_type_rus = ( - "Рыночный" - if order_type == "Market" - else "Лимитный" if order_type == "Limit" else "Нет данных" - ) side = format_value(order_data.get("side")) side_rus = ( "Покупка" @@ -40,39 +34,16 @@ class TelegramMessageHandler: take_profit = format_value(order_data.get("takeProfit")) stop_loss = format_value(order_data.get("stopLoss")) - position_idx = safe_int(order_data.get("positionIdx")) - position_idx_rus = ( - "Односторонний" - if position_idx == 0 - else ( - "Покупка в режиме хеджирования" - if position_idx == 1 - else ( - "Продажа в режиме хеджирования" - if position_idx == 2 - else "Нет данных" - ) - ) - ) - status_map = { - "New": "Ордер создан", - "Cancelled": "Ордер отменен", - "Deactivated": "Ордер деактивирован", "Untriggered": "Условный ордер выставлен", } if order_status == "Filled" or order_status not in status_map: return None - status_text = status_map[order_status] - text = ( - f"{status_text}:\n" f"Торговая пара: {symbol}\n" - f"Режим позиции: {position_idx_rus}\n" f"Количество: {qty}\n" - f"Тип ордера: {order_type_rus}\n" f"Движение: {side_rus}\n" ) if price and price != "0": @@ -97,13 +68,6 @@ class TelegramMessageHandler: symbol = format_value(execution.get("symbol")) exec_price = format_value(execution.get("execPrice")) exec_fee = format_value(execution.get("execFee")) - exec_qty = format_value(execution.get("execQty")) - order_type = format_value(execution.get("orderType")) - order_type_rus = ( - "Рыночный" - if order_type == "Market" - else "Лимитный" if order_type == "Limit" else "Нет данных" - ) side = format_value(execution.get("side")) side_rus = ( "Покупка" @@ -113,14 +77,17 @@ class TelegramMessageHandler: if safe_float(closed_size) == 0: await rq.set_fee_user_auto_trading( - tg_id=tg_id, symbol=symbol, side=side, fee=safe_float(exec_fee) + tg_id=tg_id, symbol=symbol, fee=safe_float(exec_fee) ) - if side == "Buy": - res_side = "Sell" - else: - res_side = "Buy" + user_auto_trading = await rq.get_user_auto_trading( - tg_id=tg_id, symbol=symbol, side=res_side + tg_id=tg_id, symbol=symbol + ) + + get_total_fee = user_auto_trading.total_fee + total_fee = safe_float(exec_fee) + safe_float(get_total_fee) + await rq.set_total_fee_user_auto_trading( + tg_id=tg_id, symbol=symbol, total_fee=total_fee ) if user_auto_trading is not None and user_auto_trading.fee is not None: @@ -142,13 +109,15 @@ class TelegramMessageHandler: ) text = f"{header}\n" f"Торговая пара: {symbol}\n" - if safe_float(closed_size) > 0: - text += f"Количество закрытых сделок: {closed_size}\n" + user_deals_data = await rq.get_user_deal_by_symbol( + tg_id=tg_id, symbol=symbol + ) + exec_bet = user_deals_data.order_quantity + base_quantity = user_deals_data.base_quantity text += ( f"Цена исполнения: {exec_price}\n" - f"Количество исполненных сделок: {exec_qty}\n" - f"Тип ордера: {order_type_rus}\n" + f"Текущая ставка: {exec_bet}\n" f"Движение: {side_rus}\n" f"Комиссия за сделку: {exec_fee}\n" ) @@ -175,27 +144,26 @@ class TelegramMessageHandler: await self.telegram_bot.send_message( chat_id=tg_id, text=profit_text, reply_markup=kbi.profile_bybit ) - if side == "Buy": - r_side = "Sell" - else: - r_side = "Buy" await rq.set_auto_trading( - tg_id=tg_id, symbol=symbol, auto_trading=False, side=r_side + tg_id=tg_id, symbol=symbol, auto_trading=False ) - user_deals_data = await rq.get_user_deal_by_symbol( - tg_id=tg_id, symbol=symbol + + await rq.set_total_fee_user_auto_trading( + tg_id=tg_id, symbol=symbol, total_fee=0 + ) + await rq.set_fee_user_auto_trading( + tg_id=tg_id, symbol=symbol, fee=0 + ) + await rq.set_order_quantity( + tg_id=message.from_user.id, order_quantity=base_quantity ) - if user_deals_data and user_deals_data.switch_side_mode: - await rq.set_auto_trading( - tg_id=tg_id, symbol=symbol, auto_trading=False, side=side - ) else: open_order_text = "\n❗️ Сделка закрылась в минус, открываю новую сделку с увеличенной ставкой.\n" await self.telegram_bot.send_message( chat_id=tg_id, text=open_order_text ) res = await trading_cycle( - tg_id=tg_id, symbol=symbol, reverse_side=side, size=closed_size + tg_id=tg_id, symbol=symbol, reverse_side=side ) if res == "OK": @@ -211,27 +179,21 @@ class TelegramMessageHandler: error_text = errors.get( res, "❗️ Не удалось открыть новую сделку" ) - if side == "Buy": - r_side = "Sell" - else: - r_side = "Buy" await rq.set_auto_trading( - tg_id=tg_id, symbol=symbol, auto_trading=False, side=r_side + tg_id=tg_id, symbol=symbol, auto_trading=False ) - user_deals_data = await rq.get_user_deal_by_symbol( - tg_id=tg_id, symbol=symbol + + await rq.set_total_fee_user_auto_trading( + tg_id=tg_id, symbol=symbol, total_fee=0 + ) + await rq.set_fee_user_auto_trading( + tg_id=tg_id, symbol=symbol, fee=0 ) - if user_deals_data and user_deals_data.switch_side_mode: - await rq.set_auto_trading( - tg_id=tg_id, - symbol=symbol, - auto_trading=False, - side=side, - ) await self.telegram_bot.send_message( chat_id=tg_id, text=error_text, reply_markup=kbi.profile_bybit, ) + except Exception as e: logger.error("Error in telegram_message_handler: %s", e) From fe030baef5e17b6d1baaef21f458e21aefe283ce Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:30:32 +0500 Subject: [PATCH 13/43] When choosing a coin, the leverage is set to the maximum possible for this coin, also SL in accordance with the leverage. The verification range from 1 to 100 has been removed, now the verification is within the acceptable values from the exchange --- app/telegram/handlers/changing_the_symbol.py | 59 +++++--------------- 1 file changed, 14 insertions(+), 45 deletions(-) diff --git a/app/telegram/handlers/changing_the_symbol.py b/app/telegram/handlers/changing_the_symbol.py index f13d9cb..16e5568 100644 --- a/app/telegram/handlers/changing_the_symbol.py +++ b/app/telegram/handlers/changing_the_symbol.py @@ -7,13 +7,12 @@ from aiogram.types import CallbackQuery, Message import app.telegram.keyboards.inline as kbi import database.request as rq from app.bybit.get_functions.get_tickers import get_tickers +from app.bybit.get_functions.get_instruments_info import get_instruments_info from app.bybit.profile_bybit import user_profile_bybit -from app.bybit.set_functions.set_leverage import ( - set_leverage, - set_leverage_to_buy_and_sell, -) +from app.bybit.set_functions.set_leverage import set_leverage + from app.bybit.set_functions.set_margin_mode import set_margin_mode -from app.bybit.set_functions.set_switch_position_mode import set_switch_position_mode +from app.helper_functions import safe_float from app.telegram.states.states import ChangingTheSymbolState from logger_helper.logger_helper import LOGGING_CONFIG @@ -91,12 +90,7 @@ async def set_symbol(message: Message, state: FSMContext) -> None: await rq.create_user_additional_settings(tg_id=message.from_user.id) return - trade_mode = additional_settings.trade_mode or "Merged_Single" - mode = 0 if trade_mode == "Merged_Single" else 3 margin_type = additional_settings.margin_type or "ISOLATED_MARGIN" - leverage = "10" - leverage_to_buy = "10" - leverage_to_sell = "10" ticker = await get_tickers(tg_id=message.from_user.id, symbol=symbol) if ticker is None: @@ -105,6 +99,8 @@ async def set_symbol(message: Message, state: FSMContext) -> None: ) return + instruments_info = await get_instruments_info(tg_id=message.from_user.id, symbol=symbol) + max_leverage = instruments_info.get("leverageFilter").get("maxLeverage") req = await rq.set_user_symbol(tg_id=message.from_user.id, symbol=symbol) if not req: @@ -118,45 +114,18 @@ async def set_symbol(message: Message, state: FSMContext) -> None: tg_id=message.from_user.id, message=message, state=state ) - res = await set_switch_position_mode( - tg_id=message.from_user.id, symbol=symbol, mode=mode - ) - if res == "You have an existing position, so position mode cannot be switched": - if mode == 0: - mode = 3 - else: - mode = 0 - await set_switch_position_mode( - tg_id=message.from_user.id, symbol=symbol, mode=mode - ) - if trade_mode == "Merged_Single": - trade_mode = "Both_Sides" - else: - trade_mode = "Merged_Single" - await rq.set_trade_mode(tg_id=message.from_user.id, trade_mode=trade_mode) - await set_margin_mode(tg_id=message.from_user.id, margin_mode=margin_type) - if margin_type == "ISOLATED_MARGIN": - await set_leverage_to_buy_and_sell( - tg_id=message.from_user.id, - symbol=symbol, - leverage_to_buy=str(leverage_to_buy), - leverage_to_sell=str(leverage_to_sell), - ) - else: - await set_leverage( - tg_id=message.from_user.id, symbol=symbol, leverage=str(leverage) - ) - - await rq.set_leverage(tg_id=message.from_user.id, leverage=str(leverage)) - await rq.set_leverage_to_buy_and_sell( - tg_id=message.from_user.id, - leverage_to_buy=str(leverage_to_buy), - leverage_to_sell=str(leverage_to_sell), + await set_leverage( + tg_id=message.from_user.id, symbol=symbol, leverage=str(max_leverage) ) - await rq.set_limit_price(tg_id=message.from_user.id, limit_price=0) + + await rq.set_leverage(tg_id=message.from_user.id, leverage=str(max_leverage)) + risk_percent = 100 / safe_float(max_leverage) + await rq.set_stop_loss_percent( + tg_id=message.from_user.id, stop_loss_percent=risk_percent) await rq.set_trigger_price(tg_id=message.from_user.id, trigger_price=0) + await rq.set_order_quantity(tg_id=message.from_user.id, order_quantity=1.0) await state.clear() except Exception as e: From 42c4660fe32d3adb263a80c22cfba58dc8ecb9bd Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 13:31:52 +0500 Subject: [PATCH 14/43] The price of the trading pair has been removed, and the trade cancellation button has been removed. The text has been corrected --- app/bybit/profile_bybit.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/bybit/profile_bybit.py b/app/bybit/profile_bybit.py index 3ec82a7..e930a32 100644 --- a/app/bybit/profile_bybit.py +++ b/app/bybit/profile_bybit.py @@ -6,7 +6,6 @@ from aiogram.types import Message import app.telegram.keyboards.inline as kbi import database.request as rq from app.bybit.get_functions.get_balance import get_balance -from app.bybit.get_functions.get_tickers import get_tickers from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG logging.config.dictConfig(LOGGING_CONFIG) @@ -22,17 +21,14 @@ async def user_profile_bybit(tg_id: int, message: Message, state: FSMContext) -> if wallet: balance = wallet.get("totalWalletBalance", "0") symbol = await rq.get_user_symbol(tg_id=tg_id) - get_tickers_info = await get_tickers(tg_id=tg_id, symbol=symbol) - price_symbol = get_tickers_info.get("lastPrice") or 0 await message.answer( - text=f"💎Ваш профиль Bybit:\n\n" + text=f"💎Ваш профиль:\n\n" f"⚖️ Баланс: {float(balance):,.2f} USD\n" - f"📊Торговая пара: {symbol}\n" - f"$$$ Цена: {float(price_symbol):,.4f}\n\n" + f"📊Торговая пара: {symbol}\n\n" f"Краткая инструкция:\n" f"1. Укажите торговую пару (например: BTCUSDT).\n" f"2. В настройках выставьте все необходимые параметры.\n" - f"3. Нажмите кнопку 'Начать торговлю' и выберите режим торговли.\n", + f"3. Нажмите кнопку 'Начать торговлю'.\n", reply_markup=kbi.main_menu, ) else: From fc8ab19ae965a49db3337a867efd087056a613aa Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 14:14:46 +0500 Subject: [PATCH 15/43] Fixed the budget calculation function --- app/bybit/web_socket.py | 4 +++- app/helper_functions.py | 11 +++-------- app/telegram/handlers/settings.py | 19 +------------------ app/telegram/keyboards/reply.py | 6 ++++-- 4 files changed, 11 insertions(+), 29 deletions(-) diff --git a/app/bybit/web_socket.py b/app/bybit/web_socket.py index b89fb6e..4c3579f 100644 --- a/app/bybit/web_socket.py +++ b/app/bybit/web_socket.py @@ -46,7 +46,9 @@ class WebSocketBot: self.user_sockets.clear() self.user_messages.clear() self.user_keys.clear() - logger.info("Closed old websocket for user %s due to key change", tg_id) + logger.info( + "Closed old websocket for user %s due to key change", tg_id + ) success = await self.try_connect_user(api_key, api_secret, tg_id) if success: diff --git a/app/helper_functions.py b/app/helper_functions.py index 04db1ce..07fd74d 100644 --- a/app/helper_functions.py +++ b/app/helper_functions.py @@ -158,7 +158,7 @@ async def get_liquidation_price( async def calculate_total_budget( - quantity, martingale_factor, max_steps, commission_fee_percent + quantity, martingale_factor, max_steps ) -> float: """ Calculate the total budget for a series of trading steps. @@ -167,7 +167,6 @@ async def calculate_total_budget( quantity (float): The initial quantity of the asset. martingale_factor (float): The factor by which the quantity is multiplied for each step. max_steps (int): The maximum number of trading steps. - commission_fee_percent (float): The commission fee percentage. Returns: float: The total budget for the series of trading steps. @@ -175,12 +174,8 @@ async def calculate_total_budget( total = 0 for step in range(max_steps): set_quantity = quantity * (martingale_factor**step) - if commission_fee_percent == 0: - # Commission fee is not added to the position size - r_quantity = set_quantity - else: - # Commission fee is added to the position size - r_quantity = set_quantity * (1 + 2 * commission_fee_percent) + + r_quantity = set_quantity total += r_quantity return total diff --git a/app/telegram/handlers/settings.py b/app/telegram/handlers/settings.py index ed9a914..27487d4 100644 --- a/app/telegram/handlers/settings.py +++ b/app/telegram/handlers/settings.py @@ -6,7 +6,6 @@ from aiogram.types import CallbackQuery import app.telegram.keyboards.inline as kbi import database.request as rq -from app.bybit import get_bybit_client from app.helper_functions import calculate_total_budget, safe_float from logger_helper.logger_helper import LOGGING_CONFIG @@ -26,7 +25,6 @@ async def additional_settings(callback_query: CallbackQuery, state: FSMContext) try: await state.clear() tg_id = callback_query.from_user.id - symbol = await rq.get_user_symbol(tg_id=tg_id) additional_data = await rq.get_user_additional_settings(tg_id=tg_id) if not additional_data: @@ -64,30 +62,15 @@ async def additional_settings(callback_query: CallbackQuery, state: FSMContext) max_bets = additional_data.max_bets_in_series quantity = f(additional_data.order_quantity) trigger_price = f(additional_data.trigger_price) or 0 - risk_management_data = await rq.get_user_risk_management(tg_id=tg_id) - commission_fee = risk_management_data.commission_fee - client = await get_bybit_client(tg_id=tg_id) - fee_info = client.get_fee_rates(category="linear", symbol=symbol) - - if commission_fee == "Yes_commission_fee": - commission_fee_percent = safe_float( - fee_info["result"]["list"][0]["takerFeeRate"] - ) - - else: - commission_fee_percent = 0.0 switch_side_mode = "" if trade_mode == "Switch": switch_side_mode = f"- Направление первой сделки: {switch_side}\n" - quantity_price = quantity * trigger_price - total_commission = quantity_price * commission_fee_percent total_budget = await calculate_total_budget( quantity=quantity, martingale_factor=martingale, max_steps=max_bets, - commission_fee_percent=total_commission, ) text = ( f"Основные настройки:\n\n" @@ -99,7 +82,7 @@ async def additional_settings(callback_query: CallbackQuery, state: FSMContext) f"- Коэффициент мартингейла: {martingale:.2f}\n" f"- Триггер цена: {trigger_price:.4f} USDT\n" f"- Максимальное кол-во ставок в серии: {max_bets}\n\n" - f"- Бюджет серии: {total_budget:.4f} USDT\n" + f"- Бюджет серии: {total_budget:.2f} USDT\n" ) keyboard = kbi.get_additional_settings_keyboard(mode=trade_mode) diff --git a/app/telegram/keyboards/reply.py b/app/telegram/keyboards/reply.py index fed4241..f4b8716 100644 --- a/app/telegram/keyboards/reply.py +++ b/app/telegram/keyboards/reply.py @@ -1,8 +1,10 @@ from aiogram.types import KeyboardButton, ReplyKeyboardMarkup profile = ReplyKeyboardMarkup( - keyboard=[[KeyboardButton(text="Панель Bybit"), KeyboardButton(text="Профиль")], - [KeyboardButton(text="Подключить платформу Bybit")]], + keyboard=[ + [KeyboardButton(text="Панель Bybit"), KeyboardButton(text="Профиль")], + [KeyboardButton(text="Подключить платформу Bybit")], + ], resize_keyboard=True, one_time_keyboard=True, input_field_placeholder="Выберите пункт меню...", From 7901af86af40b8ec0fe35224cc557a5cfb8d5b58 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:21:24 +0700 Subject: [PATCH 16/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/env.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alembic/env.py | 90 -------------------------------------------------- 1 file changed, 90 deletions(-) delete mode 100644 alembic/env.py diff --git a/alembic/env.py b/alembic/env.py deleted file mode 100644 index cadad9c..0000000 --- a/alembic/env.py +++ /dev/null @@ -1,90 +0,0 @@ -import asyncio -from logging.config import fileConfig - -from sqlalchemy import pool -from sqlalchemy.engine import Connection -from sqlalchemy.ext.asyncio import async_engine_from_config - -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.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: - 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 -target_metadata = Base.metadata - -# 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( - url=DATABASE_URL, - target_metadata=target_metadata, - literal_binds=True, - dialect_opts={"paramstyle": "named"}, - ) - - with context.begin_transaction(): - context.run_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( - config.get_section(config.config_ini_section, {}), - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) - - async with connectable.connect() as connection: - await connection.run_sync(do_run_migrations) - - await connectable.dispose() - - -def run_migrations_online() -> None: - """Run migrations in 'online' mode.""" - - asyncio.run(run_async_migrations()) - - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() From 1ec1f1784de10aabb5cf5d48b33f22a3b694b072 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:21:33 +0700 Subject: [PATCH 17/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/script.py.mako?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alembic/script.py.mako | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 alembic/script.py.mako diff --git a/alembic/script.py.mako b/alembic/script.py.mako deleted file mode 100644 index 1101630..0000000 --- a/alembic/script.py.mako +++ /dev/null @@ -1,28 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision | comma,n} -Create Date: ${create_date} - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -# revision identifiers, used by Alembic. -revision: str = ${repr(up_revision)} -down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)} -branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} -depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} - - -def upgrade() -> None: - """Upgrade schema.""" - ${upgrades if upgrades else "pass"} - - -def downgrade() -> None: - """Downgrade schema.""" - ${downgrades if downgrades else "pass"} From a58ebe6a46888a17c939b385e142adbce2b15bbc Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:21:38 +0700 Subject: [PATCH 18/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alembic/README | 1 - 1 file changed, 1 deletion(-) delete mode 100644 alembic/README diff --git a/alembic/README b/alembic/README deleted file mode 100644 index 98e4f9c..0000000 --- a/alembic/README +++ /dev/null @@ -1 +0,0 @@ -Generic single-database configuration. \ No newline at end of file From aab05994ce3ca31071e640a8f0b2fdae1945fae4 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:08 +0700 Subject: [PATCH 19/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/09db71875980=5Fupdated=5Fuser=5Fdeals?= =?UTF-8?q?=5Ftable.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../09db71875980_updated_user_deals_table.py | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 alembic/versions/09db71875980_updated_user_deals_table.py diff --git a/alembic/versions/09db71875980_updated_user_deals_table.py b/alembic/versions/09db71875980_updated_user_deals_table.py deleted file mode 100644 index 017638f..0000000 --- a/alembic/versions/09db71875980_updated_user_deals_table.py +++ /dev/null @@ -1,36 +0,0 @@ -"""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 ### From 3f43d426517e03242b1c776b7700826fea1da740 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:13 +0700 Subject: [PATCH 20/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/0eed68eddcdb=5Fadded=5Fconditional=5F?= =?UTF-8?q?order=5Ftype.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ed68eddcdb_added_conditional_order_type.py | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 alembic/versions/0eed68eddcdb_added_conditional_order_type.py diff --git a/alembic/versions/0eed68eddcdb_added_conditional_order_type.py b/alembic/versions/0eed68eddcdb_added_conditional_order_type.py deleted file mode 100644 index 0353cd1..0000000 --- a/alembic/versions/0eed68eddcdb_added_conditional_order_type.py +++ /dev/null @@ -1,40 +0,0 @@ -"""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 ### From 21a93d47d48716d11ce2973734bcdb903a4e0ad4 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:18 +0700 Subject: [PATCH 21/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/2b9572b49ecd=5Fadded=5Fside=5Ffor=5Fu?= =?UTF-8?q?ser=5Fauto=5Ftrading.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...b49ecd_added_side_for_user_auto_trading.py | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 alembic/versions/2b9572b49ecd_added_side_for_user_auto_trading.py diff --git a/alembic/versions/2b9572b49ecd_added_side_for_user_auto_trading.py b/alembic/versions/2b9572b49ecd_added_side_for_user_auto_trading.py deleted file mode 100644 index cfaef08..0000000 --- a/alembic/versions/2b9572b49ecd_added_side_for_user_auto_trading.py +++ /dev/null @@ -1,34 +0,0 @@ -"""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 ### From 44c4fde0369e028fb0ceb035359d7c93d876c304 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:21 +0700 Subject: [PATCH 22/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/45977e9d8558=5Fupdated=5Forder=5Fquan?= =?UTF-8?q?tity.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../45977e9d8558_updated_order_quantity.py | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 alembic/versions/45977e9d8558_updated_order_quantity.py diff --git a/alembic/versions/45977e9d8558_updated_order_quantity.py b/alembic/versions/45977e9d8558_updated_order_quantity.py deleted file mode 100644 index 0fa2f58..0000000 --- a/alembic/versions/45977e9d8558_updated_order_quantity.py +++ /dev/null @@ -1,38 +0,0 @@ -"""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 ### From 6c6f0dbb7b9cf380e5aeac67c7bc9ab0b37c27de Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:26 +0700 Subject: [PATCH 23/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/10bf073c71f9=5Fadded=5Ffee=5Ffor=5Fus?= =?UTF-8?q?er=5Fauto=5Ftrading.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...73c71f9_added_fee_for_user_auto_trading.py | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 alembic/versions/10bf073c71f9_added_fee_for_user_auto_trading.py diff --git a/alembic/versions/10bf073c71f9_added_fee_for_user_auto_trading.py b/alembic/versions/10bf073c71f9_added_fee_for_user_auto_trading.py deleted file mode 100644 index b5e559a..0000000 --- a/alembic/versions/10bf073c71f9_added_fee_for_user_auto_trading.py +++ /dev/null @@ -1,34 +0,0 @@ -"""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 ### From c0e40dc20515fed7e3ee23fd63b016b5af0dc944 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:30 +0700 Subject: [PATCH 24/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/3534adf891fc=5Fupdate=5Flast=5Fside?= =?UTF-8?q?=5Fthe=5Fconditional=5Fdata.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...c_update_last_side_the_conditional_data.py | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 alembic/versions/3534adf891fc_update_last_side_the_conditional_data.py diff --git a/alembic/versions/3534adf891fc_update_last_side_the_conditional_data.py b/alembic/versions/3534adf891fc_update_last_side_the_conditional_data.py deleted file mode 100644 index 5bbc3f8..0000000 --- a/alembic/versions/3534adf891fc_update_last_side_the_conditional_data.py +++ /dev/null @@ -1,32 +0,0 @@ -"""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 ### From 3986989dbd877afe2123ff602d5c47211604594e Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:35 +0700 Subject: [PATCH 25/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/42c66cfe8d4e=5Fupdated=5Fmartingale?= =?UTF-8?q?=5Ffactor.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../42c66cfe8d4e_updated_martingale_factor.py | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 alembic/versions/42c66cfe8d4e_updated_martingale_factor.py diff --git a/alembic/versions/42c66cfe8d4e_updated_martingale_factor.py b/alembic/versions/42c66cfe8d4e_updated_martingale_factor.py deleted file mode 100644 index 30b6920..0000000 --- a/alembic/versions/42c66cfe8d4e_updated_martingale_factor.py +++ /dev/null @@ -1,38 +0,0 @@ -"""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 ### From c89c2ad803bb3e67884d115f123bfbe1c4c80a0d Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:39 +0700 Subject: [PATCH 26/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/70094ba27e80=5Fcreate=5Fuser=5Fcondit?= =?UTF-8?q?ional=5Fsetting.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ba27e80_create_user_conditional_setting.py | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 alembic/versions/70094ba27e80_create_user_conditional_setting.py diff --git a/alembic/versions/70094ba27e80_create_user_conditional_setting.py b/alembic/versions/70094ba27e80_create_user_conditional_setting.py deleted file mode 100644 index d09e8b0..0000000 --- a/alembic/versions/70094ba27e80_create_user_conditional_setting.py +++ /dev/null @@ -1,40 +0,0 @@ -"""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 ### From 2cdfba3537f7b54125d346bc44a5fd6400b3549b Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:43 +0700 Subject: [PATCH 27/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/73a00faa4f7f=5Fadded=5Fuser=5Fauto=5F?= =?UTF-8?q?trading=5Ftable.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...00faa4f7f_added_user_auto_trading_table.py | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 alembic/versions/73a00faa4f7f_added_user_auto_trading_table.py diff --git a/alembic/versions/73a00faa4f7f_added_user_auto_trading_table.py b/alembic/versions/73a00faa4f7f_added_user_auto_trading_table.py deleted file mode 100644 index 9e0c743..0000000 --- a/alembic/versions/73a00faa4f7f_added_user_auto_trading_table.py +++ /dev/null @@ -1,42 +0,0 @@ -"""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 ### From d81a47b669a7d47c0da1e931eba03883a1df2bc8 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:48 +0700 Subject: [PATCH 28/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/77197715747c=5Fdeleted=5Fposition=5Fi?= =?UTF-8?q?dx=5Ffor=5Fuser=5Fdeals=5F.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...7c_deleted_position_idx_for_user_deals_.py | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 alembic/versions/77197715747c_deleted_position_idx_for_user_deals_.py diff --git a/alembic/versions/77197715747c_deleted_position_idx_for_user_deals_.py b/alembic/versions/77197715747c_deleted_position_idx_for_user_deals_.py deleted file mode 100644 index 0f4d2a0..0000000 --- a/alembic/versions/77197715747c_deleted_position_idx_for_user_deals_.py +++ /dev/null @@ -1,32 +0,0 @@ -"""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 ### From 8c08451d8263dab365950b2e5cbeb354461efb53 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:22:52 +0700 Subject: [PATCH 29/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/863d6215e1eb=5Fupdated=5Fdeals.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../versions/863d6215e1eb_updated_deals.py | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 alembic/versions/863d6215e1eb_updated_deals.py diff --git a/alembic/versions/863d6215e1eb_updated_deals.py b/alembic/versions/863d6215e1eb_updated_deals.py deleted file mode 100644 index 6863b0b..0000000 --- a/alembic/versions/863d6215e1eb_updated_deals.py +++ /dev/null @@ -1,32 +0,0 @@ -"""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 ### From 2df3b8b40db9a65e96ff0db4a24fd564bb7a6948 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:03 +0700 Subject: [PATCH 30/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/c710f4e2259c=5Funnecessary=5Fdata=5Fh?= =?UTF-8?q?as=5Fbeen=5Fdeleted.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2259c_unnecessary_data_has_been_deleted.py | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 alembic/versions/c710f4e2259c_unnecessary_data_has_been_deleted.py diff --git a/alembic/versions/c710f4e2259c_unnecessary_data_has_been_deleted.py b/alembic/versions/c710f4e2259c_unnecessary_data_has_been_deleted.py deleted file mode 100644 index 455e1ba..0000000 --- a/alembic/versions/c710f4e2259c_unnecessary_data_has_been_deleted.py +++ /dev/null @@ -1,79 +0,0 @@ -"""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 ### From b426eb213631b92ee242cd197b77906a2759fafb Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:07 +0700 Subject: [PATCH 31/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/968f8121104f=5Fupdated=5Fuser=5Fdeals?= =?UTF-8?q?=5Fand=5Fuser=5Fconditional=5F.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...pdated_user_deals_and_user_conditional_.py | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 alembic/versions/968f8121104f_updated_user_deals_and_user_conditional_.py diff --git a/alembic/versions/968f8121104f_updated_user_deals_and_user_conditional_.py b/alembic/versions/968f8121104f_updated_user_deals_and_user_conditional_.py deleted file mode 100644 index 77b4490..0000000 --- a/alembic/versions/968f8121104f_updated_user_deals_and_user_conditional_.py +++ /dev/null @@ -1,36 +0,0 @@ -"""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 ### From ee285523f25cdb1d7ccbd5490abafe6b0a1bc8b5 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:12 +0700 Subject: [PATCH 32/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/8f1476c68efa=5Fadded=5Fposition=5Fidx?= =?UTF-8?q?=5Ffor=5Fuser=5Fdeals=5Ftable.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...added_position_idx_for_user_deals_table.py | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 alembic/versions/8f1476c68efa_added_position_idx_for_user_deals_table.py diff --git a/alembic/versions/8f1476c68efa_added_position_idx_for_user_deals_table.py b/alembic/versions/8f1476c68efa_added_position_idx_for_user_deals_table.py deleted file mode 100644 index 2c51fb0..0000000 --- a/alembic/versions/8f1476c68efa_added_position_idx_for_user_deals_table.py +++ /dev/null @@ -1,34 +0,0 @@ -"""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 ### From 37b7b6effd3dfb21369d0fcf1d2de5ec0fd4a3b2 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:17 +0700 Subject: [PATCH 33/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/c98b9dc36d15=5Ffixed=5Fauto=5Ftrade.p?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../versions/c98b9dc36d15_fixed_auto_trade.py | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 alembic/versions/c98b9dc36d15_fixed_auto_trade.py diff --git a/alembic/versions/c98b9dc36d15_fixed_auto_trade.py b/alembic/versions/c98b9dc36d15_fixed_auto_trade.py deleted file mode 100644 index 5bd00be..0000000 --- a/alembic/versions/c98b9dc36d15_fixed_auto_trade.py +++ /dev/null @@ -1,38 +0,0 @@ -"""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 ### From 99c59be9ed52ca78b7d1e0a5a77795ceaffa8c7a Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:21 +0700 Subject: [PATCH 34/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/acbcc95de48d=5Fupdated=5Fuserdeals.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../acbcc95de48d_updated_userdeals.py | 60 ------------------- 1 file changed, 60 deletions(-) delete mode 100644 alembic/versions/acbcc95de48d_updated_userdeals.py diff --git a/alembic/versions/acbcc95de48d_updated_userdeals.py b/alembic/versions/acbcc95de48d_updated_userdeals.py deleted file mode 100644 index 1822547..0000000 --- a/alembic/versions/acbcc95de48d_updated_userdeals.py +++ /dev/null @@ -1,60 +0,0 @@ -"""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 ### From 3db001bd1932a0101b79b122443b62093765f056 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:26 +0700 Subject: [PATCH 35/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/ccdc5764eb4f=5Fadded=5Fuserdeals.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../versions/ccdc5764eb4f_added_userdeals.py | 50 ------------------- 1 file changed, 50 deletions(-) delete mode 100644 alembic/versions/ccdc5764eb4f_added_userdeals.py diff --git a/alembic/versions/ccdc5764eb4f_added_userdeals.py b/alembic/versions/ccdc5764eb4f_added_userdeals.py deleted file mode 100644 index 4d697c3..0000000 --- a/alembic/versions/ccdc5764eb4f_added_userdeals.py +++ /dev/null @@ -1,50 +0,0 @@ -"""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 ### From 8071f8c896353643949dc79f874f0ad315f1b1f6 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:30 +0700 Subject: [PATCH 36/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/d3c85bad8c98=5Fadded=5Flimit=5Fand=5F?= =?UTF-8?q?trigger=5Fprice=5Ffor=5Fuser=5F.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...added_limit_and_trigger_price_for_user_.py | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 alembic/versions/d3c85bad8c98_added_limit_and_trigger_price_for_user_.py diff --git a/alembic/versions/d3c85bad8c98_added_limit_and_trigger_price_for_user_.py b/alembic/versions/d3c85bad8c98_added_limit_and_trigger_price_for_user_.py deleted file mode 100644 index c2fc35a..0000000 --- a/alembic/versions/d3c85bad8c98_added_limit_and_trigger_price_for_user_.py +++ /dev/null @@ -1,34 +0,0 @@ -"""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 ### From fab8ff50403b00f82d4638fd87a69e0a23c129f3 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:35 +0700 Subject: [PATCH 37/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/dbffe818030c=5Fadded=5Flast=5Fside=5F?= =?UTF-8?q?and=5Fauto=5Ftrading=5Ffor=5F.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...c_added_last_side_and_auto_trading_for_.py | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 alembic/versions/dbffe818030c_added_last_side_and_auto_trading_for_.py diff --git a/alembic/versions/dbffe818030c_added_last_side_and_auto_trading_for_.py b/alembic/versions/dbffe818030c_added_last_side_and_auto_trading_for_.py deleted file mode 100644 index eec8b99..0000000 --- a/alembic/versions/dbffe818030c_added_last_side_and_auto_trading_for_.py +++ /dev/null @@ -1,40 +0,0 @@ -"""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 ### From e0e4ad5d4b4cd34cb218bab864dafc51ad95fdd4 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:39 +0700 Subject: [PATCH 38/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/ef342b38e17b=5Fadded=5Ffee=5Fuser=5Fd?= =?UTF-8?q?eals.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ef342b38e17b_added_fee_user_deals.py | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 alembic/versions/ef342b38e17b_added_fee_user_deals.py diff --git a/alembic/versions/ef342b38e17b_added_fee_user_deals.py b/alembic/versions/ef342b38e17b_added_fee_user_deals.py deleted file mode 100644 index b9e3983..0000000 --- a/alembic/versions/ef342b38e17b_added_fee_user_deals.py +++ /dev/null @@ -1,32 +0,0 @@ -"""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 ### From b03d05bb758823b7a4945976232aadf46ef5ffca Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:43 +0700 Subject: [PATCH 39/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/ef38c90eed55=5Fadded=5Flast=5Fside=5F?= =?UTF-8?q?the=5Fconditional=5Fdata.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...55_added_last_side_the_conditional_data.py | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 alembic/versions/ef38c90eed55_added_last_side_the_conditional_data.py diff --git a/alembic/versions/ef38c90eed55_added_last_side_the_conditional_data.py b/alembic/versions/ef38c90eed55_added_last_side_the_conditional_data.py deleted file mode 100644 index 39eff8c..0000000 --- a/alembic/versions/ef38c90eed55_added_last_side_the_conditional_data.py +++ /dev/null @@ -1,38 +0,0 @@ -"""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 ### From eeb7f81440f7ebff0119d3a6283520cb0b0f40e7 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:47 +0700 Subject: [PATCH 40/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/f00a94ccdf01=5Fupdated=5Fdeals.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../versions/f00a94ccdf01_updated_deals.py | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 alembic/versions/f00a94ccdf01_updated_deals.py diff --git a/alembic/versions/f00a94ccdf01_updated_deals.py b/alembic/versions/f00a94ccdf01_updated_deals.py deleted file mode 100644 index 9e0309b..0000000 --- a/alembic/versions/f00a94ccdf01_updated_deals.py +++ /dev/null @@ -1,40 +0,0 @@ -"""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 ### From 0784cbb54a6f6be4880c99139a8510b0df6eedee Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:23:51 +0700 Subject: [PATCH 41/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic/versions/fd8581c0cc87=5Fupdated=5Fleverage.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../versions/fd8581c0cc87_updated_leverage.py | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 alembic/versions/fd8581c0cc87_updated_leverage.py diff --git a/alembic/versions/fd8581c0cc87_updated_leverage.py b/alembic/versions/fd8581c0cc87_updated_leverage.py deleted file mode 100644 index cc9bb37..0000000 --- a/alembic/versions/fd8581c0cc87_updated_leverage.py +++ /dev/null @@ -1,32 +0,0 @@ -"""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 ### From 630f2002d344ff232eefe509c5e013e3f46c0223 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 10 Oct 2025 16:24:01 +0700 Subject: [PATCH 42/43] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20alembic.ini?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alembic.ini | 147 ---------------------------------------------------- 1 file changed, 147 deletions(-) delete mode 100644 alembic.ini diff --git a/alembic.ini b/alembic.ini deleted file mode 100644 index 0791816..0000000 --- a/alembic.ini +++ /dev/null @@ -1,147 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# path to migration scripts. -# this is typically a path given in POSIX (e.g. forward slashes) -# format, relative to the token %(here)s which refers to the location of this -# ini file -script_location = %(here)s/alembic - -# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s -# Uncomment the line below if you want the files to be prepended with date and time -# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file -# for all available tokens -# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s - -# sys.path path, will be prepended to sys.path if present. -# defaults to the current working directory. for multiple paths, the path separator -# is defined by "path_separator" below. -prepend_sys_path = . - - -# timezone to use when rendering the date within the migration file -# as well as the filename. -# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. -# Any required deps can installed by adding `alembic[tz]` to the pip requirements -# string value is passed to ZoneInfo() -# leave blank for localtime -# timezone = - -# max length of characters to apply to the "slug" field -# truncate_slug_length = 40 - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - -# set to 'true' to allow .pyc and .pyo files without -# a source .py file to be detected as revisions in the -# versions/ directory -# sourceless = false - -# version location specification; This defaults -# to /versions. When using multiple version -# directories, initial revisions must be specified with --version-path. -# The path separator used here should be the separator specified by "path_separator" -# below. -# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions - -# path_separator; This indicates what character is used to split lists of file -# paths, including version_locations and prepend_sys_path within configparser -# files such as alembic.ini. -# The default rendered in new alembic.ini files is "os", which uses os.pathsep -# to provide os-dependent path splitting. -# -# Note that in order to support legacy alembic.ini files, this default does NOT -# take place if path_separator is not present in alembic.ini. If this -# option is omitted entirely, fallback logic is as follows: -# -# 1. Parsing of the version_locations option falls back to using the legacy -# "version_path_separator" key, which if absent then falls back to the legacy -# behavior of splitting on spaces and/or commas. -# 2. Parsing of the prepend_sys_path option falls back to the legacy -# behavior of splitting on spaces, commas, or colons. -# -# Valid values for path_separator are: -# -# path_separator = : -# path_separator = ; -# path_separator = space -# path_separator = newline -# -# Use os.pathsep. Default configuration used for new projects. -path_separator = os - -# set to 'true' to search source files recursively -# in each "version_locations" directory -# new in Alembic version 1.10 -# recursive_version_locations = false - -# the output encoding used when revision files -# are written from script.py.mako -# output_encoding = utf-8 - -# 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 -# file. -sqlalchemy.url = driver://user:pass@localhost/dbname - - -[post_write_hooks] -# post_write_hooks defines scripts or Python functions that are run -# on newly generated revision scripts. See the documentation for further -# detail and examples - -# format using "black" - use the console_scripts runner, against the "black" entrypoint -# hooks = black -# black.type = console_scripts -# black.entrypoint = black -# black.options = -l 79 REVISION_SCRIPT_FILENAME - -# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module -# hooks = ruff -# ruff.type = module -# ruff.module = ruff -# ruff.options = check --fix REVISION_SCRIPT_FILENAME - -# Alternatively, use the exec runner to execute a binary found on your PATH -# hooks = ruff -# ruff.type = exec -# ruff.executable = ruff -# ruff.options = check --fix REVISION_SCRIPT_FILENAME - -# Logging configuration. This is also consumed by the user-maintained -# env.py script only. -[loggers] -keys = root,sqlalchemy,alembic - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARNING -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARNING -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S From d0971f59b4f2bc96c67cb890cb411ec8cfd4f1ef Mon Sep 17 00:00:00 2001 From: algizn97 Date: Fri, 10 Oct 2025 14:42:47 +0500 Subject: [PATCH 43/43] Added environments --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 0ff2590..91631b6 100644 --- a/.gitignore +++ b/.gitignore @@ -146,6 +146,8 @@ myenv ENV/ env.bak/ venv.bak/ +alembic.ini +/alembic # Spyder project settings .spyderproject .spyproject