diff --git a/app/db/paper_trading.py b/app/db/paper_trading.py index d8af9cc..3f23e91 100644 --- a/app/db/paper_trading.py +++ b/app/db/paper_trading.py @@ -481,6 +481,14 @@ def _fmt_pct(value) -> str: return f"{sign}{pct:.2f}%" +def _side_label(side: str) -> str: + return "空" if normalize_side(side) == "short" else "多" + + +def _side_action(side: str, long_text: str, short_text: str) -> str: + return short_text if normalize_side(side) == "short" else long_text + + def _card_field(label: str, value) -> dict: return { "tag": "div", @@ -549,13 +557,16 @@ def _push_custom_paper_card(card: dict) -> tuple[bool, object]: def _push_event_card(event_type: str, trade: dict, result: dict, event_time: str = "") -> None: symbol = str(trade.get("symbol") or "") short_symbol = symbol.replace("/USDT", "") + side = normalize_side(result.get("side") or trade.get("side")) + side_label = _side_label(side) if event_type == "open": _push_paper_card( event_type, symbol, - f"交易开仓 - {short_symbol}", + f"交易开仓({side_label}) - {short_symbol}", "blue", [ + ("方向", side_label), ("成交价", _fmt_price(result.get("entry_price"))), ("名义仓位", f"{_safe_float(result.get('notional_usdt')):.2f} USDT"), ("杠杆/保证金", f"{_safe_float(result.get('leverage')):.1f}x / {_safe_float(result.get('margin_usdt')):.2f} USDT"), @@ -570,9 +581,10 @@ def _push_event_card(event_type: str, trade: dict, result: dict, event_time: str _push_paper_card( event_type, symbol, - f"{title_prefix} - {short_symbol}", + f"{title_prefix}({side_label}) - {short_symbol}", "red" if _safe_float(result.get("pnl_usdt")) < 0 else "green", [ + ("方向", side_label), ("退出价", _fmt_price(result.get("exit_price"))), ("收益率", _fmt_pct(result.get("pnl_pct"))), ("收益额", f"{_safe_float(result.get('pnl_usdt')):.2f} USDT"), @@ -586,12 +598,13 @@ def _push_event_card(event_type: str, trade: dict, result: dict, event_time: str _push_paper_card( event_type, symbol, - f"移动止盈{'启动' if event_type == 'trailing_activate' else '上移'} - {short_symbol}", + f"移动止盈{'启动' if event_type == 'trailing_activate' else '调整'}({side_label}) - {short_symbol}", "yellow", [ + ("方向", side_label), ("保护价", _fmt_price(result.get("trailing_stop"))), ("当前收益", _fmt_pct(result.get("pnl_pct"))), - ("动作", "启动保护" if event_type == "trailing_activate" else "上移保护价"), + ("动作", "启动保护" if event_type == "trailing_activate" else _side_action(side, "上移保护价", "下移保护价")), ], "移动止盈用于锁定浮盈。", event_time, @@ -600,39 +613,45 @@ def _push_event_card(event_type: str, trade: dict, result: dict, event_time: str def _push_order_created_card(order: dict, event_time: str = "") -> None: symbol = str(order.get("symbol") or "") + side = normalize_side(order.get("side")) + side_label = _side_label(side) target = _safe_float(order.get("target_price")) current = _safe_float(order.get("current_price_at_create")) - distance = round((current / target - 1) * 100, 2) if target and current else 0 + distance = order_distance_pct(side, current, target) if target and current else 0 _push_paper_card( "paper_order_create", symbol, - f"挂单创建 - {symbol.replace('/USDT', '')}", + f"挂单创建({side_label}) - {symbol.replace('/USDT', '')}", "wathet", [ + ("方向", side_label), ("目标价", _fmt_price(target)), ("当前价", _fmt_price(current)), ("距目标", _fmt_pct(distance)), ("有效期", order.get("expires_at") or "--"), ], - "等回踩机会已进入挂单,触价后进入持仓。", + _side_action(side, "等回踩机会已进入挂单,触价后进入持仓。", "等反抽机会已进入挂单,触价后进入持仓。"), event_time, ) def _push_order_filled_card(order: dict, result: dict, event_time: str = "") -> None: symbol = str(order.get("symbol") or "") + side = normalize_side(result.get("side") or order.get("side")) + side_label = _side_label(side) _push_paper_card( "paper_order_fill", symbol, - f"挂单成交并开仓 - {symbol.replace('/USDT', '')}", + f"挂单成交并开仓({side_label}) - {symbol.replace('/USDT', '')}", "green", [ + ("方向", side_label), ("挂单价", _fmt_price(order.get("target_price"))), ("成交价", _fmt_price(result.get("entry_price") or order.get("fill_price"))), ("名义仓位", f"{_safe_float(result.get('notional_usdt')):.2f} USDT"), ("来源", order.get("source_status") or "wait_pullback"), ], - "价格触达理想入场位,挂单已转为持仓。", + _side_action(side, "价格触达理想回踩位,挂单已转为持仓。", "价格触达理想反抽位,挂单已转为持仓。"), event_time, ) @@ -808,6 +827,7 @@ def _open_trade(conn, rec: dict, current_price: float, event_time: str, config: result = { "opened": True, "trade_id": trade_id, + "side": side, "entry_price": entry_price, "qty": qty, "notional_usdt": notional, @@ -1258,7 +1278,7 @@ def _close_trade(conn, trade: dict, current_price: float, reason: str, event_tim _push_event_card( "close", trade, - {"exit_price": exit_price, "exit_reason": reason, "pnl_pct": pnl_pct, "pnl_usdt": pnl_usdt}, + {"side": side, "exit_price": exit_price, "exit_reason": reason, "pnl_pct": pnl_pct, "pnl_usdt": pnl_usdt}, now, ) return {"closed": True, "trade_id": trade["id"], "exit_reason": reason, "pnl_pct": pnl_pct, "pnl_usdt": pnl_usdt} @@ -1313,6 +1333,7 @@ def _apply_position_health_guard( def _update_trailing_stop(conn, trade: dict, current_price: float, pnl_pct: float, event_time: str) -> tuple[float, dict]: cfg = _trailing_config() + side = normalize_side(trade.get("side")) current_trail = _safe_float(trade.get("trailing_stop")) decision = evaluate_trailing_stop(position=trade, current_price=current_price, pnl_pct=pnl_pct, config=cfg).as_dict() if not decision.get("activated") and not decision.get("moved"): @@ -1358,7 +1379,7 @@ def _update_trailing_stop(conn, trade: dict, current_price: float, pnl_pct: floa }, event_time, ) - _push_event_card(event_type, trade, {"trailing_stop": new_trail, "pnl_pct": pnl_pct}, event_time) + _push_event_card(event_type, trade, {"side": side, "trailing_stop": new_trail, "pnl_pct": pnl_pct}, event_time) return new_trail, { "activated": bool(decision.get("activated")), "moved": bool(decision.get("moved")),