From c0809623730f78ef1726d6fcc4f8469d4d880159 Mon Sep 17 00:00:00 2001
From: aaron <>
Date: Wed, 4 Mar 2026 12:14:07 +0800
Subject: [PATCH] update
---
.../crypto_agent/market_signal_analyzer.py | 239 +++++++++++++-----
backend/app/services/multi_llm_service.py | 121 +++++++++
scripts/analyze_eth_4h.py | 219 ++++++++++++++++
3 files changed, 513 insertions(+), 66 deletions(-)
create mode 100644 scripts/analyze_eth_4h.py
diff --git a/backend/app/crypto_agent/market_signal_analyzer.py b/backend/app/crypto_agent/market_signal_analyzer.py
index 978b68f..98301a2 100644
--- a/backend/app/crypto_agent/market_signal_analyzer.py
+++ b/backend/app/crypto_agent/market_signal_analyzer.py
@@ -418,51 +418,99 @@ class MarketSignalAnalyzer:
- **突破交易**:多周期同时突破关键位 + 放量,信号最强
- **回调交易**:30m 趋势向上,15m 回调到 EMA20,5m 反弹确认
-## 八、入场方式(稳健版 - 防止持续止损)
+## 八、入场方式(基于形态的智能选择)
-### 核心原则:挂单优先,市价慎用
+### 核心原则:根据市场形态选择入场方式
-**🎯 入场方式优先级**:
-1. **limit 挂单(首选)**:等待回调/反弹到关键位
-2. **观望(次选)**:价格不合适时耐心等待
-3. **market 市价(慎用)**:仅在极少数情况下使用
+**🎯 形态识别优先**:
+1. **突破/跌破形态** → market 市价入场(抓住机会)
+2. **箱体震荡形态** → limit 挂单入场(耐心等待)
+3. **不明确形态** → limit 挂单或观望
-### limit(挂单等待入场)- 默认首选
-使用场景(90%的情况用 limit):
-- ✅ **所有回调/反弹入场**(策略2的主要方式)
-- ✅ **信号强度中等**(B/C 级)
-- ✅ **市场横盘整理**,价格在区间内波动
-- ✅ **等待回调到支撑位**(EMA20、前期低点)
-- ✅ **等待反弹到压力位**(EMA20、前期高点)
-- ✅ **希望获得更优成交价格**
-- ✅ **当前价格距离关键位 > 0.5%**
+### 第一步:识别当前市场形态
-**挂单是稳健交易的基础,可以避免追涨杀跌!**
+#### 1. 突破形态(Breakout)- 市价入场
+**识别标准**(必须同时满足):
+- ✅ 价格**放量突破**关键阻力位/支撑位(量比 > 1.5)
+- ✅ 突破后**没有立即回落**(站稳突破位上方/下方)
+- ✅ 15m RSI 在 50-65(多)或 35-50(空)- 有延续空间
+- ✅ 多周期**共振突破**(5m + 15m + 30m 同时突破)
-### market(现价立即入场)- 极少使用
-**⚠️ market 入场的严格限制**:
+**确认信号**:
+- K线实体完全突破关键位(影线不算)
+- 突破后至少2根K线站稳
+- 成交量明显放大(量比 > 1.5)
+- 无明显的假突破迹象(如快速回落)
-仅在以下**极少数**情况使用 market:
-- ✅ 强共振信号(A级,confidence ≥ 90)
-- ✅ 放量突破**后回踩确认**,不是突破时追
-- ✅ 多周期同时确认,回调明确
-- ✅ 15m RSI 45-60(多)或 40-55(空)- 安全区域
-- ✅ 盈亏比要求:≥ 1:1.5
-- ✅ **止损设置**:正常止损(1-1.5%),正常仓位
+**入场方式**:**market 市价入场**
+- **原因**:突破行情通常快速延续,等待回调会错过机会
+- **止损**:突破位下方 1-1.5%
+- **目标**:上方 2-3%
-**❌ 以下情况绝对不用 market**:
-- ❌ 价格正在快速加速移动
-- ❌ 5m 连续 2 根以上大阳/阴线
-- ❌ 价格偏离 EMA5 > 1.5%
-- ❌ 15m RSI > 65(多)或 < 35(空)
-- ❌ 量比 < 1.0(无放量配合)
-- ❌ 盈亏比 < 1:1.5
+#### 2. 跌破形态(Breakdown)- 市价入场
+**识别标准**(必须同时满足):
+- ✅ 价格**放量跌破**关键支撑位(量比 > 1.5)
+- ✅ 跌破后**没有立即反弹**(继续走弱)
+- ✅ 15m RSI 在 35-50(空)- 有延续空间
+- ✅ 多周期**共振跌破**(5m + 15m + 30m 同时跌破)
-**重要**:
-- 必须同时输出 `entry_price`(建议入场价)和 `entry_type`(入场方式)
-- **90% 的情况应该使用 limit 挂单**
-- **market 是最后选择,不是首选**
-- 宁可错过机会,也不要追涨杀跌
+**入场方式**:**market 市价入场**
+- **原因**:跌破行情通常快速延续,等待反弹会错过机会
+- **止损**:跌破位上方 1-1.5%
+- **目标**:下方 2-3%
+
+#### 3. 箱体震荡形态(Range-bound)- 挂单入场
+**识别标准**(满足以下至少3个):
+- ✅ 布林带收口(波动率收缩)
+- ✅ 15m RSI 在 40-60 震荡(无明确方向)
+- ✅ 价格在区间内来回波动(上下边界清晰)
+- ✅ EMA5/20/50 走平或纠缠(无趋势)
+- ✅ 量能温和(无异常放量)
+
+**入场方式**:**limit 挂单入场**
+- **上沿做空**:价格接近上沿阻力位时挂空单
+- **下沿做多**:价格接近下沿支撑位时挂多单
+- **止损**:箱体边界外 1%
+- **目标**:对岸边界
+
+#### 4. 不明确形态 - 挂单或观望
+- 如果既不符合突破也不符合震荡,等待更明确的信号
+- 优先使用 limit 挂单,宁可错过也不要做错
+
+### 第二步:根据形态决定入场方式
+
+| 市场形态 | 入场方式 | 原因 |
+|---------|---------|------|
+| **放量突破**(多周期共振) | **market 市价** | 抓住突破机会,等待回调会错过 |
+| **放量跌破**(多周期共振) | **market 市价** | 抓住跌破机会,等待反弹会错过 |
+| **箱体震荡**(区间清晰) | **limit 挂单** | 在边界反向挂单,耐心等待 |
+| **趋势回调**(顺势) | **limit 挂单** | 等待回调到支撑位再入场 |
+| **不明确** | **观望或 limit** | 等待更明确的信号 |
+
+### 第三步:入场方式执行规则
+
+**market 市价入场**(仅限突破/跌破形态):
+- ✅ 必须满足突破/跌破的所有识别标准
+- ✅ 量比 > 1.5(放量确认)
+- ✅ 多周期共振(5m + 15m + 30m)
+- ✅ 止损设置在突破/跌破位外侧 1-1.5%
+- ✅ 盈亏比 ≥ 1:1.5
+
+**limit 挂单入场**(震荡和回调形态):
+- ✅ 震荡市:在边界反向挂单
+- ✅ 趋势回调:等待回调到 EMA20/支撑位挂单
+- ✅ 挂单价格距离当前价格 ≥ 0.5%
+- ✅ 盈亏比 ≥ 1:1.5
+
+### ⚠️ 绝对禁止的入场情况(无论哪种形态)
+
+**❌ 追涨杀跌**(价格正在快速加速移动):
+- 5m 连续 2 根以上大阳/阴线
+- 15m RSI > 65(多)或 < 35(空)- 极端超买超卖
+- 价格偏离 EMA5 > 1.5%
+- 信号入场价距离当前价格 ≥ 2%
+
+**以上情况强制 HOLD,禁止任何操作!**
## 输出格式
请严格按照以下 JSON 格式输出:
@@ -499,21 +547,26 @@ class MarketSignalAnalyzer:
## 重要说明
- `entry_price`:建议入场价格(单一值)
- `entry_type`:入场方式 - `market`(现价立即入场)或 `limit`(挂单等待)
+- **基于形态选择入场方式**:
+ - 突破/跌破形态 + 放量 + 多周期共振 → `market` 市价入场
+ - 箱体震荡/趋势回调 → `limit` 挂单入场
+ - 不明确形态 → `limit` 或观望
- **所有价格必须是纯数字**,不要加 $ 符号、逗号或其他格式
- `entry_price`、`stop_loss`、`take_profit` 必须是数字类型,不要是字符串
- `key_levels` 中的支撑位和阻力位也必须是数字数组
-## 信号等级与置信度(稳健版)
+## 信号等级与置信度(基于形态)
### 按信号质量分类
- **A级**(85-100):
- - 强共振:多周期同向 + 多指标共振 + 回调确认
+ - **突破/跌破形态**:多周期共振 + 放量 + 站稳
+ - **入场方式**:market 市价入场(突破/跌破)或 limit(回调)
- 盈亏比 ≥ 1:1.5
- - **建议**:limit 挂单为主(等待回调),light 仓位
+ - **建议**:突破/跌破用 market,回调用 limit,light 仓位
- **B级**(70-84):
- 量价配合 + 主要指标确认
- - 回调/反弹机会明确
+ - 震荡市边界交易或趋势回调
- 盈亏比 ≥ 1:1.5
- **建议**:limit 挂单,light 仓位
@@ -527,35 +580,89 @@ class MarketSignalAnalyzer:
- 量价背离或信号矛盾或盈亏比不足
- **不建议交易**
-## 注意事项(稳健交易重点)
-1. **挂单优先(90%用limit)**:
- - 日内交易最重要的是**耐心等待好的入场价格**
- - 价格快速移动时,**绝对不要追**,等待回调
- - 只有在极少数确认情况才用 market(<10%)
-2. **只在有明确的做多或做空机会时才输出信号**(action 为 buy 或 sell)
-3. 如果市场不明朗,没有明确交易机会,**不要输出任何信号**(signals 为空数组 [])
-4. 信号强度(confidence)要合理,不要随意给高分:
+## 注意事项(基于形态的入场方式)
+1. **形态识别优先**:
+ - 先判断是突破/跌破、震荡、还是回调形态
+ - 根据形态选择合适的入场方式
+ - 突破/跌破用 market 抓住机会,震荡用 limit 耐心等待
+2. **防止追涨杀跌**(更重要!):
+ - 价格**加速移动时**(连续大阳/阴线)强制 HOLD
+ - RSI **极端区间**(>65 或 <35)强制 HOLD
+ - 价格 **偏离 EMA5 > 1.5%** 强制 HOLD
+ - 宁可错过,也不要追涨杀跌!
+3. **只在有明确的做多或做空机会时才输出信号**(action 为 buy 或 sell)
+4. 如果市场不明朗,没有明确交易机会,**不要输出任何信号**(signals 为空数组 [])
+5. 信号强度(confidence)要合理,不要随意给高分:
- 60-70分:一般信号,可轻仓试探(micro 仓位)
- 75-84分:较强信号,可正常仓位(light 仓位)
- - 85-100分:强信号,可考虑 medium 仓位
-5. **不要输出 action 为 "wait" 的信号**,如果没有交易机会就不输出
-6. **每次检查盈亏比**:盈亏比 < 1:1.5 的信号不要输出
-7. **避免过度交易**:趋势延续时不重复输出相同方向信号
-8. **关注时效性**:日内信号有效期通常 2-4 小时,超过时间需重新评估
-9. **⚠️ 防止持续止损**:
- - 价格加速移动时(连续大阳/阴线)强制 HOLD
- - RSI 极端区间(>65 或 <35)强制 HOLD
- - 价格偏离 EMA5 > 1.5% 强制 HOLD
- - 宁可错过,也不要追涨杀跌!
+ - 85-100分:强信号(突破/跌破),可考虑 market 入场
+6. **不要输出 action 为 "wait" 的信号**,如果没有交易机会就不输出
+7. **每次检查盈亏比**:盈亏比 < 1:1.5 的信号不要输出
+8. **避免过度交易**:趋势延续时不重复输出相同方向信号
+9. **关注时效性**:日内信号有效期通常 2-4 小时,超过时间需重新评估
## 🎯 稳健交易成功关键
-1. **盈亏比第一**:宁可错过,不做错
-2. **挂单优先**:等待回调,不要追涨
-3. **耐心等待**:好的入场点需要等待
-4. **严控止损**:触及止损立即离场
-5. **不贪不急**:达到目标就走,达不到就止损
-6. **保持冷静**:不被情绪左右,按规则交易
-7. **防止持续止损**:价格加速时强制观望
+1. **形态识别优先**:先判断形态,再选入场方式
+2. **突破用market**:抓住突破机会,等待会错过
+3. **震荡用limit**:边界反向挂单,耐心等待
+4. **防止追涨杀跌**:价格加速时强制观望
+5. **盈亏比第一**:宁可错过,不做错
+6. **严控止损**:触及止损立即离场
+7. **不贪不急**:达到目标就走,达不到就止损
+8. **保持冷静**:不被情绪左右,按规则交易
+
+## 📖 形态识别示例
+
+### 示例1:放量突破 → market 市价入场
+**市场状态**:
+- BTC 在 $67,500 附近盘整
+- 突然放量突破 $68,000 阻力位(量比 > 2.0)
+- 5m、15m、30m 同时突破(多周期共振)
+- 15m RSI = 58(有延续空间,不过热)
+
+**正确做法**:
+- ✅ **立即 market 市价做多**
+- ✅ 止损:$67,200(突破位下方 1.2%)
+- ✅ 目标:$69,500(+2.2%)
+- ❌ 不要等待回调,会错过机会
+
+### 示例2:箱体震荡 → limit 挂单入场
+**市场状态**:
+- BTC 在 $67,000 - $68,000 区间震荡
+- 布林带收口,波动率降低
+- 15m RSI 在 45-55 震荡
+- EMA5/20/50 纠缠(无趋势)
+
+**正确做法**:
+- ✅ **在 $67,900 limit 挂空单**(接近上沿)
+- ✅ 或在 $67,100 limit 挂多单**(接近下沿)
+- ✅ 止损:区间边界外 1%
+- ❌ 不要市价追涨杀跌
+
+### 示例3:趋势回调 → limit 挂单等待
+**市场状态**:
+- BTC 处于上升趋势,EMA 多头排列
+- 价格从 $68,500 回调到 $68,000
+- 回调到 EMA20 附近获得支撑
+- 15m RSI 从 65 回落到 52
+
+**正确做法**:
+- ✅ **在 $67,800 limit 挂多单**(EMA20 支撑位)
+- ✅ 止损:$67,100(1%)
+- ✅ 目标:$69,200(+2%)
+- ❌ 不要市价追高,等待回调
+
+### 示例4:价格加速 → 强制 HOLD(无论什么形态)
+**市场状态**:
+- BTC 5m 连续 3 根大阳线
+- 15m RSI = 72(极端超买)
+- 价格偏离 EMA5 = 2.3%
+
+**正确做法**:
+- ✅ **HOLD 观望**
+- ❌ **禁止 market 入场**(这是追涨!)
+- ❌ **禁止 limit 入场**(价格不合适)
+- 等待回调或 RSI 回到正常区间
## 历史信号参考(非常重要!)
**如果提供了上一轮的分析信号,必须仔细参考它:**
diff --git a/backend/app/services/multi_llm_service.py b/backend/app/services/multi_llm_service.py
index 710b562..343ab59 100644
--- a/backend/app/services/multi_llm_service.py
+++ b/backend/app/services/multi_llm_service.py
@@ -2,6 +2,7 @@
多模型LLM服务 - 支持智谱AI和DeepSeek
"""
from typing import Optional, List, Dict, Any
+from datetime import datetime, timedelta
from app.config import get_settings
from app.utils.logger import logger
@@ -16,6 +17,7 @@ except ImportError:
# DeepSeek (使用OpenAI兼容接口)
try:
from openai import OpenAI
+ from openai import APIStatusError, APIError
OPENAI_AVAILABLE = True
except ImportError:
OPENAI_AVAILABLE = False
@@ -25,6 +27,9 @@ except ImportError:
class MultiLLMService:
"""多模型LLM服务类"""
+ # 余额错误通知冷却时间(秒)
+ BALANCE_ERROR_COOLDOWN = 3600 # 1小时内只通知一次
+
def __init__(self):
"""初始化多模型LLM服务"""
settings = get_settings()
@@ -33,6 +38,9 @@ class MultiLLMService:
self.current_model = None
self.model_info = {}
+ # 余额错误通知时间记录
+ self._balance_error_notified = {} # {provider: last_notified_time}
+
# 初始化智谱AI
if ZHIPUAI_AVAILABLE and settings.zhipuai_api_key:
try:
@@ -109,6 +117,87 @@ class MultiLLMService:
logger.error(f"模型不可用: {provider}")
return False
+ def _is_balance_error(self, error: Exception, provider: str) -> bool:
+ """
+ 检查错误是否是余额不足错误
+
+ Args:
+ error: 异常对象
+ provider: LLM提供商
+
+ Returns:
+ 是否是余额不足错误
+ """
+ error_str = str(error).lower()
+ error_type = type(error).__name__
+
+ # DeepSeek 余额错误
+ if provider == 'deepseek':
+ # APIStatusError: Error code: 402 - {'error': {'message': 'Insufficient Balance'
+ if '402' in error_str and 'insufficient balance' in error_str:
+ return True
+ if 'balance' in error_str and 'insufficient' in error_str:
+ return True
+
+ # 智谱AI 余额错误
+ elif provider == 'zhipu':
+ # 常见错误信息
+ if '余额' in error_str or 'balance' in error_str:
+ if 'insufficient' in error_str or '不足' in error_str:
+ return True
+ if error_type == 'APIError' and '130' in error_str: # 智谱错误码130表示余额不足
+ return True
+
+ return False
+
+ async def _notify_balance_error(self, provider: str, error: Exception):
+ """
+ 发送余额不足的Telegram通知
+
+ Args:
+ provider: LLM提供商
+ error: 异常对象
+ """
+ # 检查冷却时间
+ now = datetime.now()
+ last_notified = self._balance_error_notified.get(provider)
+ if last_notified:
+ time_since_last = (now - last_notified).total_seconds()
+ if time_since_last < self.BALANCE_ERROR_COOLDOWN:
+ logger.info(f"{provider} 余额错误通知冷却中,剩余 {int(self.BALANCE_ERROR_COOLDOWN - time_since_last)} 秒")
+ return
+
+ # 发送通知
+ try:
+ from app.services.telegram_service import get_telegram_service
+ telegram = get_telegram_service()
+
+ provider_name = {
+ 'zhipu': '智谱AI (GLM-4)',
+ 'deepseek': 'DeepSeek'
+ }.get(provider, provider)
+
+ message = f"""🚨 LLM API 余额不足警告
+
+━━━━━━━━━━━━━━━━━━━━
+
+📊 服务商: {provider_name}
+⚠️ 错误类型: 余额不足 (Insufficient Balance)
+🔍 错误信息: {str(error)[:200]}
+
+━━━━━━━━━━━━━━━━━━━━
+
+请及时充值,否则智能体将无法正常工作"""
+
+ await telegram.send_message(message, parse_mode="HTML")
+ logger.warning(f"已发送 {provider} 余额不足Telegram通知")
+
+ # 记录通知时间
+ self._balance_error_notified[provider] = now
+
+ except Exception as e:
+ logger.error(f"发送余额不足通知失败: {e}")
+
def chat(
self,
messages: List[Dict[str, str]],
@@ -214,6 +303,22 @@ class MultiLLMService:
logger.error(f"LLM调用失败: {type(e).__name__}: {e}")
import traceback
logger.error(f"详细错误: {traceback.format_exc()}")
+
+ # 检查是否是余额错误,发送Telegram通知
+ if self._is_balance_error(e, provider):
+ import asyncio
+ try:
+ # 在新的事件循环中运行(避免嵌套事件循环问题)
+ loop = asyncio.get_event_loop()
+ if loop.is_running():
+ # 如果在异步上下文中,创建任务
+ asyncio.create_task(self._notify_balance_error(provider, e))
+ else:
+ # 如果没有运行的循环,直接运行
+ asyncio.run(self._notify_balance_error(provider, e))
+ except Exception as notify_error:
+ logger.error(f"发送余额通知异常: {notify_error}")
+
return None
def chat_stream(
@@ -287,6 +392,22 @@ class MultiLLMService:
logger.error(f"LLM流式调用失败: {type(e).__name__}: {e}")
import traceback
logger.error(f"详细错误: {traceback.format_exc()}")
+
+ # 检查是否是余额错误,发送Telegram通知
+ if self._is_balance_error(e, provider):
+ import asyncio
+ try:
+ # 在新的事件循环中运行(避免嵌套事件循环问题)
+ loop = asyncio.get_event_loop()
+ if loop.is_running():
+ # 如果在异步上下文中,创建任务
+ asyncio.create_task(self._notify_balance_error(provider, e))
+ else:
+ # 如果没有运行的循环,直接运行
+ asyncio.run(self._notify_balance_error(provider, e))
+ except Exception as notify_error:
+ logger.error(f"发送余额通知异常: {notify_error}")
+
return
def analyze_intent(self, user_message: str) -> Dict[str, Any]:
diff --git a/scripts/analyze_eth_4h.py b/scripts/analyze_eth_4h.py
new file mode 100644
index 0000000..5b9cde7
--- /dev/null
+++ b/scripts/analyze_eth_4h.py
@@ -0,0 +1,219 @@
+#!/usr/bin/env python3
+"""
+分析ETH过去4小时的市场数据,检查为什么没有开仓
+"""
+import sys
+import os
+import asyncio
+from datetime import datetime, timedelta
+
+# 添加项目路径
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'backend'))
+
+from app.services.binance_service import binance_service
+from app.crypto_agent.market_signal_analyzer import MarketSignalAnalyzer
+from app.crypto_agent.trading_decision_maker import TradingDecisionMaker
+from app.services.paper_trading_service import get_paper_trading_service
+from app.utils.logger import logger
+
+
+async def analyze_eth_4h():
+ """分析ETH过去4小时的情况"""
+ print("=" * 80)
+ print("📊 ETH 过去4小时市场分析")
+ print("=" * 80)
+ print(f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
+
+ # 1. 获取服务
+ binance = binance_service
+ signal_analyzer = MarketSignalAnalyzer()
+ decision_maker = TradingDecisionMaker()
+ trading_service = get_paper_trading_service()
+
+ # 2. 获取当前行情
+ print("\n📈 当前行情:")
+ current_price = binance.get_current_price('ETHUSDT')
+ stats = binance.get_24h_stats('ETHUSDT')
+
+ if current_price:
+ print(f" 价格: ${current_price:,.2f}")
+ if stats:
+ print(f" 涨跌: {stats.get('priceChangePercent', 0):+.2f}%")
+ print(f" 成交量: {stats.get('volume', 0):,.0f}")
+
+ # 3. 获取K线数据(多周期)
+ print("\n📊 获取K线数据...")
+ data = binance.get_multi_timeframe_data('ETHUSDT')
+
+ if not data:
+ print("❌ 无法获取K线数据")
+ return
+
+ # 显示各周期数据量
+ for tf, df in data.items():
+ print(f" {tf}: {len(df)} 条数据")
+
+ # 4. 检查过去4小时的价格波动
+ print("\n📊 过去4小时价格波动:")
+ df_15m = data.get('15m')
+ if df_15m is not None and len(df_15m) > 0:
+ # 获取过去4小时的15分钟数据(16根)
+ recent_4h = df_15m.tail(16)
+ if len(recent_4h) > 0:
+ high = recent_4h['high'].max()
+ low = recent_4h['low'].min()
+ start_price = recent_4h.iloc[0]['close']
+ end_price = recent_4h.iloc[-1]['close']
+ volatility = ((high - low) / start_price) * 100
+ price_change = ((end_price - start_price) / start_price) * 100
+
+ print(f" 最高: ${high:,.2f}")
+ print(f" 最低: ${low:,.2f}")
+ print(f" 波动幅度: {volatility:.2f}%")
+ print(f" 价格变化: {price_change:+.2f}%")
+
+ # 5. 运行信号分析
+ print("\n🔍 信号分析:")
+ print("-" * 80)
+
+ analysis = await signal_analyzer.analyze('ETHUSDT', data)
+
+ # 显示分析结果
+ print(f"\n市场状态: {analysis.get('analysis_summary', 'N/A')}")
+
+ # 新闻情绪
+ if analysis.get('news_sentiment'):
+ sentiment_map = {'positive': '📈 积极', 'negative': '📉 消极', 'neutral': '➖ 中性'}
+ print(f"新闻情绪: {sentiment_map.get(analysis['news_sentiment'], analysis['news_sentiment'])}")
+
+ # 信号
+ signals = analysis.get('signals', [])
+ if signals:
+ print(f"\n🎯 生成 {len(signals)} 个信号:")
+ for sig in signals:
+ action = sig.get('action', 'wait')
+ action_map = {'buy': '🟢 做多', 'sell': '🔴 做空', 'wait': '⏸️ 观望'}
+ grade = sig.get('grade', 'D')
+ confidence = sig.get('confidence', 0)
+
+ print(f"\n {action_map.get(action, action)} [{grade}级] {confidence}%")
+ print(f" 入场: ${sig.get('entry_price', 0):,.2f}")
+ print(f" 止损: ${sig.get('stop_loss', 0):,.2f}")
+ print(f" 止盈: ${sig.get('take_profit', 0):,.2f}")
+ print(f" 入场方式: {sig.get('entry_type', 'N/A')}")
+
+ if sig.get('reason'):
+ print(f" 理由: {sig['reason']}")
+ if sig.get('risk_warning'):
+ print(f" ⚠️ 风险: {sig['risk_warning']}")
+ else:
+ print("\n⏸️ 无交易信号")
+
+ # 6. 检查当前持仓
+ print("\n💼 当前持仓:")
+ account = trading_service.get_account_status()
+ active_orders = trading_service.get_active_orders()
+
+ print(f" 余额: ${account['current_balance']:,.2f}")
+ print(f" 已用保证金: ${account['used_margin']:,.2f}")
+ print(f" 持仓数量: {len(active_orders)}")
+
+ if active_orders:
+ for order in active_orders:
+ print(f"\n {order.symbol} {order.side.value}")
+ print(f" 入场价: ${order.filled_price:,.2f}")
+ print(f" 当前价: ${current_price:,.2f}")
+ print(f" 保证金: ${order.margin:,.2f}")
+ print(f" 杠杆: {order.leverage}x")
+ else:
+ print(" 无持仓")
+
+ # 7. 模拟交易决策
+ print("\n🤖 交易决策分析:")
+ print("-" * 80)
+
+ if signals:
+ # 对每个信号做决策
+ for sig in signals[:3]: # 只看前3个
+ decision = await decision_maker.make_decision(
+ symbol='ETHUSDT',
+ signals=[sig],
+ current_data=data,
+ current_price=current_price
+ )
+
+ print(f"\n信号: {sig.get('action', 'wait')} | {sig.get('grade')}级 | {sig.get('confidence')}%")
+ print(f"决策: {decision['decision']}")
+ print(f"理由: {decision.get('reason', 'N/A')}")
+
+ if decision.get('orders_to_open'):
+ print(f"开仓: {len(decision['orders_to_open'])} 个")
+ if decision.get('orders_to_close'):
+ print(f"平仓: {decision['orders_to_close']}")
+ else:
+ print("无信号,不生成决策")
+
+ # 8. 检查是否有追涨杀跌的情况
+ print("\n⚠️ 追涨杀跌检测:")
+ print("-" * 80)
+
+ if df_15m is not None and len(df_15m) >= 5:
+ recent = df_15m.tail(5)
+
+ # 计算RSI
+ import pandas_ta as ta
+ rsi = recent['close'].ta.rsi(length=14)
+ current_rsi = rsi.iloc[-1] if not rsi.empty else 50
+
+ # 检查连续大阳线/阴线
+ big_moves = 0
+ for i in range(1, min(4, len(recent))):
+ change = abs((recent.iloc[i]['close'] - recent.iloc[i-1]['close']) / recent.iloc[i-1]['close']) * 100
+ if change >= 1.0:
+ big_moves += 1
+
+ # 检查价格偏离EMA
+ ema5 = recent['close'].ta.ema(length=5)
+ current_price = recent.iloc[-1]['close']
+ deviation = abs((current_price - ema5.iloc[-1]) / ema5.iloc[-1]) * 100 if not ema5.empty else 0
+
+ print(f" RSI(14): {current_rsi:.1f}")
+ print(f" 连续大K线: {big_moves} 根")
+ print(f" 偏离EMA5: {deviation:.2f}%")
+
+ # 判断是否满足追涨杀跌条件
+ is_chasing = False
+ reasons = []
+
+ if current_rsi > 65 or current_rsi < 35:
+ is_chasing = True
+ reasons.append(f"RSI处于极端区间 ({current_rsi:.1f})")
+
+ if big_moves >= 2:
+ is_chasing = True
+ reasons.append(f"连续{big_moves}根大K线")
+
+ if deviation > 1.5:
+ is_chasing = True
+ reasons.append(f"价格偏离EMA5超过1.5%")
+
+ if is_chasing:
+ print(f"\n 🚨 检测到追涨杀跌条件!")
+ for reason in reasons:
+ print(f" - {reason}")
+ print(f"\n → 这是防止开仓的原因之一")
+ else:
+ print(f"\n ✅ 未检测到追涨杀跌条件")
+
+ print("\n" + "=" * 80)
+ print("分析完成")
+ print("=" * 80)
+
+
+if __name__ == "__main__":
+ try:
+ asyncio.run(analyze_eth_4h())
+ except Exception as e:
+ print(f"\n❌ 分析失败: {e}")
+ import traceback
+ traceback.print_exc()