1
This commit is contained in:
parent
cf2dcfe2c7
commit
efc0abf5cb
@ -675,7 +675,10 @@ class CryptoAgent:
|
||||
decision_type = paper_decision.get('decision', 'HOLD')
|
||||
|
||||
if decision_type == 'HOLD':
|
||||
logger.info(f"\n📊 模拟交易: {paper_decision.get('reasoning', '观望')}")
|
||||
reasoning = paper_decision.get('reasoning', '观望')
|
||||
logger.info(f"\n📊 模拟交易: {reasoning}")
|
||||
# 有信号但决策为 HOLD,发送未执行通知
|
||||
await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True)
|
||||
else:
|
||||
logger.info(f"\n📊 【执行模拟交易】")
|
||||
|
||||
@ -689,7 +692,10 @@ class CryptoAgent:
|
||||
await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True)
|
||||
paper_executed = True
|
||||
else:
|
||||
logger.warning(f" ⚠️ 模拟交易未执行,跳过通知")
|
||||
# 有信号但订单创建失败,发送未执行通知
|
||||
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
|
||||
await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True, reason=reason)
|
||||
logger.warning(f" ⚠️ 模拟交易未执行,已发送通知")
|
||||
elif decision_type == 'CLOSE':
|
||||
await self._execute_close(paper_decision, paper_trading=True)
|
||||
paper_executed = True
|
||||
@ -703,7 +709,10 @@ class CryptoAgent:
|
||||
decision_type = real_decision.get('decision', 'HOLD')
|
||||
|
||||
if decision_type == 'HOLD':
|
||||
logger.info(f"\n💰 实盘交易: {real_decision.get('reasoning', '观望')}")
|
||||
reasoning = real_decision.get('reasoning', '观望')
|
||||
logger.info(f"\n💰 实盘交易: {reasoning}")
|
||||
# 有信号但决策为 HOLD,发送未执行通知
|
||||
await self._notify_signal_not_executed(market_signal, real_decision, current_price, is_paper=False)
|
||||
else:
|
||||
logger.info(f"\n💰 【执行实盘交易】")
|
||||
|
||||
@ -716,7 +725,10 @@ class CryptoAgent:
|
||||
await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False)
|
||||
real_executed = True
|
||||
else:
|
||||
logger.warning(f" ⚠️ 实盘交易未执行,跳过通知")
|
||||
# 有信号但订单创建失败,发送未执行通知
|
||||
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
|
||||
await self._notify_signal_not_executed(market_signal, real_decision, current_price, is_paper=False, reason=reason)
|
||||
logger.warning(f" ⚠️ 实盘交易未执行,已发送通知")
|
||||
elif decision_type == 'CLOSE':
|
||||
await self._execute_close(real_decision, paper_trading=False)
|
||||
real_executed = True
|
||||
@ -1402,6 +1414,73 @@ class CryptoAgent:
|
||||
if self.settings.telegram_enabled:
|
||||
await self.telegram.send_message(message)
|
||||
|
||||
async def _notify_signal_not_executed(
|
||||
self,
|
||||
market_signal: Dict[str, Any],
|
||||
decision: Dict[str, Any],
|
||||
current_price: float,
|
||||
is_paper: bool = True,
|
||||
reason: str = ""
|
||||
):
|
||||
"""发送有信号但未执行交易的通知"""
|
||||
try:
|
||||
symbol = market_signal.get('symbol')
|
||||
account_type = "📊 模拟" if is_paper else "💰 实盘"
|
||||
|
||||
# 获取最佳信号
|
||||
best_signal = self._get_best_signal_from_market(market_signal)
|
||||
if not best_signal:
|
||||
return
|
||||
|
||||
confidence = best_signal.get('confidence', 0)
|
||||
entry_type = best_signal.get('entry_type', 'market')
|
||||
entry_zone = best_signal.get('entry_zone', current_price)
|
||||
|
||||
# 决策信息
|
||||
decision_type = decision.get('decision', 'HOLD')
|
||||
decision_reasoning = decision.get('reasoning', reason)
|
||||
|
||||
# 方向图标
|
||||
action = best_signal.get('action', 'wait')
|
||||
if action == 'buy':
|
||||
action_icon = '🟢'
|
||||
action_text = '做多'
|
||||
elif action == 'sell':
|
||||
action_icon = '🔴'
|
||||
action_text = '做空'
|
||||
else:
|
||||
action_icon = '➖'
|
||||
action_text = '观望'
|
||||
|
||||
# 构建标题
|
||||
title = f"{account_type} {symbol} 信号未执行"
|
||||
|
||||
# 构建内容
|
||||
content_parts = [
|
||||
f"{action_icon} **信号**: {action_text} | 📈 信心度: **{confidence}%**",
|
||||
f"",
|
||||
f"**入场方式**: {entry_type}",
|
||||
f"**建议入场价**: ${entry_zone:,.2f}" if isinstance(entry_zone, (int, float)) else f"**建议入场价**: {entry_zone}",
|
||||
f"**当前价格**: ${current_price:,.2f}",
|
||||
f"",
|
||||
f"⚠️ **未执行原因**:",
|
||||
f"{decision_reasoning}",
|
||||
]
|
||||
|
||||
content = "\n".join(content_parts)
|
||||
|
||||
# 发送通知
|
||||
if self.settings.feishu_enabled:
|
||||
await self.feishu.send_card(title, content, "orange")
|
||||
if self.settings.telegram_enabled:
|
||||
message = f"{title}\n\n{content}"
|
||||
await self.telegram.send_message(message)
|
||||
|
||||
logger.info(f" 📤 已发送信号未执行通知: {decision_type} - {decision_reasoning[:50]}")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"发送信号未执行通知失败: {e}")
|
||||
|
||||
async def analyze_once(self, symbol: str) -> Dict[str, Any]:
|
||||
"""单次分析(用于测试或手动触发)"""
|
||||
data = self.binance.get_multi_timeframe_data(symbol)
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
import pandas as pd
|
||||
from typing import Dict, Any, Optional, List
|
||||
from datetime import datetime
|
||||
from app.utils.logger import logger
|
||||
@ -329,6 +330,12 @@ class MarketSignalAnalyzer:
|
||||
else:
|
||||
context_parts.append("量价状态: 平量 ➖")
|
||||
|
||||
# 波动率分析
|
||||
volatility_analysis = self._analyze_volatility(data)
|
||||
if volatility_analysis:
|
||||
context_parts.append(f"\n## 波动率分析")
|
||||
context_parts.append(volatility_analysis)
|
||||
|
||||
return "\n".join(context_parts)
|
||||
|
||||
async def _get_news_context(self, symbol: str) -> str:
|
||||
@ -544,3 +551,45 @@ class MarketSignalAnalyzer:
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'error': '信号分析失败'
|
||||
}
|
||||
|
||||
def _analyze_volatility(self, data: Dict[str, pd.DataFrame]) -> str:
|
||||
"""分析波动率变化"""
|
||||
df = data.get('1h')
|
||||
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 根
|
||||
older_atr = df['atr'].iloc[-12:-6].mean() # 之前 6 根
|
||||
|
||||
if pd.isna(recent_atr) or pd.isna(older_atr) or older_atr == 0:
|
||||
return ""
|
||||
|
||||
atr_change = (recent_atr - older_atr) / older_atr * 100
|
||||
|
||||
current_atr = float(df['atr'].iloc[-1])
|
||||
current_price = float(df['close'].iloc[-1])
|
||||
atr_percent = current_atr / current_price * 100
|
||||
|
||||
lines.append(f"当前 ATR: ${current_atr:.2f} ({atr_percent:.2f}%)")
|
||||
|
||||
if atr_change > 20:
|
||||
lines.append(f"**波动率扩张**: ATR 上升 {atr_change:.0f}%,趋势可能启动")
|
||||
elif atr_change < -20:
|
||||
lines.append(f"**波动率收缩**: ATR 下降 {abs(atr_change):.0f}%,可能即将突破")
|
||||
else:
|
||||
lines.append(f"波动率稳定: ATR 变化 {atr_change:+.0f}%")
|
||||
|
||||
# 布林带宽度
|
||||
if 'bb_upper' in df.columns and 'bb_lower' in df.columns:
|
||||
bb_width = (float(df['bb_upper'].iloc[-1]) - float(df['bb_lower'].iloc[-1])) / current_price * 100
|
||||
bb_width_prev = (float(df['bb_upper'].iloc[-6]) - float(df['bb_lower'].iloc[-6])) / float(df['close'].iloc[-6]) * 100
|
||||
|
||||
if bb_width < bb_width_prev * 0.8:
|
||||
lines.append(f"**布林带收口**: 宽度 {bb_width:.1f}%,变盘信号")
|
||||
elif bb_width > bb_width_prev * 1.2:
|
||||
lines.append(f"**布林带开口**: 宽度 {bb_width:.1f}%,趋势延续")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user