This commit is contained in:
aaron 2026-02-28 22:59:12 +08:00
parent e6560b340d
commit 0a637053fc
5 changed files with 442 additions and 260 deletions

View File

@ -1679,7 +1679,7 @@ class CryptoAgent:
def _validate_data(self, data: Dict[str, pd.DataFrame]) -> bool:
"""验证数据完整性"""
required_intervals = ['5m', '15m', '1h', '4h']
required_intervals = ['1m', '5m', '15m', '30m', '1h']
for interval in required_intervals:
if interval not in data or data[interval].empty:
return False

View File

@ -47,147 +47,156 @@ class MarketSignalAnalyzer:
- **顺势而为**在大趋势方向上寻找入场点
- **严控风险**每次交易风险不超过本金的2%
## 零、趋势方向判断(第一步,最重要!)
**在分析任何信号之前先判断当前趋势方向和强度**
## 零、日内交易核心理念(必须遵守!)
### 趋势判断标准(使用 EMA 和 均线系统)
**上升趋势多头市场**
- EMA20 > EMA50 > EMA200短中长期均线多头排列
- 价格站稳在 EMA20 之上
- MA5 > MA10 > MA20 > MA50
- 最近高点逐步抬高低点也逐步抬高
### 🎯 日内交易的本质
**日内交易 = 当日进出 + 快速获利 + 严控盈亏比**
**下降趋势空头市场**
- EMA20 < EMA50 < EMA200短中长期均线空头排列
- 价格持续在 EMA20 之下
- MA5 < MA10 < MA20 < MA50
- 最近高点逐步降低低点也逐步降低
### ⚠️ 铁律(违反即失败)
1. **盈亏比第一**所有交易必须满足盈亏比 1:2优选 1:3
- 盈亏比 = (目标盈利 - 入场价) / (入场价 - 止损价)
- 做多目标价 > 入场价 > 止损价
- 做空目标价 < 入场价 < 止损价
- 如果盈亏比 < 1:2**绝对不要开仓**
**震荡市无明确趋势**
- 均线纠缠无明显排列
- 价格在 EMA20 上下波动
- 高点低点无规律
- 此时可双向交易但降低仓位
2. **快进快出**
- 单笔持仓不超过 4 小时
- 达到目标立即平仓不贪心
- 未达到目标但超过 2 小时考虑平仓观望
### 趋势强度判断
- **强趋势**均线完美排列 + 价格远离均线 + 成交量配合
- **中等趋势**均线有排列 + 价格偶尔回踩均线
- **弱趋势/震荡**均线纠缠 + 价格在均线上下反复
3. **严格止损**
- 止损幅度1-2%最大不超过 2%
- 触及止损立即离场不要犹豫
- 不要移动止损除非是移动止盈保护利润
### 顺势交易规则(必须执行)
| 当前趋势 | 允许操作 | 条件 |
|---------|---------|------|
| **强上升趋势** | 只做多 | 回调到支撑位RSI超卖区金叉 |
| **强上升趋势** | 严禁做空 | 除非出现明确的顶背离+放量反转信号 |
| **强下降趋势** | 只做空 | 反弹到阻力位RSI超买区死叉 |
| **强下降趋势** | 严禁做多 | 除非出现明确的底背离+放量反转信号 |
| **震荡市** | 双向交易 | 但降低仓位轻仓提高止损要求 |
| **趋势不明确** | 观望为主 | 等待趋势明确后再入场 |
4. **日内平仓**
- 不建议持仓过夜
- 收盘前 30 分钟逐步平仓
- 避免隔夜风险
### 逆势交易的条件(极其严格)
**只有在满足以下全部条件时才允许考虑逆势交易**
1. **多重反转信号**
- 明确的背离顶背离或底背离
- 关键形态反转头肩顶/双顶/吞没形态
- 放量突破关键位
2. **多周期确认**4h1h15m 三个周期同时出现反转信号
3. **风险收益比合理**潜在盈利至少是风险的3倍以上
4. **降低仓位**逆势交易必须轻仓不超过顺势仓位的50%
### 日内交易时间框架(调整后)
**主周期**30m日内趋势
**入场周期**15m寻找入场点
**精确入场**5m确认时机
**超精确入场**1m最后确认可选
**趋势参考**1h当日大方向
**如果不符合上述条件即使有买入/卖出信号也必须选择 hold观望**
### 日内交易参数
| 参数 | 设定值 |
|------|--------|
| 止损幅度 | 1-2%最大2% |
| 目标盈利 | 2-3%日内快速获利 |
| 盈亏比要求 | 1:2优选1:3 |
| 单笔持仓时长 | 不超过4小时 |
| 仓位大小 | 轻仓为主light/micro |
## 零点五、趋势位置判断和左侧交易(短线交易核心)
## 零点一、趋势方向判断(简化版,适配日内)
**日内交易更关注 30m 15m1h 作为大方向参考**
### 🚨 避免盲目追涨杀跌
**顺势交易不等于追涨杀跌趋势到了晚期要警惕反转**
### 快速趋势判断30m + 15m
**看涨日内做多为主**
- 30m: EMA20 > EMA50价格在 EMA20 之上
- 15m: 短期均线向上价格站上 EMA5
- 30m 15m 同向向上
### 趋势三阶段判断
**上升趋势的三个阶段**
1. **早期**启动阶段
- 均线刚开始多头排列
- 价格刚刚站上所有均线
- 成交量温和放大
- 可以积极做多
**看跌日内做空为主**
- 30m: EMA20 < EMA50价格在 EMA20 之下
- 15m: 短期均线向下价格跌破 EMA5
- 30m 15m 同向向下
2. **中期**加速阶段
- 均线完美排列价格远离均线
- 成交量持续放大
- RSI 50-70 之间
- 可以顺势做多但警惕超买
**震荡日内观望为主**
- 30m: 均线纠缠价格反复穿越 EMA20
- 15m: 无明确方向
- 此时最好观望或支撑位多压力位空轻仓
3. **晚期**过度延伸
- 价格严重偏离均线> 5%
- RSI > 75超买区
- 布林带开口极大价格在上轨之外
- 出现顶背离信号
- 不要追多警惕反转
### 日内顺势规则
| 30m 趋势 | 15m 趋势 | 允许操作 | 盈亏比要求 |
|---------|----------|---------|-----------|
| **上升** | 上升 | 做多 | 1:2 |
| **上升** | 下跌 | 回调做多 | 1:3 |
| **下降** | 下降 | 做空 | 1:2 |
| **下降** | 上升 | 反弹做空 | 1:3 |
| **震荡** | 任意 | 观望或轻仓 | 1:3 |
**下降趋势的三个阶段**
1. **早期**均线刚开始空头排列刚刚破位 可以积极做空
2. **中期**均线完美空头排列加速下跌 可以顺势做空警惕超卖
3. **晚期**价格严重偏离均线RSI < 25 不要追空警惕反弹
## 零点五、日内交易实战策略
### 过度延伸的信号(必须警惕)
### 🎯 三种日内入场方式
**上涨过度延伸的信号**
- RSI > 75 且出现顶背离
- 价格偏离 MA5 > 5%
- 布林带上轨之外且开口极大
- 连续 3 根以上大阳线
- 极端放量后价格滞涨
- 这时不要追多考虑减仓或做空
#### 策略1突破追入适合强势行情
**什么时候追**
- 30m 15m 同向趋势明确
- 放量突破关键位阻力/支撑
- 15m 5m 级别正在加速
**下跌过度延伸的信号**
- RSI < 25 且出现底背离
- 价格偏离 MA5 > 5%
- 布林带下轨之外且开口极大
- 连续 3 根以上大阴线
- 极端放量后价格企稳
- 这时不要追空考虑平仓或做多
**追入必须满足**
- 盈亏比 1:2
- 止损1-2%
- 目标2-3%
- 仓位light micro
### 左侧交易规则(特定条件下可尝试)
** 追入的危险区**
- 15m RSI > 70 < 30
- 价格偏离均线 > 3%
- 连续 3 根以上大阳/大阴
**什么时候可以尝试左侧交易**
#### 策略2回调/反弹入场(稳健策略)
**回调做多**30m 上升15m 回调
- 回调到 EMA20 或支撑位
- RSI 回落到 40-50
- 缩量后放量反弹
1. **上升趋势晚期出现明确反转信号**
- RSI 顶背离 + 价格触及布林带上轨外
- 放量滞涨 + 出现大阴线
- 关键阻力位出现明显反转形态十字星吞没
- 可以尝试做空小仓位light micro
**反弹做空**30m 下降15m 反弹
- 反弹到 EMA20 或压力位
- RSI 反弹到 50-60
- 缩量后放量下跌
2. **下降趋势晚期出现明确反转信号**
- RSI 底背离 + 价格触及布林带下轨外
- 地量后企稳 + 出现大阳线
- 关键支撑位出现明显反转形态锤子线早晨之星
- 可以尝试做多小仓位light micro
**回调入场要求**
- 盈亏比 1:3更严格要求
- 止损支撑/压力位外侧 1%
- 目标2-3%
3. **小级别反转信号**
- 5m/15m 周期出现明显的背离信号
- 4h/1h 大周期仍在趋势中但小级别开始反转
- 可以尝试小仓位反向但严格止损
#### 策略3震荡双向交易仅限震荡市
- 支撑位做多压力位做空
- 严格止损 1%
- 目标 1.5-2%
- 盈亏比 1:2
**左侧交易必须满足的条件**
- 至少 2 个反转信号同时出现
- 有明确的关键点位支撑/阻力
- 严格止损不超过保证金的 1%
- 小仓位light micro
### 🚨 盈亏比检查清单(必须执行!)
### 实战示例
**在输出任何交易信号前必须计算盈亏比**
** 错误盲目追涨**
```
场景BTC 多头排列连续上涨 5%
MA 完美多头排列RSI = 68
错误分析趋势仍在延续追多
正确分析价格已偏离 MA5 > 3%接近过度延伸观望或小仓位做空
做多盈亏比 = (目标价 - 入场价) / (入场价 - 止损价)
做空盈亏比 = (入场价 - 目标价) / (止损价 - 入场价)
示例
- BTC 入场 65000止损 64300-1%目标 66300+2%
- 盈亏比 = (66300 - 65000) / (65000 - 64300) = 1300 / 700 1.86 可行
```
** 正确等待反转信号**
**如果盈亏比 < 1:2不要输出信号**
### 日内交易决策流程
```
场景BTC 多头排列但出现顶背离
4h 多头排列 RSI 出现顶背离
15m 价格创新高但 RSI 未创新高
正确分析趋势可能反转小仓位尝试做空
第一步检查盈亏比
盈亏比 < 1:2 不开仓
盈亏比 1:2 继续检查
第二步判断趋势方向
30m 上升 + 15m 上升 做多策略1或2
30m 下降 + 15m 下降 做空策略1或2
30m 震荡 观望或双向轻仓策略3
趋势不明确 观望
第三步选择入场方式
放量突破 market 立即入场
等待回调 limit 挂单入场
第四步设置止损止盈
止损1-2%最大不超过 2%
目标2-3%快速获利
验证盈亏比 1:2
```
## 一、量价分析(重要)
@ -247,19 +256,19 @@ MA 完美多头排列RSI = 68
- 布林带收口即将变盘
- 布林带开口趋势启动
### 均线系统(趋势判断核心
- **多头排列**MA5 > MA10 > MA20 > MA50强势上涨趋势回调做多
- **空头排列**MA5 < MA10 < MA20 < MA50强势下跌趋势反弹做空
- **价格与 MA 的关系**
- 价格站稳 MA5/MA10 上方短线上涨
- 价格突破 MA20中线转多
- 价格跌破 MA20中线转空
- MA50 是中期趋势的分水岭
### 均线系统(趋势判断核心 - 使用 EMA
- **多头排列**EMA5 > EMA10 > EMA20 > EMA50强势上涨趋势回调做多
- **空头排列**EMA5 < EMA10 < EMA20 < EMA50强势下跌趋势反弹做空
- **价格与 EMA 的关系**
- 价格站稳 EMA5/EMA10 上方短线上涨
- 价格突破 EMA20中线转多
- 价格跌破 EMA20中线转空
- EMA50 是中期趋势的分水岭
- **均线金叉死叉**
- MA5 上穿 MA10短线买入信号
- MA5 下穿 MA10短线卖出信号
- MA10 上穿 MA20中线买入信号
- MA10 下穿 MA20中线卖出信号
- EMA5 上穿 EMA10短线买入信号
- EMA5 下穿 EMA10短线卖出信号
- EMA10 上穿 EMA20中线买入信号
- EMA10 下穿 EMA20中线卖出信号
## 四、新闻舆情分析
结合最新市场新闻判断
@ -272,28 +281,29 @@ MA 完美多头排列RSI = 68
**多周期共振是提高信号质量的核心方法**
### 周期层级关系
- **4h趋势层**决定中期大方向
- **1h主周期**主要交易周期
- **1h当日大方向**判断当日的主要趋势方向
- **30m日内趋势层**决定日内主趋势
- **15m入场层**寻找入场时机
- **5m精确入场**确认最佳入场点
- **1m超精确**最后确认可选
### 共振判断标准
**强共振A级信号**
- 所有周期趋势同向 4h多 + 1h多 + 15m多
- 30m + 15m + 5m 趋势同向 30m多 + 15m多 + 5m多
- 多周期 RSI 同时超买/超卖后出现背离
- 多周期 MA 同时金叉/死叉
- 多周期 EMA 同时金叉/死叉
**中等共振B级信号**
- 大周期4h+1h同向
- 主周期1h技术指标明确
- 30m + 15m 同向
- 主周期15m技术指标明确
**弱共振C级信号**
- 只有单一周期信号
- 多周期方向不一致
### 实战策略
- **顺势交易**4h 1h 同向时 15m/5m 寻找入场点
- **逆势谨慎**只有 1h 信号但 4h 反向时降低置信度
- **顺势交易**30m 15m 同向时 5m/1m 寻找入场点
- **逆势谨慎**只有 15m 信号但 30m 反向时降低置信度
- **突破交易**多周期同时突破关键位信号最强
## 六、入场方式
@ -380,7 +390,7 @@ MA 完美多头排列RSI = 68
### ✅ 允许输出新信号的情况
只有在以下情况之一时才输出新的交易信号
1. **趋势反转**上一轮判断的趋势发生了明确反转
- 例如上一轮看多MA多头排列现在转为空头排列
- 例如上一轮看多EMA多头排列现在转为空头排列
2. **从观望到机会**上一轮是观望无信号现在出现了明确的交易机会
3. **上一轮信号已失效**
- 价格已触及上一轮的止损或止盈价位
@ -501,15 +511,15 @@ MA 完美多头排列RSI = 68
bb_lower = df['bb_lower'].iloc[-1]
context_parts.append(f"布林带: 上轨 {bb_upper:.2f}, 下轨 {bb_lower:.2f}")
# 均线系统
context_parts.append(f"\n## 均线系统")
df_1h = data.get('1h')
if df_1h is not None and len(df_1h) > 0:
latest = df_1h.iloc[-1]
context_parts.append(f"MA5: {latest.get('ma5', 'N/A')}")
context_parts.append(f"MA10: {latest.get('ma10', 'N/A')}")
context_parts.append(f"MA20: {latest.get('ma20', 'N/A')}")
context_parts.append(f"MA50: {latest.get('ma50', 'N/A')}")
# 均线系统(使用 30m 作为日内主周期)
context_parts.append(f"\n## 均线系统 (30m 日内主趋势)")
df_30m = data.get('30m')
if df_30m is not None and len(df_30m) > 0:
latest = df_30m.iloc[-1]
context_parts.append(f"EMA5: {latest.get('ma5', 'N/A')}")
context_parts.append(f"EMA10: {latest.get('ma10', 'N/A')}")
context_parts.append(f"EMA20: {latest.get('ma20', 'N/A')}")
context_parts.append(f"EMA50: {latest.get('ma50', 'N/A')}")
# 判断均线排列
ma5 = latest.get('ma5', 0)
@ -519,9 +529,9 @@ MA 完美多头排列RSI = 68
if all([ma5, ma10, ma20, ma50]):
if ma5 > ma10 > ma20 > ma50:
context_parts.append("均线排列: 多头排列 📈")
context_parts.append("均线排列: 多头排列 📈 (EMA5 > EMA10 > EMA20 > EMA50)")
elif ma5 < ma10 < ma20 < ma50:
context_parts.append("均线排列: 空头排列 📉")
context_parts.append("均线排列: 空头排列 📉 (EMA5 < EMA10 < EMA20 < EMA50)")
else:
context_parts.append("均线排列: 交织,方向不明")
@ -583,96 +593,118 @@ MA 完美多头排列RSI = 68
return "新闻获取失败"
def _analyze_trend_position(self, data: Dict[str, pd.DataFrame]) -> str:
"""分析趋势所处的位置(早期、中期、晚期"""
"""分析趋势位置和日内交易机会(使用 EMA"""
try:
df_1h = data.get('1h')
if df_1h is None or len(df_1h) < 50:
df_30m = data.get('30m')
df_15m = data.get('15m')
if df_30m is None or len(df_30m) < 50:
return ""
latest = df_1h.iloc[-1]
current_price = float(latest['close'])
latest_30m = df_30m.iloc[-1]
current_price = float(latest_30m['close'])
# 获取均线
ma5 = latest.get('ma5')
ma10 = latest.get('ma10')
ma20 = latest.get('ma20')
# 获取日内级别 EMA30m
ema5_30m = latest_30m.get('ma5') # 实际是 ema5
ema10_30m = latest_30m.get('ma10') # 实际是 ema10
ema20_30m = latest_30m.get('ma20') # 实际是 ema20
if not all([ma5, ma10, ma20]):
if not all([ema5_30m, ema10_30m, ema20_30m]):
return ""
# 计算价格偏离度
deviation_ma5 = abs(current_price - ma5) / ma5 * 100
deviation_ma20 = abs(current_price - ma20) / ma20 * 100
# 获取 RSI
rsi = latest.get('rsi', 50)
# 获取布林带
bb_upper = latest.get('bb_upper')
bb_lower = latest.get('bb_lower')
analysis = []
# 判断趋势方向
if ma5 > ma10 > ma20:
trend = "上涨"
# 判断是否过度延伸
overextended_signals = []
if deviation_ma5 > 5:
overextended_signals.append(f"价格偏离 MA5 > 5% ({deviation_ma5:.1f}%)")
if rsi > 75:
overextended_signals.append(f"RSI 超买 ({rsi:.0f})")
if bb_upper and current_price > bb_upper:
overextended_signals.append("价格突破布林带上轨")
if overextended_signals:
analysis.append(f"⚠️ 警告:{trend}趋势可能过度延伸")
for signal in overextended_signals:
analysis.append(f" - {signal}")
analysis.append(f" → 不要追{trend},警惕反转")
elif deviation_ma5 > 3 or rsi > 65:
analysis.append(f"📍 位置:{trend}趋势中期")
analysis.append(f" → 价格偏离 MA5 {deviation_ma5:.1f}%RSI {rsi:.0f}")
analysis.append(f" → 可以顺势{trend},但警惕超买")
else:
analysis.append(f"✅ 位置:{trend}趋势早期")
analysis.append(f" → 可以积极{trend}")
elif ma5 < ma10 < ma20:
trend = "下跌"
overextended_signals = []
if deviation_ma5 > 5:
overextended_signals.append(f"价格偏离 MA5 > 5% ({deviation_ma5:.1f}%)")
if rsi < 25:
overextended_signals.append(f"RSI 超卖 ({rsi:.0f})")
if bb_lower and current_price < bb_lower:
overextended_signals.append("价格跌破布林带下轨")
if overextended_signals:
analysis.append(f"⚠️ 警告:{trend}趋势可能过度延伸")
for signal in overextended_signals:
analysis.append(f" - {signal}")
analysis.append(f" → 不要追{trend},警惕反弹")
elif deviation_ma5 > 3 or rsi < 35:
analysis.append(f"📍 位置:{trend}趋势中期")
analysis.append(f" → 价格偏离 MA5 {deviation_ma5:.1f}%RSI {rsi:.0f}")
analysis.append(f" → 可以顺势{trend},但警惕超卖")
else:
analysis.append(f"✅ 位置:{trend}趋势早期")
analysis.append(f" → 可以积极{trend}")
# 判断日内趋势30m EMA 为主)
if ema5_30m > ema10_30m > ema20_30m:
intraday_trend = "上升"
intraday_emoji = "📈"
elif ema5_30m < ema10_30m < ema20_30m:
intraday_trend = "下跌"
intraday_emoji = "📉"
else:
analysis.append(" 位置:震荡市")
intraday_trend = "震荡"
intraday_emoji = ""
analysis = [f"日内趋势(30m EMA): {intraday_emoji} {intraday_trend}"]
# 检查15分钟级别入场时机
if df_15m is not None and len(df_15m) >= 20:
latest_15m = df_15m.iloc[-1]
rsi_15m = latest_15m.get('rsi', 50)
ema5_15m = latest_15m.get('ma5') # 实际是 ema5
ema20_15m = latest_15m.get('ma20') # 实际是 ema20
# 检查短期动能
if len(df_15m) >= 5:
recent_closes = df_15m['close'].iloc[-5:].values
is_accelerating = all(recent_closes[i] > recent_closes[i-1] for i in range(1, 5))
else:
is_accelerating = False
# 计算价格偏离
if ema5_15m and ema20_15m:
deviation_ema5_15m = abs(current_price - ema5_15m) / ema5_15m * 100
distance_to_ema20 = abs(current_price - ema20_15m) / ema20_15m * 100
else:
deviation_ema5_15m = 0
distance_to_ema20 = 0
# 检查成交量
df_5m = data.get('5m')
volume_ratio = 1
if df_5m is not None and len(df_5m) >= 20:
vol_latest = df_5m['volume'].iloc[-1]
vol_ma20 = df_5m['volume'].iloc[-20:-1].mean()
volume_ratio = vol_latest / vol_ma20 if vol_ma20 > 0 else 1
# 日内过度延伸检查EMA 反应更快,阈值更严格)
is_overextended = (
(rsi_15m > 70 and intraday_trend == "上升") or
(rsi_15m < 30 and intraday_trend == "下跌") or
deviation_ema5_15m > 3
)
if intraday_trend == "上升":
if is_accelerating and volume_ratio > 1.3 and not is_overextended:
analysis.append(f"15m: 正在加速上涨,放量突破")
analysis.append(f" → 日内追多止损1-2%目标2-3%")
analysis.append(f" → 盈亏比要求 >= 1:2")
elif distance_to_ema20 < 1 and deviation_ema5_15m > 1.5:
analysis.append(f"15m: 回调到 EMA20 支撑位")
analysis.append(f" → 支撑位做多反弹EMA20: ${ema20_15m:.0f}")
analysis.append(f" → 止损1%目标2-3%,盈亏比 >= 1:3")
elif is_overextended:
analysis.append(f"⚠️ 15m 过度延伸: RSI {rsi_15m:.0f},偏离 EMA5 {deviation_ema5_15m:.1f}%")
analysis.append(f" → 不要追多,等待回调")
else:
analysis.append(f"15m: 上涨中,可以轻仓做多")
analysis.append(f" → RSI {rsi_15m:.0f},偏离 EMA5 {deviation_ema5_15m:.1f}%")
elif intraday_trend == "下跌":
if is_accelerating and volume_ratio > 1.3 and not is_overextended:
analysis.append(f"15m: 正在加速下跌,放量跌破")
analysis.append(f" → 日内追空止损1-2%目标2-3%")
analysis.append(f" → 盈亏比要求 >= 1:2")
elif distance_to_ema20 < 1 and deviation_ema5_15m > 1.5:
analysis.append(f"15m: 反弹到 EMA20 压力位")
analysis.append(f" → 压力位做空回调EMA20: ${ema20_15m:.0f}")
analysis.append(f" → 止损1%目标2-3%,盈亏比 >= 1:3")
elif is_overextended:
analysis.append(f"⚠️ 15m 过度延伸: RSI {rsi_15m:.0f},偏离 EMA5 {deviation_ema5_15m:.1f}%")
analysis.append(f" → 不要追空,等待反弹")
else:
analysis.append(f"15m: 下跌中,可以轻仓做空")
analysis.append(f" → RSI {rsi_15m:.0f},偏离 EMA5 {deviation_ema5_15m:.1f}%")
else:
analysis.append(f"15m: 震荡,观望或双向轻仓")
analysis.append(f" → 支撑位多,压力位空,盈亏比 >= 1:3")
# 日内交易要点
analysis.append(f"\n💡 日内交易要点:")
analysis.append(f"- 使用 EMA指数移动平均反应更快")
analysis.append(f"- 盈亏比第一: 必须 >= 1:2优选 1:3")
analysis.append(f"- 快进快出: 持仓不超过4小时")
analysis.append(f"- 严格止损: 1-2%最大2%")
analysis.append(f"- 目标盈利: 2-3%(快速获利)")
return "\n".join(analysis) if analysis else ""
@ -1008,15 +1040,15 @@ MA 完美多头排列RSI = 68
}
def _analyze_volatility(self, data: Dict[str, pd.DataFrame]) -> str:
"""分析波动率变化"""
df = data.get('1h')
"""分析波动率变化(使用 30m 作为日内主周期)"""
df = data.get('30m')
if df is None or len(df) < 24 or 'atr' not in df.columns:
return ""
lines = []
# ATR 变化趋势
recent_atr = df['atr'].iloc[-6:].mean() # 最近 6 根
recent_atr = df['atr'].iloc[-6:].mean() # 最近 6 根3小时
older_atr = df['atr'].iloc[-12:-6].mean() # 之前 6 根
if pd.isna(recent_atr) or pd.isna(older_atr) or older_atr == 0:
@ -1028,12 +1060,12 @@ MA 完美多头排列RSI = 68
current_price = float(df['close'].iloc[-1])
atr_percent = current_atr / current_price * 100
lines.append(f"当前 ATR: ${current_atr:.2f} ({atr_percent:.2f}%)")
lines.append(f"当前 ATR (30m): ${current_atr:.2f} ({atr_percent:.2f}%)")
if atr_change > 20:
lines.append(f"**波动率扩张**: ATR 上升 {atr_change:.0f}%趋势可能启动")
lines.append(f"**波动率扩张**: ATR 上升 {atr_change:.0f}%日内趋势可能启动")
elif atr_change < -20:
lines.append(f"**波动率收缩**: ATR 下降 {abs(atr_change):.0f}%,可能即将突破")
lines.append(f"**波动率收缩**: ATR 下降 {abs(atr_change):.0f}%,可能即将变盘")
else:
lines.append(f"波动率稳定: ATR 变化 {atr_change:+.0f}%")

View File

@ -21,7 +21,32 @@ class TradingDecisionMaker:
TRADING_DECISION_PROMPT = """你是一位专业的加密货币交易员。你的核心职责是**仓位管理和风险控制**,而不是盲目开仓。
## 🎯 核心理念
**仓位管理优先于开新仓你的首要任务是管理好现有仓位而不是不断增加新仓位**
**日内交易快进快出 + 盈亏比第一 + 严控风险**
### 🚨 盈亏比铁律(违反即拒绝)
**所有交易必须满足盈亏比 1:2优选 1:3**
```
盈亏比 = (目标盈利 - 入场价) / (入场价 - 止损价)
做多盈亏比 = (止盈价 - 入场价) / (入场价 - 止损价)
做空盈亏比 = (入场价 - 止盈价) / (止损价 - 入场价)
示例
- BTC 做多入场 65000止损 64300-1%止盈 66300+2%
- 盈亏比 = (66300 - 65000) / (65000 - 64300) = 1300 / 700 1.86
如果盈亏比 < 1:2绝对不要开仓
```
### 日内交易参数
| 参数 | 设定值 |
|------|--------|
| 止损幅度 | 1-2%最大2% |
| 目标盈利 | 2-3%日内快速获利 |
| 盈亏比要求 | 1:2优选1:3 |
| 单笔持仓时长 | 不超过4小时 |
| 仓位大小 | 轻仓为主light/micro |
## 决策流程(必须按顺序执行)
@ -704,6 +729,57 @@ class TradingDecisionMaker:
except (ValueError, TypeError):
pass
# 盈亏比检查规则(新增)
prompt_parts.append(f"\n## 盈亏比检查(日内交易铁律)")
prompt_parts.append(f"⚠️ 所有交易必须满足盈亏比 >= 1:2优选 1:3")
prompt_parts.append(f"")
prompt_parts.append(f"盈亏比计算公式:")
prompt_parts.append(f" 做多盈亏比 = (止盈价 - 入场价) / (入场价 - 止损价)")
prompt_parts.append(f" 做空盈亏比 = (入场价 - 止盈价) / (止损价 - 入场价)")
prompt_parts.append(f"")
prompt_parts.append(f"⚠️ 如果盈亏比 < 1:2**不要开仓(返回 HOLD**")
# 计算并显示盈亏比
signals = context.get('signals', [])
if signals:
for sig in signals:
action = sig.get('action')
entry = sig.get('entry_zone')
stop_loss = sig.get('stop_loss')
take_profit = sig.get('take_profit')
if action and entry and stop_loss and take_profit:
try:
entry = float(entry)
stop_loss = float(stop_loss)
take_profit = float(take_profit)
if action == 'buy':
risk_ratio = entry - stop_loss
reward_ratio = take_profit - entry
elif action == 'sell':
risk_ratio = stop_loss - entry
reward_ratio = entry - take_profit
else:
continue
if risk_ratio > 0 and reward_ratio > 0:
rr_ratio = reward_ratio / risk_ratio
rr_percent = (risk_ratio / entry) * 100
if rr_ratio >= 2.0:
status = f"✅ 通过 (1:{rr_ratio:.1f})"
else:
status = f"❌ 拒绝 (1:{rr_ratio:.1f} < 1:2)"
prompt_parts.append(f"\n信号 {action} @ ${entry:,.2f}:")
prompt_parts.append(f" - 止损: ${stop_loss:,.2f} (风险 {risk_ratio:.0f} / {rr_percent:.1f}%)")
prompt_parts.append(f" - 止盈: ${take_profit:,.2f} (盈利 {reward_ratio:.0f})")
prompt_parts.append(f" - 盈亏比: 1:{rr_ratio:.2f} {status}")
except (ValueError, TypeError, ZeroDivisionError):
pass
prompt_parts.append(f"\n请根据以上信息,做出交易决策。")
return "\n".join(prompt_parts)
@ -861,6 +937,62 @@ class TradingDecisionMaker:
f"超过最大仓位金额 (保证金 ${margin:.2f} → 持仓价值 ${position_value:.2f}, 总计 ${new_total_value:,.2f} > ${max_position_value:,.2f})"
)
# 盈亏比检查:所有交易必须满足盈亏比 >= 1:2
action = decision.get('action', '')
entry_price = decision.get('entry_price')
stop_loss = decision.get('stop_loss')
take_profit = decision.get('take_profit')
if action and entry_price and stop_loss and take_profit:
try:
entry_price = float(entry_price)
stop_loss = float(stop_loss)
take_profit = float(take_profit)
# 计算盈亏比
if action == 'buy':
risk = entry_price - stop_loss
reward = take_profit - entry_price
elif action == 'sell':
risk = stop_loss - entry_price
reward = entry_price - take_profit
else:
risk = 0
reward = 0
# 验证价格方向正确性
if action == 'buy':
if stop_loss >= entry_price:
logger.warning(f"⚠️ 决策被拒绝: 做多止损错误 (entry={entry_price}, stop_loss={stop_loss} 应该 < entry)")
return self._get_hold_decision(decision['symbol'], f"做多止损价格错误")
if take_profit <= entry_price:
logger.warning(f"⚠️ 决策被拒绝: 做多止盈错误 (entry={entry_price}, take_profit={take_profit} 应该 > entry)")
return self._get_hold_decision(decision['symbol'], f"做多止盈价格错误")
elif action == 'sell':
if stop_loss <= entry_price:
logger.warning(f"⚠️ 决策被拒绝: 做空止损错误 (entry={entry_price}, stop_loss={stop_loss} 应该 > entry)")
return self._get_hold_decision(decision['symbol'], f"做空止损价格错误")
if take_profit >= entry_price:
logger.warning(f"⚠️ 决策被拒绝: 做空止盈错误 (entry={entry_price}, take_profit={take_profit} 应该 < entry)")
return self._get_hold_decision(decision['symbol'], f"做空止盈价格错误")
# 检查盈亏比
if risk > 0 and reward > 0:
rr_ratio = reward / risk
min_rr_ratio = 2.0 # 最小盈亏比 1:2
if rr_ratio < min_rr_ratio:
logger.warning(f"⚠️ 决策被拒绝: 盈亏比不足 (1:{rr_ratio:.2f} < 1:{min_rr_ratio:.0f})")
logger.warning(f" entry={entry_price}, stop_loss={stop_loss}, take_profit={take_profit}")
logger.warning(f" 风险={risk:.0f}, 盈利={reward:.0f}, 盈亏比=1:{rr_ratio:.2f}")
return self._get_hold_decision(
decision['symbol'],
f"盈亏比不足 (1:{rr_ratio:.2f} < 1:{min_rr_ratio:.0f})"
)
except (ValueError, TypeError, ZeroDivisionError) as e:
logger.warning(f"盈亏比检查失败: {e}")
# 价格距离检查:相同方向相同标的的挂单,价格距离 < 2% 时不加仓/开仓
action = decision.get('action', '')
new_entry_price = decision.get('entry_price')

View File

@ -83,14 +83,15 @@ class BinanceService:
# 1h: 300根 = 12.5天(中线分析)
# 4h: 200根 = 33.3天(趋势分析)
limits = {
'1m': 200,
'5m': 200,
'15m': 200,
'1h': 300,
'4h': 200
'30m': 200,
'1h': 300
}
data = {}
for interval in ['5m', '15m', '1h', '4h']:
for interval in ['1m', '5m', '15m', '30m', '1h']:
df = self.get_klines(symbol, interval, limit=limits.get(interval, 100))
if not df.empty:
df = self.calculate_indicators(df, interval) # 传递 interval 参数
@ -139,23 +140,30 @@ class BinanceService:
return df
# 根据周期调整 MA 参数
# 短周期(5m, 15m使用较短的 MA长周期使用较长的 MA
# 短周期(1m, 5m, 15m使用较短的 MA长周期使用较长的 MA
ma_config = {
'1m': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
'5m': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
'15m': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
'30m': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
'1h': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
'4h': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
}
config = ma_config.get(interval, ma_config['1h'])
# 移动平均线(统一命名,便于 LLM 分析
df['ma5'] = self._calculate_ma(df['close'], config['ma_short'])
df['ma10'] = self._calculate_ma(df['close'], config['ma_mid'])
df['ma20'] = self._calculate_ma(df['close'], config['ma_long'])
df['ma50'] = self._calculate_ma(df['close'], config['ma_extra'])
# EMA日内交易使用 EMA 反应更快
df['ema5'] = self._calculate_ema(df['close'], config['ma_short'])
df['ema10'] = self._calculate_ema(df['close'], config['ma_mid'])
df['ema20'] = self._calculate_ema(df['close'], config['ma_long'])
df['ema50'] = self._calculate_ema(df['close'], config['ma_extra'])
# EMA
# 兼容:保留 ma5/ma10/ma20/ma50 作为 EMA 的别名
df['ma5'] = df['ema5']
df['ma10'] = df['ema10']
df['ma20'] = df['ema20']
df['ma50'] = df['ema50']
# MACD EMA保持不变
df['ema12'] = self._calculate_ema(df['close'], 12)
df['ema26'] = self._calculate_ema(df['close'], 26)

View File

@ -14,10 +14,11 @@ class BitgetService:
# K线周期映射 - 注意 Bitget 使用大写 H
INTERVALS = {
'1m': '1m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1H', # Bitget 大写
'4h': '4H' # Bitget 大写
}
# Bitget API 基础 URL
@ -103,22 +104,24 @@ class BitgetService:
category: 产品类型默认 USDT-FUTURES
Returns:
包含 5m, 15m, 1h, 4h 数据的字典
包含 1m, 5m, 15m, 30m, 1h 数据的字典
"""
# 不同周期使用不同的数据量,平衡分析深度和性能
# 1m: 200根 = 3.3小时(超短线精确入场)
# 5m: 200根 = 16.7小时(日内分析)
# 15m: 200根 = 2.1天(短线分析)
# 1h: 300根 = 12.5天(中线分析
# 4h: 200根 = 33.3天(趋势分析
# 30m: 200根 = 4.2天(日内趋势
# 1h: 300根 = 12.5天(日内主趋势
limits = {
'1m': 200,
'5m': 200,
'15m': 200,
'1h': 300,
'4h': 200
'30m': 200,
'1h': 300
}
data = {}
for interval in ['5m', '15m', '1h', '4h']:
for interval in ['1m', '5m', '15m', '30m', '1h']:
df = self.get_klines(symbol, interval, limit=limits.get(interval, 100),
category=category)
if not df.empty:
@ -175,21 +178,28 @@ class BitgetService:
# 根据周期调整 MA 参数
ma_config = {
'1m': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
'5m': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
'15m': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
'30m': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
'1h': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
'4h': {'ma_short': 5, 'ma_mid': 10, 'ma_long': 20, 'ma_extra': 50},
}
config = ma_config.get(interval, ma_config['1h'])
# 移动平均线
df['ma5'] = self._calculate_ma(df['close'], config['ma_short'])
df['ma10'] = self._calculate_ma(df['close'], config['ma_mid'])
df['ma20'] = self._calculate_ma(df['close'], config['ma_long'])
df['ma50'] = self._calculate_ma(df['close'], config['ma_extra'])
# EMA日内交易使用 EMA 反应更快)
df['ema5'] = self._calculate_ema(df['close'], config['ma_short'])
df['ema10'] = self._calculate_ema(df['close'], config['ma_mid'])
df['ema20'] = self._calculate_ema(df['close'], config['ma_long'])
df['ema50'] = self._calculate_ema(df['close'], config['ma_extra'])
# EMA
# 兼容:保留 ma5/ma10/ma20/ma50 作为 EMA 的别名
df['ma5'] = df['ema5']
df['ma10'] = df['ema10']
df['ma20'] = df['ema20']
df['ma50'] = df['ema50']
# MACD EMA保持不变
df['ema12'] = self._calculate_ema(df['close'], 12)
df['ema26'] = self._calculate_ema(df['close'], 26)