07.4.4.1.11 — Advanced Trend Quality & EMA Distance Layer
This commit is contained in:
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
from src.notifications.models import NotificationMessage
|
||||
from src.runtime_events.event_types import RuntimeEventType
|
||||
from src.runtime_events.models import RuntimeEvent
|
||||
from src.core.numbers import safe_float
|
||||
|
||||
|
||||
def build_execution_notification(event: RuntimeEvent) -> NotificationMessage | None:
|
||||
@@ -27,8 +28,9 @@ def _build_position_opened(event: RuntimeEvent) -> NotificationMessage:
|
||||
payload = event.payload
|
||||
|
||||
symbol = _format_symbol(payload.get("symbol"))
|
||||
strategy = str(payload.get("strategy") or "—")
|
||||
side = str(payload.get("side") or "—").upper()
|
||||
strategy = str(payload.get("strategy") or "—").title()
|
||||
side_raw = str(payload.get("side") or "—").upper()
|
||||
side = side_raw.title()
|
||||
leverage = _format_leverage(payload.get("leverage"))
|
||||
entry_price = _format_price(payload.get("entry_price"))
|
||||
size = _format_size(payload.get("size"))
|
||||
@@ -39,13 +41,13 @@ def _build_position_opened(event: RuntimeEvent) -> NotificationMessage:
|
||||
)
|
||||
semantic_lines = payload.get("semantic_lines") or []
|
||||
|
||||
side_icon = "🟢" if side == "LONG" else "🔴"
|
||||
side_icon = "🟢" if side_raw == "LONG" else "🔴"
|
||||
|
||||
lines = [
|
||||
"<b>🧾 Позиция открыта</b>",
|
||||
"",
|
||||
f"{side_icon} {symbol} · {strategy} · {side} {leverage}",
|
||||
f"Вход: {entry_price}",
|
||||
f"Вход: ${entry_price}",
|
||||
f"Размер: {size}",
|
||||
f"Объём: {_format_notional(entry_price=payload.get('entry_price'), size=payload.get('size'))}",
|
||||
"",
|
||||
@@ -71,7 +73,7 @@ def _build_position_closed(event: RuntimeEvent) -> NotificationMessage:
|
||||
payload = event.payload
|
||||
|
||||
symbol = _format_symbol(payload.get("symbol"))
|
||||
side = str(payload.get("side") or "—").upper()
|
||||
side = str(payload.get("side") or "—").title()
|
||||
leverage = _format_leverage(payload.get("leverage"))
|
||||
|
||||
entry_price = _format_price(payload.get("entry_price"))
|
||||
@@ -91,8 +93,8 @@ def _build_position_closed(event: RuntimeEvent) -> NotificationMessage:
|
||||
f"{pnl_icon} {pnl_label} · {pnl_text}",
|
||||
"",
|
||||
f"{symbol} · {side} {leverage}",
|
||||
f"Вход: $ {entry_price}",
|
||||
f"Выход: $ {exit_price}",
|
||||
f"Вход: ${entry_price}",
|
||||
f"Выход: ${exit_price}",
|
||||
f"Размер: {size}",
|
||||
]
|
||||
|
||||
@@ -138,8 +140,13 @@ def _build_position_flipped(event: RuntimeEvent) -> NotificationMessage:
|
||||
symbol = _format_symbol(payload.get("symbol"))
|
||||
strategy = str(payload.get("strategy") or "—").title()
|
||||
|
||||
old_side = str(payload.get("old_side") or "—").upper()
|
||||
new_side = str(payload.get("new_side") or payload.get("side") or "—").upper()
|
||||
old_side_raw = str(payload.get("old_side") or "—").upper()
|
||||
new_side_raw = str(
|
||||
payload.get("new_side") or payload.get("side") or "—"
|
||||
).upper()
|
||||
|
||||
old_side = old_side_raw.title()
|
||||
new_side = new_side_raw.title()
|
||||
|
||||
old_leverage = _format_leverage(
|
||||
payload.get("old_leverage")
|
||||
@@ -161,8 +168,8 @@ def _build_position_flipped(event: RuntimeEvent) -> NotificationMessage:
|
||||
pnl_icon = "🟢" if pnl_value >= 0 else "🔴"
|
||||
pnl_label = "Прибыль" if pnl_value >= 0 else "Убыток"
|
||||
|
||||
old_icon = "🟢" if old_side == "LONG" else "🔴"
|
||||
new_icon = "🟢" if new_side == "LONG" else "🔴"
|
||||
old_icon = "🟢" if old_side_raw == "LONG" else "🔴"
|
||||
new_icon = "🟢" if new_side_raw == "LONG" else "🔴"
|
||||
|
||||
confidence = float(payload.get("confidence") or 0.0)
|
||||
repeat_count = int(payload.get("repeat_count") or 0)
|
||||
@@ -178,12 +185,12 @@ def _build_position_flipped(event: RuntimeEvent) -> NotificationMessage:
|
||||
f"{symbol} · {strategy} {old_icon} {old_side} → {new_icon} {new_side}",
|
||||
"",
|
||||
f"Закрыта {old_side} {old_leverage}",
|
||||
f"Вход: $ {entry_price}",
|
||||
f"Выход: $ {exit_price}",
|
||||
f"Вход: ${entry_price}",
|
||||
f"Выход: ${exit_price}",
|
||||
f"Размер: {old_size}",
|
||||
"",
|
||||
f"Открыта {new_side} {new_leverage}",
|
||||
f"Вход: $ {new_entry_price}",
|
||||
f"Вход: ${new_entry_price}",
|
||||
f"Размер: {new_size}",
|
||||
(
|
||||
"Объём: "
|
||||
@@ -215,9 +222,9 @@ def _build_flip_blocked(event: RuntimeEvent) -> NotificationMessage:
|
||||
signal = str(payload.get("signal") or "").upper()
|
||||
confidence = float(payload.get("confidence") or 0.0)
|
||||
reason = str(payload.get("reason") or "Flip заблокирован")
|
||||
position_side = str(payload.get("position_side") or "—").upper()
|
||||
position_side = str(payload.get("position_side") or "—").title()
|
||||
|
||||
target_side = "LONG" if signal == "BUY" else "SHORT" if signal == "SELL" else "—"
|
||||
target_side = "Long" if signal == "BUY" else "Short" if signal == "SELL" else "—"
|
||||
icon = "🟢" if target_side == "LONG" else "🔴" if target_side == "SHORT" else ""
|
||||
|
||||
text = (
|
||||
@@ -247,43 +254,30 @@ def _format_symbol(value: object) -> str:
|
||||
|
||||
|
||||
def _format_leverage(value: object) -> str:
|
||||
try:
|
||||
return f"x{float(value):g}"
|
||||
except (TypeError, ValueError):
|
||||
number = safe_float(value)
|
||||
|
||||
if number is None:
|
||||
return "—"
|
||||
|
||||
return f"x{number:g}"
|
||||
|
||||
|
||||
def _format_price(value: object) -> str:
|
||||
try:
|
||||
number = float(value)
|
||||
except (TypeError, ValueError):
|
||||
number = safe_float(value)
|
||||
|
||||
if number is None:
|
||||
return "—"
|
||||
|
||||
return f"{number:,.2f}".replace(",", " ")
|
||||
|
||||
|
||||
def _format_size(value: object) -> str:
|
||||
try:
|
||||
return f"{float(value):.8f}".rstrip("0").rstrip(".")
|
||||
except (TypeError, ValueError):
|
||||
number = safe_float(value)
|
||||
|
||||
if number is None:
|
||||
return "—"
|
||||
|
||||
|
||||
def _format_pnl(value: object) -> str:
|
||||
try:
|
||||
number = float(value)
|
||||
except (TypeError, ValueError):
|
||||
return "—"
|
||||
|
||||
amount = f"$ {abs(number):,.2f}".replace(",", " ").rstrip("0").rstrip(".")
|
||||
|
||||
if number > 0:
|
||||
return f"🟢 +{amount}"
|
||||
|
||||
if number < 0:
|
||||
return f"🔴 −{amount}"
|
||||
|
||||
return "$ 0"
|
||||
return f"{number:.8f}".rstrip("0").rstrip(".")
|
||||
|
||||
|
||||
def _alert_priority(*, confidence: float, repeat_count: int) -> str:
|
||||
@@ -319,9 +313,12 @@ def _format_notional(
|
||||
entry_price: object,
|
||||
size: object,
|
||||
) -> str:
|
||||
try:
|
||||
value = float(entry_price) * float(size)
|
||||
except (TypeError, ValueError):
|
||||
entry = safe_float(entry_price)
|
||||
amount = safe_float(size)
|
||||
|
||||
if entry is None or amount is None:
|
||||
return "—"
|
||||
|
||||
value = entry * amount
|
||||
|
||||
return f"$ {value:,.2f}".replace(",", " ").rstrip("0").rstrip(".")
|
||||
Reference in New Issue
Block a user