a
This commit is contained in:
parent
236918329e
commit
098132bda6
|
|
@ -261,6 +261,137 @@
|
||||||
line-height: 1.6;
|
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 {
|
.suggested-params {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
|
|
||||||
|
|
@ -317,6 +317,22 @@ function Recommendations() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="card-content">
|
<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-info">
|
||||||
<div className="price-item">
|
<div className="price-item">
|
||||||
<label>当前价格:</label>
|
<label>当前价格:</label>
|
||||||
|
|
@ -337,8 +353,24 @@ function Recommendations() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</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">
|
<div className="recommendation-reason">
|
||||||
<strong>推荐原因:</strong>
|
<strong>技术分析原因:</strong>
|
||||||
<p>{rec.recommendation_reason || '-'}</p>
|
<p>{rec.recommendation_reason || '-'}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -404,6 +436,22 @@ function Recommendations() {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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] && (
|
{showDetails[rec.id || rec.symbol] && (
|
||||||
<div className="details-panel">
|
<div className="details-panel">
|
||||||
<div className="details-section">
|
<div className="details-section">
|
||||||
|
|
|
||||||
|
|
@ -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_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
|
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 = {
|
base_data = {
|
||||||
'symbol': symbol,
|
'symbol': symbol,
|
||||||
|
|
@ -488,7 +502,14 @@ class TradeRecommender:
|
||||||
'take_profit_1_usdt': round(take_profit_1_usdt, 2),
|
'take_profit_1_usdt': round(take_profit_1_usdt, 2),
|
||||||
'take_profit_2_usdt': round(take_profit_2_usdt, 2),
|
'take_profit_2_usdt': round(take_profit_2_usdt, 2),
|
||||||
'recommendation_time': recommendation_time,
|
'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(
|
logger.debug(
|
||||||
f"✓ 生成推荐: {symbol} {direction} "
|
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]
|
return [limit_recommendation, market_recommendation]
|
||||||
|
|
||||||
except Exception as e:
|
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
|
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]:
|
async def get_active_recommendations(self) -> List[Dict]:
|
||||||
"""获取当前有效的推荐"""
|
"""获取当前有效的推荐"""
|
||||||
if DB_AVAILABLE and TradeRecommendation:
|
if DB_AVAILABLE and TradeRecommendation:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user