This commit is contained in:
aaron 2026-04-28 15:02:13 +08:00
parent 97fc08671f
commit b531ed4055
4 changed files with 186 additions and 7 deletions

View File

@ -30,6 +30,11 @@ from app.crypto_agent.market_signal_analyzer import MarketSignalAnalyzer
from app.crypto_agent.execution_guardian import ExecutionGuardian
from app.crypto_agent.execution_targets import ExecutionTarget, build_default_execution_targets
from app.utils.system_status import get_system_monitor, AgentStatus
from app.utils.signal_text import (
humanize_entry_basis,
humanize_setup_basis,
humanize_setup_type,
)
class CryptoAgent:
@ -2707,6 +2712,9 @@ class CryptoAgent:
setup_type = best_signal.get('setup_type', 'unknown')
setup_basis = best_signal.get('setup_basis', '')
entry_basis = best_signal.get('entry_basis', '')
setup_type_text = humanize_setup_type(setup_type)
setup_basis_text = humanize_setup_basis(setup_basis)
entry_basis_text = humanize_entry_basis(entry_basis)
# 等级(基于信心度映射)- 与 market_signal_analyzer.py 保持一致
# A级(80-100): 量价配合 + 多指标共振 + 多周期确认
@ -2771,11 +2779,11 @@ class CryptoAgent:
f"",
]
if setup_type and setup_type != 'unknown':
content_parts.append(f"🧩 **Setup**: `{setup_type}`")
content_parts.append(f"🧩 **交易形态**: {setup_type_text}")
if setup_basis:
content_parts.append(f"📌 **Setup依据**: {setup_basis}")
content_parts.append(f"📌 **形态依据**: {setup_basis_text}")
if entry_basis:
content_parts.append(f"🎯 **入场依据**: {entry_basis}")
content_parts.append(f"🎯 **入场依据**: {entry_basis_text}")
content_parts.append("")
# 入场价格显示
@ -2886,6 +2894,9 @@ class CryptoAgent:
setup_type = best_signal.get('setup_type', 'unknown') if best_signal else 'unknown'
setup_basis = best_signal.get('setup_basis', '') if best_signal else ''
entry_basis = best_signal.get('entry_basis', '') if best_signal else ''
setup_type_text = humanize_setup_type(setup_type)
setup_basis_text = humanize_setup_basis(setup_basis)
entry_basis_text = humanize_entry_basis(entry_basis)
# 对限价单:用实际订单状态决定显示
# resting=真的在挂单中, filled=已立即成交, None=市价单或未知
@ -2971,9 +2982,9 @@ class CryptoAgent:
]
if setup_type and setup_type != 'unknown':
content_parts.extend([
f"🧩 **Setup**: `{setup_type}`",
f"📌 **Setup依据**: {setup_basis}" if setup_basis else None,
f"🎯 **入场依据**: {entry_basis}" if entry_basis else None,
f"🧩 **交易形态**: {setup_type_text}",
f"📌 **形态依据**: {setup_basis_text}" if setup_basis else None,
f"🎯 **入场依据**: {entry_basis_text}" if entry_basis else None,
f"",
])
content_parts.extend([

View File

@ -548,6 +548,27 @@ class BaseExecutor(ABC):
return
content_parts.append(f"**{label}**: {value}")
def _append_signal_context(self, content_parts: List[str], details: Optional[Dict[str, Any]] = None):
if not details:
return
from app.utils.signal_text import (
humanize_entry_basis,
humanize_setup_basis,
humanize_setup_type,
)
setup_type = details.get('setup_type')
setup_basis = details.get('setup_basis')
entry_basis = details.get('entry_basis')
if setup_type and str(setup_type) != "unknown":
self._append_notification_detail(content_parts, "交易形态", humanize_setup_type(setup_type))
if setup_basis:
self._append_notification_detail(content_parts, "形态依据", humanize_setup_basis(setup_basis))
if entry_basis:
self._append_notification_detail(content_parts, "入场依据", humanize_entry_basis(entry_basis))
def _build_notification_header(self,
symbol: str,
account_id: str,
@ -621,6 +642,7 @@ class BaseExecutor(ABC):
# 添加详情
if details:
self._append_signal_context(content_parts, details)
self._append_notification_detail(content_parts, "数量", details.get('size'))
if details.get('price') is not None:
self._append_notification_detail(content_parts, "价格", f"${details['price']:,.2f}")
@ -821,8 +843,9 @@ class BaseExecutor(ABC):
self._append_notification_detail(content_parts, "信息", message)
if details:
self._append_signal_context(content_parts, details)
for key, value in details.items():
if key in {'account_id', 'target_key'}:
if key in {'account_id', 'target_key', 'setup_type', 'setup_basis', 'entry_basis'}:
continue
self._append_notification_detail(content_parts, key, value)

View File

@ -0,0 +1,127 @@
"""
信号文本翻译工具
将内部策略枚举setup/entry basis 等英文编码转换为更适合通知展示的中文描述
"""
from __future__ import annotations
from typing import Dict, Optional
SETUP_TYPE_LABELS: Dict[str, str] = {
"range_reversal": "区间反转",
"trend_reversal": "趋势反转",
"breakout_confirmation": "突破确认",
"breakout_pullback": "突破后回踩",
"trend_continuation_pullback": "趋势延续回踩",
"deep_pullback_continuation": "深度回踩延续",
"unknown": "未分类",
}
LOCATION_LABELS: Dict[str, str] = {
"near_long_zone": "临近做多区",
"near_short_zone": "临近做空区",
"near_range_support": "临近区间支撑",
"near_range_resistance": "临近区间阻力",
"middle_of_range": "位于区间中部",
"far_from_trade_zone": "远离优先交易区",
"between_trade_zones": "位于交易区之间",
"range_edge": "区间边缘",
"unknown": "未知位置",
}
FEATURE_VALUE_LABELS: Dict[str, str] = {
"bullish_acceptance": "多头承接有效",
"bearish_acceptance": "空头承接有效",
"bullish_rejection": "多头拒绝信号",
"bearish_rejection": "空头拒绝信号",
"bullish_continuation": "多头延续",
"bearish_continuation": "空头延续",
"pullback_on_light_volume": "缩量回调",
"counter_pressure_expanding": "逆向压力放大",
"acceptance_breakout_up": "向上突破并站稳",
"acceptance_breakout_down": "向下突破并站稳",
"weak_breakout_up": "向上弱突破",
"weak_breakout_down": "向下弱突破",
"healthy_pullback": "健康回调",
"heavy_sell_pullback": "放量下压回调",
"heavy_buy_pullback": "放量上冲回调",
"none": "",
"neutral": "中性",
"unknown": "未知",
"early": "趋势早期",
}
FIELD_LABELS: Dict[str, str] = {
"setup": "形态",
"location": "位置",
"volume_price_state": "量价状态",
"breakout_quality": "突破质量",
"pullback_quality": "回调质量",
"rejection_signal": "拒绝信号",
}
ENTRY_BASIS_LABELS: Dict[str, str] = {
"breakout_acceptance_follow_through": "突破站稳后顺势跟进",
"pullback_into_trade_zone": "回踩交易区后挂单介入",
"pullback_confirmed": "回调确认后介入",
"rejection_or_structure_shift": "出现拒绝信号或结构切换后介入",
"generic_entry": "按通用入场条件执行",
}
def humanize_setup_type(value: Optional[str]) -> str:
code = str(value or "").strip()
if not code:
return "-"
return SETUP_TYPE_LABELS.get(code, code)
def _humanize_value(raw: str) -> str:
text = str(raw or "").strip()
if not text:
return "-"
if text in SETUP_TYPE_LABELS:
return SETUP_TYPE_LABELS[text]
if text in LOCATION_LABELS:
return LOCATION_LABELS[text]
if text in FEATURE_VALUE_LABELS:
return FEATURE_VALUE_LABELS[text]
return text
def humanize_setup_basis(value: Optional[str]) -> str:
text = str(value or "").strip()
if not text:
return "-"
parts = []
for chunk in text.split("|"):
piece = chunk.strip()
if not piece:
continue
if "=" not in piece:
parts.append(piece)
continue
key, raw_value = [item.strip() for item in piece.split("=", 1)]
key_label = FIELD_LABELS.get(key, key)
value_label = _humanize_value(raw_value)
parts.append(f"{key_label}={value_label}")
return "".join(parts) if parts else text
def humanize_entry_basis(value: Optional[str]) -> str:
text = str(value or "").strip()
if not text:
return "-"
if text in ENTRY_BASIS_LABELS:
return ENTRY_BASIS_LABELS[text]
reversal_prefix = "reversal_from_"
if text.startswith(reversal_prefix):
location = text[len(reversal_prefix):]
return f"{LOCATION_LABELS.get(location, location)}出现反转信号后介入"
return text

View File

@ -0,0 +1,18 @@
from app.utils.signal_text import (
humanize_entry_basis,
humanize_setup_basis,
humanize_setup_type,
)
def test_humanize_setup_type_returns_chinese_label():
assert humanize_setup_type("range_reversal") == "区间反转"
def test_humanize_setup_basis_formats_composite_basis():
text = "setup=range_reversal | location=near_long_zone | rejection_signal=bullish_rejection"
assert humanize_setup_basis(text) == "形态=区间反转;位置=临近做多区;拒绝信号=多头拒绝信号"
def test_humanize_entry_basis_formats_reversal_pattern():
assert humanize_entry_basis("reversal_from_near_long_zone") == "在临近做多区出现反转信号后介入"