This commit is contained in:
薇薇安 2026-01-17 21:16:37 +08:00
parent 236918329e
commit 098132bda6
3 changed files with 348 additions and 4 deletions

View File

@ -261,6 +261,137 @@
line-height: 1.6;
}
/* 推荐分类和风险标签 */
.recommendation-tags {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 10px;
}
.category-tag,
.risk-tag {
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
}
.category-tag {
background-color: #2196F3;
color: white;
}
.category-tag.category-逆势反弹高风险 {
background-color: #f44336;
}
.category-tag.category-震荡区间操作 {
background-color: #FF9800;
}
.risk-tag {
background-color: #9E9E9E;
color: white;
}
.risk-tag.risk-高 {
background-color: #f44336;
}
.risk-tag.risk-中高 {
background-color: #FF5722;
}
.risk-tag.risk-中等 {
background-color: #FF9800;
}
.risk-tag.risk-低中 {
background-color: #4CAF50;
}
/* 用户指南 */
.user-guide {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px;
border-radius: 8px;
margin: 10px 0;
}
.user-guide strong {
display: block;
margin-bottom: 10px;
font-size: 16px;
}
.user-guide-content {
margin: 0;
white-space: pre-wrap;
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.8;
background: rgba(255, 255, 255, 0.1);
padding: 12px;
border-radius: 4px;
overflow-x: auto;
}
/* 交易教程 */
.trading-tutorial {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 12px 15px;
border-radius: 4px;
margin: 10px 0;
}
.trading-tutorial strong {
display: block;
margin-bottom: 8px;
color: #856404;
}
.trading-tutorial p {
margin: 0;
color: #856404;
line-height: 1.6;
}
/* 预期持仓时间和退出提醒 */
.expected-hold-time,
.max-hold-warning {
background-color: #e3f2fd;
border-left: 4px solid #2196F3;
padding: 10px 15px;
border-radius: 4px;
margin: 10px 0;
}
.expected-hold-time strong,
.max-hold-warning strong {
display: inline-block;
margin-right: 8px;
color: #1976d2;
}
.expected-hold-time span,
.max-hold-warning span {
color: #1976d2;
line-height: 1.6;
}
.max-hold-warning {
background-color: #fff3e0;
border-left-color: #ff9800;
}
.max-hold-warning strong,
.max-hold-warning span {
color: #e65100;
}
.suggested-params {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));

View File

@ -317,6 +317,22 @@ function Recommendations() {
</div>
<div className="card-content">
{/* 推荐分类和风险等级标签 */}
{(rec.recommendation_category || rec.risk_level) && (
<div className="recommendation-tags">
{rec.recommendation_category && (
<span className={`category-tag category-${rec.recommendation_category?.replace(/[\(\)]/g, '').replace(/\s+/g, '-').toLowerCase() || 'default'}`}>
{rec.recommendation_category}
</span>
)}
{rec.risk_level && (
<span className={`risk-tag risk-${rec.risk_level?.replace(/\s+/g, '-').toLowerCase() || 'medium'}`}>
风险: {rec.risk_level}
</span>
)}
</div>
)}
<div className="price-info">
<div className="price-item">
<label>当前价格:</label>
@ -337,8 +353,24 @@ function Recommendations() {
)}
</div>
{/* 用户指南(人话版计划) */}
{rec.user_guide && (
<div className="user-guide">
<strong>📋 操作计划:</strong>
<pre className="user-guide-content">{rec.user_guide}</pre>
</div>
)}
{/* 交易教程(如果存在) */}
{rec.trading_tutorial && (
<div className="trading-tutorial">
<strong>💡 交易提示:</strong>
<p>{rec.trading_tutorial}</p>
</div>
)}
<div className="recommendation-reason">
<strong>推荐原因:</strong>
<strong>技术分析原因:</strong>
<p>{rec.recommendation_reason || '-'}</p>
</div>
@ -404,6 +436,22 @@ function Recommendations() {
</button>
</div>
{/* 预期持仓时间(如果存在) */}
{rec.expected_hold_time && (
<div className="expected-hold-time">
<strong> 预期持仓时间:</strong>
<span>{rec.expected_hold_time}</span>
</div>
)}
{/* 最大持仓天数提醒 */}
{rec.max_hold_days && (
<div className="max-hold-warning">
<strong> 退出提醒:</strong>
<span>若持仓超过{rec.max_hold_days}天仍未触及第一目标建议平仓离场重新评估</span>
</div>
)}
{showDetails[rec.id || rec.symbol] && (
<div className="details-panel">
<div className="details-section">

View File

@ -455,6 +455,20 @@ class TradeRecommender:
expected_pnl_1 = (estimated_win_rate / 100) * take_profit_1_usdt - ((100 - estimated_win_rate) / 100) * stop_loss_usdt
expected_pnl_2 = (estimated_win_rate / 100) * take_profit_2_usdt - ((100 - estimated_win_rate) / 100) * stop_loss_usdt
# 分类推荐类型和风险等级
recommendation_category, risk_level, simple_reason, risk_warning, trading_tutorial = self._classify_recommendation(
market_regime, trend_4h, direction, trade_signal, signal_strength
)
# 计算预期持仓时间(基于策略周期和市场状态)
expected_hold_time = self._estimate_hold_time(market_regime, trend_4h, direction, signal_strength)
# 生成用户指南(人话版计划)
user_guide = self._generate_user_guide(
symbol, direction, suggested_limit_price, stop_loss_price, take_profit_1, take_profit_2,
simple_reason, expected_hold_time, risk_warning, recommendation_category
)
# 基础推荐数据
base_data = {
'symbol': symbol,
@ -488,7 +502,14 @@ class TradeRecommender:
'take_profit_1_usdt': round(take_profit_1_usdt, 2),
'take_profit_2_usdt': round(take_profit_2_usdt, 2),
'recommendation_time': recommendation_time,
'timestamp': timestamp
'timestamp': timestamp,
# 新增字段:用户指南和分类
'user_guide': user_guide,
'recommendation_category': recommendation_category,
'risk_level': risk_level,
'expected_hold_time': expected_hold_time,
'trading_tutorial': trading_tutorial,
'max_hold_days': 3 # 最大持仓天数(超过此天数建议平仓)
}
# 限价单推荐
@ -507,15 +528,159 @@ class TradeRecommender:
logger.debug(
f"✓ 生成推荐: {symbol} {direction} "
f"(信号强度: {signal_strength}/10, 胜率预估: {estimated_win_rate:.1f}%)"
f"(信号强度: {signal_strength}/10, 胜率预估: {estimated_win_rate:.1f}%, "
f"分类: {recommendation_category}, 风险: {risk_level})"
)
return [limit_recommendation, market_recommendation]
except Exception as e:
logger.error(f"创建推荐失败 {symbol_info.get('symbol', 'unknown')}: {e}")
logger.error(f"创建推荐失败 {symbol_info.get('symbol', 'unknown')}: {e}", exc_info=True)
return None
def _classify_recommendation(
self,
market_regime: str,
trend_4h: Optional[str],
direction: str,
trade_signal: Dict,
signal_strength: int
) -> tuple:
"""
分类推荐类型和风险等级
Returns:
(recommendation_category, risk_level, simple_reason, risk_warning, trading_tutorial)
"""
# 判断是否为逆趋势交易
is_counter_trend = False
if trend_4h:
if (direction == 'BUY' and trend_4h == 'down') or (direction == 'SELL' and trend_4h == 'up'):
is_counter_trend = True
# 分类推荐类型
if market_regime == 'trending':
if not is_counter_trend:
# 顺趋势突破
category = "顺趋势突破"
risk_level = "中等"
simple_reason = "趋势明确,顺势而为,胜率较高"
risk_warning = "趋势可能反转,注意止损保护"
tutorial = "此类交易适合跟随趋势,持仓时间可稍长,但需关注趋势是否持续"
else:
# 逆趋势反弹(高风险)
category = "逆势反弹(高风险)"
risk_level = ""
simple_reason = "逆趋势操作,风险较高,适合快进快出"
risk_warning = "⚠️ 高风险:逆趋势交易胜率较低,务必严格止损,快进快出"
tutorial = "此类交易胜率较低,仅适合短线快进快出,务必严格止损。建议持仓不超过数小时,一旦触及止损立即离场"
elif market_regime == 'ranging':
# 震荡区间操作
category = "震荡区间操作"
risk_level = "中等"
simple_reason = "市场震荡,在区间边界操作,适合高抛低吸"
risk_warning = "震荡可能转为趋势,注意突破风险"
tutorial = "此类交易适合在震荡区间边界操作预期持仓时间较短数小时至1天到达目标及时止盈"
else:
# 未知市场状态
category = "市场状态不明"
risk_level = "中高"
simple_reason = "市场状态不明确,谨慎操作"
risk_warning = "市场状态不明,建议降低仓位或等待更明确信号"
tutorial = "市场状态不明确,建议谨慎操作,降低仓位,严格止损"
# 根据信号强度调整风险等级
if signal_strength >= 7:
if risk_level == "":
risk_level = "中高"
elif risk_level == "中等":
risk_level = "低中"
elif signal_strength < 5:
if risk_level == "中等":
risk_level = "中高"
elif risk_level == "低中":
risk_level = "中等"
return category, risk_level, simple_reason, risk_warning, tutorial
def _estimate_hold_time(
self,
market_regime: str,
trend_4h: Optional[str],
direction: str,
signal_strength: int
) -> str:
"""
估算预期持仓时间
Returns:
预期持仓时间的文字描述
"""
# 基于策略周期1H/15min和市场状态估算
if market_regime == 'trending':
# 趋势市场,持仓时间可能较长
if signal_strength >= 7:
return "预期持仓数小时至2天趋势明确可适当延长持仓"
else:
return "预期持仓数小时至1天趋势较弱及时止盈"
elif market_regime == 'ranging':
# 震荡市场,持仓时间较短
return "预期持仓:数小时至半天(震荡市场,快进快出)"
else:
# 未知状态,保守估计
return "预期持仓:数小时(市场状态不明,谨慎持仓)"
def _generate_user_guide(
self,
symbol: str,
direction: str,
limit_price: float,
stop_loss: float,
tp1: float,
tp2: float,
simple_reason: str,
expected_hold_time: str,
risk_warning: str,
category: str
) -> str:
"""
生成用户指南人话版计划
Args:
symbol: 交易对
direction: 方向
limit_price: 建议入场价限价单
stop_loss: 止损价
tp1: 第一目标止盈价
tp2: 第二目标止盈价
simple_reason: 简单原因
expected_hold_time: 预期持仓时间
risk_warning: 风险警告
category: 推荐分类
Returns:
用户指南文本
"""
direction_cn = "买入" if direction == 'BUY' else "卖出"
user_guide = f"""【操作计划】{direction_cn} {symbol}
推荐类型{category}
核心理由{simple_reason}
具体点位
入场价限价单: {limit_price:.4f} USDT
止损价: {stop_loss:.4f} USDT
第一目标: {tp1:.4f} USDT盈亏比1:1
第二目标: {tp2:.4f} USDT盈亏比2.5:1
持仓周期{expected_hold_time}
退出条件
触及止损立即平仓
触及第一目标可部分止盈或全部止盈
触及第二目标建议全部止盈
持仓超过3天未触及第一目标建议平仓离场重新评估
关键提醒{risk_warning}"""
return user_guide
async def get_active_recommendations(self) -> List[Dict]:
"""获取当前有效的推荐"""
if DB_AVAILABLE and TradeRecommendation: