2
0
forked from kodorvan/stcs
Files
stcs/app/telegram/handlers/main_settings/additional_settings.py
algizn97 1508629727 Fixed
2025-10-03 14:19:18 +05:00

1555 lines
62 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging.config
from aiogram import F, Router
from aiogram.fsm.context import FSMContext
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_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.telegram.states.states import AdditionalSettingsState
from logger_helper.logger_helper import LOGGING_CONFIG
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("additional_settings")
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
) -> None:
"""
Handles the 'trade_mode' callback query.
Clears the current FSM state, edits the message text to display trade mode options
with explanation for 'Long' and 'Short' modes, 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.trade_mode,
)
logger.debug(
"Command trade_mode processed successfully for user: %s",
callback_query.from_user.id,
)
except Exception as e:
await callback_query.answer(
text="Произошла ошибка. Пожалуйста, попробуйте позже."
)
logger.error(
"Error processing command trade_mode for user %s: %s",
callback_query.from_user.id,
e,
)
@router_additional_settings.callback_query(
lambda c: c.data == "Merged_Single" or c.data == "Both_Sides"
)
async def trade_mode(callback_query: CallbackQuery, state: FSMContext) -> None:
"""
Handles callback queries related to trade mode selection.
Updates FSM context with selected trade mode 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 trade mode.
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)
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="Произошла ошибка при обновлении режима позиции."
)
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),
)
except Exception as e:
await callback_query.answer(text="Произошла ошибка при смене режима позиции.")
logger.error(
"Error processing set trade_mode 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
) -> None:
"""
Handles the 'margin_type' callback query.
Clears the current FSM state, edits the message text to display margin 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="Выберите тип маржи:", reply_markup=kbi.margin_type
)
logger.debug(
"Command margin_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 margin_type for user %s: %s",
callback_query.from_user.id,
e,
)
@router_additional_settings.callback_query(
lambda c: c.data == "ISOLATED_MARGIN" or c.data == "REGULAR_MARGIN"
)
async def set_margin_type(callback_query: CallbackQuery, state: FSMContext) -> None:
"""
Handles callback queries starting with 'Isolated' or 'Cross'.
Updates FSM context with selected margin 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 margin type.
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)
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)
bybit_margin_mode = callback_query.data
response = await set_margin_mode(
tg_id=callback_query.from_user.id, margin_mode=bybit_margin_mode
)
if not response:
await callback_query.answer(
text="Произошла ошибка при установке типа маржи"
)
return
req = await rq.set_margin_type(
tg_id=callback_query.from_user.id, margin_type=callback_query.data
)
if not req:
await callback_query.answer(
text="Произошла ошибка при установке типа маржи"
)
return
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="Произошла ошибка при установке типа маржи"
)
except Exception as e:
await callback_query.answer(text="Произошла ошибка при установке типа маржи")
logger.error(
"Error processing command margin_type for user %s: %s",
callback_query.from_user.id,
e,
)
finally:
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:
"""
Handles the 'trigger_price' callback query.
Clears the current FSM state, edits the message text to prompt for the trigger 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.trigger_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_additional_settings
)
await state.update_data(prompt_message_id=msg.message_id)
logger.debug(
"Command trigger_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 trigger_price for user %s: %s",
callback_query.from_user.id,
e,
)
@router_additional_settings.message(AdditionalSettingsState.trigger_price_state)
async def set_trigger_price(message: Message, state: FSMContext) -> None:
"""
Handles user input for setting the trigger price.
Updates FSM context with the selected trigger 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 trigger 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
trigger_price_value = message.text
if not is_number(trigger_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,
trigger_price_value,
)
return
req = await rq.set_trigger_price(
tg_id=message.from_user.id, trigger_price=safe_float(trigger_price_value)
)
if req:
await message.answer(
text=f"Цена триггера установлена на: {trigger_price_value}",
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 set_trigger_price for user %s: %s",
message.from_user.id,
e,
)
@router_additional_settings.callback_query(F.data == "leverage")
async def leverage_to_buy(callback_query: CallbackQuery, state: FSMContext) -> None:
"""
Handles the 'leverage' callback query.
Clears the current FSM state, edits the message text to display the leverage 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.answer()
additional_settings = await rq.get_user_additional_settings(
callback_query.from_user.id
)
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)
logger.debug(
"Command leverage processed successfully for user: %s",
callback_query.from_user.id,
)
except Exception as e:
await callback_query.answer(
text="Произошла ошибка. Пожалуйста, попробуйте позже."
)
logger.error(
"Error processing command leverage for user %s: %s",
callback_query.from_user.id,
e,
)
@router_additional_settings.message(AdditionalSettingsState.leverage_state)
async def leverage(message: Message, state: FSMContext) -> None:
"""
Handles user input for setting the leverage.
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 = message.text
tg_id = message.from_user.id
if not is_number(get_leverage):
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,
)
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
)
max_leverage = (
safe_float(instruments_info.get("leverageFilter").get("maxLeverage"))
or 100
)
if leverage_float > max_leverage or leverage_float < min_leverage:
await message.answer(
text=f"Кредитное плечо должно быть от {min_leverage} до {max_leverage}.",
reply_markup=kbi.back_to_additional_settings,
)
logger.info(
"User %s input invalid (out of range): %s, %s, %s: %s",
message.from_user.id,
symbol,
min_leverage,
max_leverage,
leverage_float,
)
return
else:
await message.answer(
text="Произошла ошибка. Пожалуйста, попробуйте позже.",
reply_markup=kbi.back_to_additional_settings,
)
response = await set_leverage(
tg_id=message.from_user.id, symbol=symbol, leverage=str(leverage_float)
)
if not response:
await message.answer(
text="Невозможно установить кредитное плечо для текущего режима торговли.",
reply_markup=kbi.back_to_additional_settings,
)
return
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:
await message.answer(
text=f"Кредитное плечо успешно установлено на {leverage_float}.",
reply_markup=kbi.back_to_additional_settings,
)
logger.info(
"User %s set leverage: %s", message.from_user.id, leverage_float
)
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 leverage for user %s: %s", message.from_user.id, e
)
@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:
"""
Handles the 'order_quantity' callback query.
Clears the current FSM state, edits the message text to display the order quantity 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(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}.",
reply_markup=kbi.back_to_additional_settings,
)
await state.update_data(prompt_message_id=msg.message_id)
logger.debug(
"Command order_quantity 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_quantity for user %s: %s",
callback_query.from_user.id,
e,
)
@router_additional_settings.message(AdditionalSettingsState.quantity_state)
async def set_order_quantity(message: Message, state: FSMContext) -> None:
"""
Handles user input for setting the order quantity.
Updates FSM context with the selected order quantity 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 order quantity.
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
order_quantity_value = message.text
if not is_number(order_quantity_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,
order_quantity_value,
)
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
)
if req:
await message.answer(
text=f"Количество ордера установлено на {message.text}.",
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_order_quantity: %s", e)
@router_additional_settings.callback_query(F.data == "martingale_factor")
async def martingale_factor(callback_query: CallbackQuery, state: FSMContext) -> None:
"""
Handles the 'martingale_factor' callback query.
Clears the current FSM state, edits the message text to display the martingale factor 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(AdditionalSettingsState.martingale_factor_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)
logger.debug(
"Command martingale_factor processed successfully for user: %s",
callback_query.from_user.id,
)
except Exception as e:
await callback_query.answer(
text="Произошла ошибка. Пожалуйста, попробуйте позже."
)
logger.error(
"Error processing command martingale_factor for user %s: %s",
callback_query.from_user.id,
e,
)
@router_additional_settings.message(AdditionalSettingsState.martingale_factor_state)
async def set_martingale_factor(message: Message, state: FSMContext) -> None:
"""
Handles user input for setting the martingale factor.
Updates FSM context with the selected martingale factor 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 martingale factor.
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
martingale_factor_value = message.text
if not is_number(martingale_factor_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,
martingale_factor_value,
)
return
martingale_factor_value_float = safe_float(martingale_factor_value)
req = await rq.set_martingale_factor(
tg_id=message.from_user.id, martingale_factor=martingale_factor_value_float
)
if req:
await message.answer(
text=f"Коэффициент мартингейла установлен на {message.text}.",
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_martingale_factor: %s", e)
@router_additional_settings.callback_query(F.data == "max_bets_in_series")
async def max_bets_in_series(callback_query: CallbackQuery, state: FSMContext) -> None:
"""
Handles the 'max_bets_in_series' callback query.
Clears the current FSM state, edits the message text to display the max bets in series 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(AdditionalSettingsState.max_bets_in_series_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)
logger.debug(
"Command max_bets_in_series 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_bets_in_series for user %s: %s",
callback_query.from_user.id,
e,
)
@router_additional_settings.message(AdditionalSettingsState.max_bets_in_series_state)
async def set_max_bets_in_series(message: Message, state: FSMContext) -> None:
"""
Handles user input for setting the max bets in series.
Updates FSM context with the selected max steps 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 max bets in series.
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_bets_in_series_value = message.text
if not is_int(max_bets_in_series_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,
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)
)
if req:
await message.answer(
text=f"Максимальное количество шагов установлено на {message.text}.",
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_max_bets_in_series for user %s: %s",
message.from_user.id,
e,
)