07.4.3.14 — Auto Trading UI. Realistic Pricing & Debug Live Tools

This commit is contained in:
2026-05-09 01:34:46 +03:00
parent ee78f9774a
commit df76490783
15 changed files with 2161 additions and 464 deletions

View File

@@ -24,30 +24,45 @@ class AutoRiskStates(StatesGroup):
waiting_max_loss = State()
def _format_number(value: float | int | None) -> str:
if value is None:
return ""
number = float(value)
if abs(number - round(number)) < 1e-9:
return f"{int(round(number))}"
return f"{number:.2f}".rstrip("0").rstrip(".")
def _format_percent(value: float | None) -> str:
if value is None:
return "off"
return f"🟢 {value:g}%"
return "off"
return f"{_format_number(value)}%"
def _format_usd(value: float | None) -> str:
if value is None:
return "off"
return f"🟢 {value:g} USD"
return "off"
return f"{_format_number(value)} USD"
def _rule_icon(value: float | None) -> str:
return "" if value is not None else "⚠️"
def _risk_keyboard() -> InlineKeyboardMarkup:
state = AutoTradeService().get_state()
builder = InlineKeyboardBuilder()
builder.button(text=f"🛑 Stop Loss", callback_data="auto:risk:set_sl")
builder.button(text=f"🎯 Take Profit", callback_data="auto:risk:set_tp")
builder.button(text=f"💸 Max Loss", callback_data="auto:risk:set_ml")
builder.button(text="♻️ Reset", callback_data="auto:risk:reset")
builder.button(text="⬅️ Назад", callback_data="settings:auto")
builder.button(text="🛑 SL", callback_data="auto:risk:set_sl")
builder.button(text="🎯 TP", callback_data="auto:risk:set_tp")
builder.button(text="💸 ML", callback_data="auto:risk:set_ml")
builder.button(text="🤖 Автоторговля", callback_data="auto:home")
builder.button(text="⬅️ Назад", callback_data="settings:auto")
builder.button(text="♻️ Сбросить", callback_data="auto:risk:reset")
builder.adjust(2, 2, 2)
builder.adjust(3, 1, 2)
return builder.as_markup()
@@ -66,16 +81,13 @@ def _risk_text(status_message: str | None = None) -> str:
status = "🟢 Активна" if active_count else "⚪ Выключена"
text = (
"<b>⚠️ Risk Settings</b>\n\n"
"<b>🧯 Защита позиции</b>\n\n"
"<b>СИСТЕМА</b> · Настройки · Автоторговля\n\n"
f"Статус защиты: {status}\n"
f"Активных правил: {active_count}/3\n\n"
f"🛑 Stop Loss: {_format_percent(state.stop_loss_percent)}\n"
f"🎯 Take Profit: {_format_percent(state.take_profit_percent)}\n"
f"💸 Max Loss: {_format_usd(state.max_loss_usd)}\n\n"
"<b>Подсказка:</b>\n"
"Пример: <code>0.5</code>, <code>1</code>\n"
"Введите <code>0</code>, чтобы отключить параметр."
f"{_rule_icon(state.stop_loss_percent)} Stop Loss · {_format_percent(state.stop_loss_percent)}\n"
f"{_rule_icon(state.take_profit_percent)} Take Profit · {_format_percent(state.take_profit_percent)}\n"
f"{_rule_icon(state.max_loss_usd)} Max Loss · {_format_usd(state.max_loss_usd)}\n"
)
if status_message:
@@ -155,7 +167,7 @@ async def _remember_risk_screen(callback: CallbackQuery, state: FSMContext) -> N
def _parse_positive_or_none(raw_text: str | None) -> float | None:
value_text = (raw_text or "").strip().replace(",", ".")
if value_text in {"0", "0.0", "off", "OFF", "-"}:
if value_text.lower() in {"0", "0.0", "off", "-"}:
return None
value = float(value_text)
@@ -222,11 +234,11 @@ async def ask_stop_loss(callback: CallbackQuery, state: FSMContext) -> None:
if callback.message is not None:
await callback.message.edit_text(
"<b>🛑 Stop Loss</b>\n\n"
"<b>Stop Loss</b>\n\n"
"<b>СИСТЕМА</b> · Настройки · Автоторговля\n\n"
"Введите Stop Loss в процентах.\n"
"Например: <code>2</code>\n\n"
"Введите <code>0</code>, чтобы отключить."
"Например: <code>1</code>, <code>0.5</code>, <code>0,5</code>\n\n"
"отключить параметр - <code>0</code>"
)
await callback.answer()
@@ -240,11 +252,11 @@ async def ask_take_profit(callback: CallbackQuery, state: FSMContext) -> None:
if callback.message is not None:
await callback.message.edit_text(
"<b>🎯 Take Profit</b>\n\n"
"<b>Take Profit</b>\n\n"
"<b>СИСТЕМА</b> · Настройки · Автоторговля\n\n"
"Введите Take Profit в процентах.\n"
"Например: <code>3</code>\n\n"
"Введите <code>0</code>, чтобы отключить."
"Например: <code>2</code>, <code>1.5</code>, <code>1,5</code>\n\n"
"отключить параметр - <code>0</code>"
)
await callback.answer()
@@ -258,11 +270,11 @@ async def ask_max_loss(callback: CallbackQuery, state: FSMContext) -> None:
if callback.message is not None:
await callback.message.edit_text(
"<b>💸 Max Loss</b>\n\n"
"<b>Maximum Loss</b>\n\n"
"<b>СИСТЕМА</b> · Настройки · Автоторговля\n\n"
"Введите максимальный paper-убыток в USD.\n"
"Например: <code>10</code>\n\n"
"Введите <code>0</code>, чтобы отключить."
"Например: <code>100</code>, <code>50.5</code>, <code>50,5</code>\n\n"
"отключить параметр - <code>0</code>"
)
await callback.answer()
@@ -309,7 +321,7 @@ async def set_stop_loss(message: Message, state: FSMContext) -> None:
try:
value = _parse_positive_or_none(message.text)
except ValueError:
await message.answer("Введите число. Например: 2 или 0 для отключения.")
await message.answer("Введите число. Например: 1, 0.5 или 0 для отключения.")
return
if not _validate_percent(value):
@@ -333,7 +345,7 @@ async def set_take_profit(message: Message, state: FSMContext) -> None:
try:
value = _parse_positive_or_none(message.text)
except ValueError:
await message.answer("Введите число. Например: 3 или 0 для отключения.")
await message.answer("Введите число. Например: 2, 1.5 или 0 для отключения.")
return
if not _validate_percent(value):
@@ -357,7 +369,7 @@ async def set_max_loss(message: Message, state: FSMContext) -> None:
try:
value = _parse_positive_or_none(message.text)
except ValueError:
await message.answer("Введите число. Например: 10 или 0 для отключения.")
await message.answer("Введите число. Например: 100, 50.5 или 0 для отключения.")
return
if not _validate_max_loss(value):