11
This commit is contained in:
parent
97fc08671f
commit
b531ed4055
@ -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([
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
127
backend/app/utils/signal_text.py
Normal file
127
backend/app/utils/signal_text.py
Normal 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
|
||||
18
backend/tests/test_signal_text.py
Normal file
18
backend/tests/test_signal_text.py
Normal 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") == "在临近做多区出现反转信号后介入"
|
||||
Loading…
Reference in New Issue
Block a user