07.4.3.14 — Auto Trading UI. Realistic Pricing & Debug Live Tools
This commit is contained in:
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user