feat: 解析ai分析到各个卡片中

master^2
Lxy 1 week ago
parent 70865d0958
commit fb8c4f6587

@ -53,6 +53,12 @@ class AIAnalysisPrompt:
"volume": {"status": "放量/缩量/正常", "ratio": 2.0}, "volume": {"status": "放量/缩量/正常", "ratio": 2.0},
"kdj": {"k": 30, "d": 25, "j": 40, "status": "超卖", "signal": "金叉/死叉"}, "kdj": {"k": 30, "d": 25, "j": 40, "status": "超卖", "signal": "金叉/死叉"},
"conclusion": "择入场结论" "conclusion": "择入场结论"
},
"5min": {
"macd": {"trend": "up/down/neutral", "histogram": "放大/缩小/背离", "position": "零轴上/下"},
"volume": {"status": "放量/缩量/正常", "ratio": 1.8},
"kdj": {"k": 50, "d": 45, "j": 60, "status": "超买/超卖/中性", "signal": "金叉/死叉"},
"conclusion": "精确定位入场点"
} }
}, },
"kdj_diagnosis": { "kdj_diagnosis": {

@ -1967,6 +1967,99 @@ body.theme-minimal .sort-select select:hover {
min-height: 120px; min-height: 120px;
} }
/* 情景预案卡片样式 */
.scenario-container {
display: flex;
flex-direction: column;
gap: 10px;
}
.scenario-item {
display: grid;
grid-template-columns: 80px 60px 1fr;
align-items: center;
gap: 12px;
padding: 10px 12px;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
transition: all 0.2s ease;
}
.scenario-item:hover {
border-color: var(--border-glow);
background: var(--bg-card-hover);
}
.scenario-name {
font-size: 13px;
font-weight: 600;
color: var(--cyan);
}
.scenario-probability {
font-size: 13px;
font-weight: 600;
color: var(--amber);
text-align: center;
}
.scenario-action {
font-size: 12px;
color: var(--text-secondary);
line-height: 1.4;
}
/* 多周期趋势样式优化 */
.trends-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.trend-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 10px;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
}
.trend-period {
font-size: 12px;
color: var(--text-muted);
font-weight: 500;
}
.trend-badge {
font-size: 12px;
font-weight: 600;
padding: 4px 10px;
border-radius: 4px;
text-align: center;
}
.trend-badge.up {
color: var(--green);
background: rgba(16, 185, 129, 0.1);
border: 1px solid rgba(16, 185, 129, 0.3);
}
.trend-badge.down {
color: var(--red);
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.3);
}
.trend-badge.neutral {
color: var(--amber);
background: rgba(245, 158, 11, 0.1);
border: 1px solid rgba(245, 158, 11, 0.3);
}
.ai-analysis-placeholder { .ai-analysis-placeholder {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

@ -382,6 +382,17 @@
</div> </div>
</div> </div>
<!-- 情景预案 -->
<div class="panel-card scenario-card" id="scenario-panel" style="display:none;">
<div class="panel-header">
<i class="fas fa-chess"></i>
<span>情景预案</span>
</div>
<div class="scenario-container" id="scenario-plans">
<!-- 动态生成 -->
</div>
</div>
<!-- 趋势评分 --> <!-- 趋势评分 -->
<div class="panel-card score-card"> <div class="panel-card score-card">
<div class="panel-header"> <div class="panel-header">

@ -795,15 +795,22 @@ async function showAIHistoryDetail(recordId) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
${Object.entries(result.four_dimensional).map(([period, d]) => ` ${(() => {
const periodNames = { '60min': '60分钟', '30min': '30分钟', '15min': '15分钟', '5min': '5分钟' };
const periodOrder = ['60min', '30min', '15min', '5min'];
const sortedEntries = Object.entries(result.four_dimensional).sort((a, b) => {
return periodOrder.indexOf(a[0]) - periodOrder.indexOf(b[0]);
});
return sortedEntries.map(([period, d]) => `
<tr> <tr>
<td><strong>${period}</strong></td> <td><strong>${periodNames[period] || period}</strong></td>
<td>${d.macd?.trend || '--'}</td> <td>${d.macd?.trend || '--'}</td>
<td>${d.volume?.status || '--'}</td> <td>${d.volume?.status || '--'}</td>
<td>${d.kdj?.status || '--'}</td> <td>${d.kdj?.status || '--'}</td>
<td>${d.conclusion || '--'}</td> <td>${d.conclusion || '--'}</td>
</tr> </tr>
`).join('')} `).join('');
})()}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -1421,6 +1428,119 @@ function displayAIAnalysisResult(data) {
</div> </div>
</div> </div>
`; `;
// 同步AI分析数据到主面板各个卡片
syncAIToPanels(result);
}
function syncAIToPanels(result) {
const suggestion = result.trading_suggestion || {};
const fourDim = result.four_dimensional || {};
const pivotPoints = result.pivot_points || {};
const kdjDiag = result.kdj_diagnosis || {};
const scenarios = result.scenario_plans || {};
// 1. 同步到AI交易建议卡片
const suggestionBadge = document.getElementById('suggestion-badge');
if (suggestionBadge) {
suggestionBadge.textContent = suggestion.direction || '--';
suggestionBadge.className = `suggestion-badge ${suggestion.direction === '做多' ? 'up' : suggestion.direction === '做空' ? 'down' : 'neutral'}`;
}
const entryPriceEl = document.getElementById('entry-price');
if (entryPriceEl) entryPriceEl.textContent = suggestion.entry_range ? `${suggestion.entry_range.min}-${suggestion.entry_range.max}` : '--';
const targetPriceEl = document.getElementById('target-price');
if (targetPriceEl) {
const takeProfit = suggestion.take_profit?.[0];
targetPriceEl.textContent = takeProfit?.price || '--';
}
const stopLossEl = document.getElementById('stop-loss');
if (stopLossEl) stopLossEl.textContent = suggestion.stop_loss || '--';
const riskLevelEl = document.getElementById('risk-level');
if (riskLevelEl) riskLevelEl.textContent = suggestion.position_size || '--';
// 2. 同步到技术指标卡片
// 从60min周期提取MACD和KDJ信息
const macd60 = fourDim['60min']?.macd || {};
const kdj60 = fourDim['60min']?.kdj || {};
const macdSignalEl = document.getElementById('macd-signal');
if (macdSignalEl) macdSignalEl.textContent = macd60.trend || '--';
const macdDetailEl = document.getElementById('macd-detail');
if (macdDetailEl) macdDetailEl.textContent = macd60.position ? `${macd60.position} | ${macd60.histogram || ''}` : '--';
const kdjSignalEl = document.getElementById('kdj-signal');
if (kdjSignalEl) kdjSignalEl.textContent = kdj60.status || '--';
const kdjDetailEl = document.getElementById('kdj-detail');
if (kdjDetailEl) kdjDetailEl.textContent = kdj60.signal || '--';
// 3. 同步到关键点位卡片
if (pivotPoints.r1) {
const r1El = document.getElementById('resistance-1');
if (r1El) r1El.querySelector('span:last-child').textContent = pivotPoints.r1;
}
if (pivotPoints.r2) {
const r2El = document.getElementById('resistance-2');
if (r2El) r2El.querySelector('span:last-child').textContent = pivotPoints.r2;
}
if (pivotPoints.pp) {
const ppEl = document.getElementById('pivot-point');
if (ppEl) ppEl.querySelector('span:last-child').textContent = pivotPoints.pp;
}
if (pivotPoints.s1) {
const s1El = document.getElementById('support-1');
if (s1El) s1El.querySelector('span:last-child').textContent = pivotPoints.s1;
}
if (pivotPoints.s2) {
const s2El = document.getElementById('support-2');
if (s2El) s2El.querySelector('span:last-child').textContent = pivotPoints.s2;
}
// 4. 同步到多周期趋势卡片
const periodTrendsEl = document.getElementById('period-trends');
if (periodTrendsEl && Object.keys(fourDim).length > 0) {
const periodNames = { '60min': '60分钟', '30min': '30分钟', '15min': '15分钟', '5min': '5分钟' };
const periodOrder = ['60min', '30min', '15min', '5min'];
// 按固定顺序排列周期
const sortedEntries = Object.entries(fourDim).sort((a, b) => {
return periodOrder.indexOf(a[0]) - periodOrder.indexOf(b[0]);
});
periodTrendsEl.innerHTML = sortedEntries.map(([period, data]) => {
const trend = data.conclusion || data.macd?.trend || 'neutral';
const trendClass = trend.includes('多') || trend === 'up' ? 'up' : trend.includes('空') || trend === 'down' ? 'down' : 'neutral';
const trendText = trend.includes('多') ? '偏多' : trend.includes('空') ? '偏空' : '震荡';
return `<div class="trend-item"><span class="trend-period">${periodNames[period] || period}</span><span class="trend-badge ${trendClass}">${trendText}</span></div>`;
}).join('');
}
// 5. 同步到情景预案卡片
const scenarioPanel = document.getElementById('scenario-panel');
const scenarioPlansEl = document.getElementById('scenario-plans');
if (scenarioPanel && scenarioPlansEl && Object.keys(scenarios).length > 0) {
scenarioPanel.style.display = 'block';
const scenarioNames = {
'breakthrough': '突破',
'consolidation': '震荡',
'reversal': '反转',
'news_impact': '消息影响'
};
scenarioPlansEl.innerHTML = Object.entries(scenarios).map(([key, data]) => `
<div class="scenario-item">
<span class="scenario-name">${scenarioNames[key] || key}</span>
<span class="scenario-probability">${data.probability || 0}%</span>
<span class="scenario-action">${data.action || '--'}</span>
</div>
`).join('');
} else if (scenarioPanel) {
scenarioPanel.style.display = 'none';
}
} }
function showAIDetailModal() { function showAIDetailModal() {
@ -1433,13 +1553,14 @@ function showAIDetailModal() {
const modalBody = document.getElementById('ai-analysis-modal-body'); const modalBody = document.getElementById('ai-analysis-modal-body');
let fourDimensionalHTML = ''; let fourDimensionalHTML = '';
const periods = ['60min', '30min', '15min']; const periods = ['60min', '30min', '15min', '5min'];
const periodNames = { '60min': '60分钟', '30min': '30分钟', '15min': '15分钟', '5min': '5分钟' };
periods.forEach(period => { periods.forEach(period => {
const data = result.four_dimensional?.[period]; const data = result.four_dimensional?.[period];
if (data) { if (data) {
fourDimensionalHTML += ` fourDimensionalHTML += `
<tr> <tr>
<td class="period-cell">${period}</td> <td class="period-cell">${periodNames[period] || period}</td>
<td> <td>
<div>趋势: ${data.macd?.trend || '--'}</div> <div>趋势: ${data.macd?.trend || '--'}</div>
<div>位置: ${data.macd?.position || '--'}</div> <div>位置: ${data.macd?.position || '--'}</div>

Loading…
Cancel
Save