Merge pull request 'разъебаться по полной' (#21) from Alex/stcs:devel into stable

Reviewed-on: #21
This commit is contained in:
2025-10-25 21:05:58 +07:00
7 changed files with 193 additions and 23 deletions

View File

@@ -0,0 +1,34 @@
"""Added column side for additional_setiings
Revision ID: e5d612e44563
Revises: fbf4e3658310
Create Date: 2025-10-25 18:25:52.746250
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'e5d612e44563'
down_revision: Union[str, Sequence[str], None] = 'fbf4e3658310'
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('side', sa.String(), nullable=False, server_default='')
)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('user_additional_settings', 'side')
# ### end Alembic commands ###

View File

@@ -28,11 +28,9 @@ async def start_trading_cycle(
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)
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
side= additional_data.side
margin_type = additional_data.margin_type
leverage = additional_data.leverage
order_quantity = additional_data.order_quantity
@@ -43,19 +41,8 @@ async def start_trading_cycle(
stop_loss_percent = risk_management_data.stop_loss_percent
total_commission = 0
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"
side = side
else:
if trade_mode == "Long":
side = "Buy"

View File

@@ -105,10 +105,10 @@ 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:
@router_additional_settings.callback_query(F.data == "switch_side_second")
async def switch_side_second(callback_query: CallbackQuery, state: FSMContext) -> None:
"""
Handles the 'switch_side_start' callback query.
Handles the 'switch_side_second' 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.
@@ -123,13 +123,13 @@ async def switch_side_start(callback_query: CallbackQuery, state: FSMContext) ->
try:
await state.clear()
await callback_query.message.edit_text(
text="Выберите направление первой сделки серии:\n\n"
text="Выберите направление первой сделки последующих серии:\n\n"
"По направлению - сделка открывается в направлении последней сделки предыдущей серии.\n"
"Противоположно - сделка открывается в противоположном направлении последней сделки предыдущей серии.\n",
reply_markup=kbi.switch_side,
)
logger.debug(
"Command switch_side_start processed successfully for user: %s",
"Command switch_side_second processed successfully for user: %s",
callback_query.from_user.id,
)
except Exception as e:
@@ -137,7 +137,7 @@ async def switch_side_start(callback_query: CallbackQuery, state: FSMContext) ->
text="Произошла ошибка. Пожалуйста, попробуйте позже."
)
logger.error(
"Error processing command switch_side_start for user %s: %s",
"Error processing command switch_side_second for user %s: %s",
callback_query.from_user.id,
e,
)
@@ -193,6 +193,89 @@ async def switch_side_handler(callback_query: CallbackQuery, state: FSMContext)
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 second 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", reply_markup=kbi.side_for_switch
)
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 == "buy_switch" or c.data == "sell_switch")
async def switch_side_handler_2(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 == "sell_switch":
side = "Sell"
side_rus = "Продажа"
else:
side = "Buy"
side_rus = "Покупка"
req = await rq.set_side(
tg_id=callback_query.from_user.id, side=side
)
if not req:
await callback_query.answer(
text="Произошла ошибка при смене направления. Пожалуйста, попробуйте позже."
)
return
await callback_query.answer(text=f"Выбрано: {side_rus}")
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

View File

@@ -62,10 +62,19 @@ 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
side = additional_data.side
side_map = {
"Buy": "Лонг",
"Sell": "Шорт",
}
side_rus = side_map.get(side, side)
switch_side_mode = ""
side = ""
if trade_mode == "Switch":
switch_side_mode = f"- Направление первой сделки: {switch_side}\n"
side = f"- Направление первой сделки: {side_rus}\n"
switch_side_mode = f"- Направление первой сделки последующих серии: {switch_side}\n"
total_budget = await calculate_total_budget(
quantity=quantity,
@@ -75,6 +84,7 @@ async def additional_settings(callback_query: CallbackQuery, state: FSMContext)
text = (
f"Основные настройки:\n\n"
f"- Режим торговли: {trade_mode_rus}\n"
f"{side}"
f"{switch_side_mode}"
f"- Тип маржи: {margin_type_rus}\n"
f"- Размер кредитного плеча: {leverage:.2f}\n"

View File

@@ -94,7 +94,10 @@ def get_additional_settings_keyboard(mode: str
if mode == "Switch":
buttons.append(
[InlineKeyboardButton(text="Направление первой сделки", callback_data="switch_side_start")]
[InlineKeyboardButton(text="Направление первой сделки первой серии", callback_data="switch_side_start")]
)
buttons.append(
[InlineKeyboardButton(text="Направление первой сделки последующих серии", callback_data="switch_side_second")]
)
buttons.append(
@@ -148,6 +151,19 @@ switch_side = InlineKeyboardMarkup(
]
)
side_for_switch = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(text="Лонг", callback_data="buy_switch"),
InlineKeyboardButton(text="Шорт", callback_data="sell_switch"),
],
[
InlineKeyboardButton(text="Назад", callback_data="additional_settings"),
InlineKeyboardButton(text="На главную", callback_data="profile_bybit"),
],
]
)
margin_type = InlineKeyboardMarkup(
inline_keyboard=[
[

View File

@@ -92,6 +92,7 @@ class UserAdditionalSettings(Base):
nullable=False, unique=True)
trade_mode = Column(String, nullable=False, default="Merged_Single")
switch_side = Column(String, nullable=False, default="По направлению")
side = Column(String, nullable=False, default="Buy")
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")

View File

@@ -358,6 +358,45 @@ async def set_switch_side(tg_id: int, switch_side: str) -> bool:
return False
async def set_side(tg_id: int, side: str) -> bool:
"""
Set side for a user in the database.
:param tg_id: Telegram user ID
:param side: "BUY" or "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.side = side
else:
# Creating new record
user_additional_settings = UserAdditionalSettings(
side=side,
user=user,
)
session.add(user_additional_settings)
await session.commit()
logger.info("User 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 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.