update
This commit is contained in:
parent
7e58b5203b
commit
c080962373
@ -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 回到正常区间
|
||||
|
||||
## 历史信号参考(非常重要!)
|
||||
**如果提供了上一轮的分析信号,必须仔细参考它:**
|
||||
|
||||
@ -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"""🚨 <b>LLM API 余额不足警告</b>
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📊 <b>服务商:</b> {provider_name}
|
||||
⚠️ <b>错误类型:</b> 余额不足 (Insufficient Balance)
|
||||
🔍 <b>错误信息:</b> {str(error)[:200]}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
<i>请及时充值,否则智能体将无法正常工作</i>"""
|
||||
|
||||
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]:
|
||||
|
||||
219
scripts/analyze_eth_4h.py
Normal file
219
scripts/analyze_eth_4h.py
Normal file
@ -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()
|
||||
Loading…
Reference in New Issue
Block a user