Merge pull request 'разъебаться по полной' (#21) from Alex/stcs:devel into stable
Reviewed-on: #21
This commit is contained in:
@@ -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 ###
|
||||||
@@ -28,11 +28,9 @@ async def start_trading_cycle(
|
|||||||
symbol = await rq.get_user_symbol(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)
|
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)
|
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
|
trade_mode = additional_data.trade_mode
|
||||||
switch_side = additional_data.switch_side
|
switch_side = additional_data.switch_side
|
||||||
|
side= additional_data.side
|
||||||
margin_type = additional_data.margin_type
|
margin_type = additional_data.margin_type
|
||||||
leverage = additional_data.leverage
|
leverage = additional_data.leverage
|
||||||
order_quantity = additional_data.order_quantity
|
order_quantity = additional_data.order_quantity
|
||||||
@@ -43,19 +41,8 @@ async def start_trading_cycle(
|
|||||||
stop_loss_percent = risk_management_data.stop_loss_percent
|
stop_loss_percent = risk_management_data.stop_loss_percent
|
||||||
total_commission = 0
|
total_commission = 0
|
||||||
|
|
||||||
get_side = "Buy"
|
|
||||||
|
|
||||||
if user_deals_data:
|
|
||||||
get_side = user_deals_data.last_side or "Buy"
|
|
||||||
|
|
||||||
if trade_mode == "Switch":
|
if trade_mode == "Switch":
|
||||||
if switch_side == "По направлению":
|
side = side
|
||||||
side = get_side
|
|
||||||
else:
|
|
||||||
if get_side == "Buy":
|
|
||||||
side = "Sell"
|
|
||||||
else:
|
|
||||||
side = "Buy"
|
|
||||||
else:
|
else:
|
||||||
if trade_mode == "Long":
|
if trade_mode == "Long":
|
||||||
side = "Buy"
|
side = "Buy"
|
||||||
|
|||||||
@@ -105,10 +105,10 @@ async def trade_mode(callback_query: CallbackQuery, state: FSMContext) -> None:
|
|||||||
await state.clear()
|
await state.clear()
|
||||||
|
|
||||||
|
|
||||||
@router_additional_settings.callback_query(F.data == "switch_side_start")
|
@router_additional_settings.callback_query(F.data == "switch_side_second")
|
||||||
async def switch_side_start(callback_query: CallbackQuery, state: FSMContext) -> None:
|
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,
|
Clears the current FSM state, edits the message text to display the switch side start message,
|
||||||
and shows an inline keyboard for selection.
|
and shows an inline keyboard for selection.
|
||||||
@@ -123,13 +123,13 @@ async def switch_side_start(callback_query: CallbackQuery, state: FSMContext) ->
|
|||||||
try:
|
try:
|
||||||
await state.clear()
|
await state.clear()
|
||||||
await callback_query.message.edit_text(
|
await callback_query.message.edit_text(
|
||||||
text="Выберите направление первой сделки серии:\n\n"
|
text="Выберите направление первой сделки последующих серии:\n\n"
|
||||||
"По направлению - сделка открывается в направлении последней сделки предыдущей серии.\n"
|
"По направлению - сделка открывается в направлении последней сделки предыдущей серии.\n"
|
||||||
"Противоположно - сделка открывается в противоположном направлении последней сделки предыдущей серии.\n",
|
"Противоположно - сделка открывается в противоположном направлении последней сделки предыдущей серии.\n",
|
||||||
reply_markup=kbi.switch_side,
|
reply_markup=kbi.switch_side,
|
||||||
)
|
)
|
||||||
logger.debug(
|
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,
|
callback_query.from_user.id,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -137,7 +137,7 @@ async def switch_side_start(callback_query: CallbackQuery, state: FSMContext) ->
|
|||||||
text="Произошла ошибка. Пожалуйста, попробуйте позже."
|
text="Произошла ошибка. Пожалуйста, попробуйте позже."
|
||||||
)
|
)
|
||||||
logger.error(
|
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,
|
callback_query.from_user.id,
|
||||||
e,
|
e,
|
||||||
)
|
)
|
||||||
@@ -193,6 +193,89 @@ async def switch_side_handler(callback_query: CallbackQuery, state: FSMContext)
|
|||||||
await state.clear()
|
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")
|
@router_additional_settings.callback_query(F.data == "margin_type")
|
||||||
async def settings_for_margin_type(
|
async def settings_for_margin_type(
|
||||||
callback_query: CallbackQuery, state: FSMContext
|
callback_query: CallbackQuery, state: FSMContext
|
||||||
|
|||||||
@@ -62,10 +62,19 @@ async def additional_settings(callback_query: CallbackQuery, state: FSMContext)
|
|||||||
max_bets = additional_data.max_bets_in_series
|
max_bets = additional_data.max_bets_in_series
|
||||||
quantity = f(additional_data.order_quantity)
|
quantity = f(additional_data.order_quantity)
|
||||||
trigger_price = f(additional_data.trigger_price) or 0
|
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 = ""
|
switch_side_mode = ""
|
||||||
|
side = ""
|
||||||
if trade_mode == "Switch":
|
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(
|
total_budget = await calculate_total_budget(
|
||||||
quantity=quantity,
|
quantity=quantity,
|
||||||
@@ -75,6 +84,7 @@ async def additional_settings(callback_query: CallbackQuery, state: FSMContext)
|
|||||||
text = (
|
text = (
|
||||||
f"Основные настройки:\n\n"
|
f"Основные настройки:\n\n"
|
||||||
f"- Режим торговли: {trade_mode_rus}\n"
|
f"- Режим торговли: {trade_mode_rus}\n"
|
||||||
|
f"{side}"
|
||||||
f"{switch_side_mode}"
|
f"{switch_side_mode}"
|
||||||
f"- Тип маржи: {margin_type_rus}\n"
|
f"- Тип маржи: {margin_type_rus}\n"
|
||||||
f"- Размер кредитного плеча: {leverage:.2f}\n"
|
f"- Размер кредитного плеча: {leverage:.2f}\n"
|
||||||
|
|||||||
@@ -94,7 +94,10 @@ def get_additional_settings_keyboard(mode: str
|
|||||||
|
|
||||||
if mode == "Switch":
|
if mode == "Switch":
|
||||||
buttons.append(
|
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(
|
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(
|
margin_type = InlineKeyboardMarkup(
|
||||||
inline_keyboard=[
|
inline_keyboard=[
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ class UserAdditionalSettings(Base):
|
|||||||
nullable=False, unique=True)
|
nullable=False, unique=True)
|
||||||
trade_mode = Column(String, nullable=False, default="Merged_Single")
|
trade_mode = Column(String, nullable=False, default="Merged_Single")
|
||||||
switch_side = Column(String, nullable=False, default="По направлению")
|
switch_side = Column(String, nullable=False, default="По направлению")
|
||||||
|
side = Column(String, nullable=False, default="Buy")
|
||||||
trigger_price = Column(Float, nullable=False, default=0.0)
|
trigger_price = Column(Float, nullable=False, default=0.0)
|
||||||
margin_type = Column(String, nullable=False, default="ISOLATED_MARGIN")
|
margin_type = Column(String, nullable=False, default="ISOLATED_MARGIN")
|
||||||
leverage = Column(String, nullable=False, default="10")
|
leverage = Column(String, nullable=False, default="10")
|
||||||
|
|||||||
@@ -358,6 +358,45 @@ async def set_switch_side(tg_id: int, switch_side: str) -> bool:
|
|||||||
return False
|
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:
|
async def set_leverage(tg_id: int, leverage: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Set leverage for a user in the database.
|
Set leverage for a user in the database.
|
||||||
|
|||||||
Reference in New Issue
Block a user