|
|
|
@ -64,11 +64,15 @@ function initEventListeners() {
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('theme-toggle').addEventListener('click', toggleTheme);
|
|
|
|
document.getElementById('theme-toggle').addEventListener('click', toggleTheme);
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('suggestion-card').addEventListener('click', function() {
|
|
|
|
// AI交易建议卡片点击事件(已隐藏,暂时注释)
|
|
|
|
if (currentDetailData) {
|
|
|
|
// const suggestionCard = document.getElementById('suggestion-card');
|
|
|
|
showSuggestionModal(currentDetailData);
|
|
|
|
// if (suggestionCard) {
|
|
|
|
}
|
|
|
|
// suggestionCard.addEventListener('click', function() {
|
|
|
|
});
|
|
|
|
// if (currentDetailData) {
|
|
|
|
|
|
|
|
// showSuggestionModal(currentDetailData);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// 刷新全部按钮
|
|
|
|
// 刷新全部按钮
|
|
|
|
document.getElementById('refresh-all-btn').addEventListener('click', refreshAllSymbols);
|
|
|
|
document.getElementById('refresh-all-btn').addEventListener('click', refreshAllSymbols);
|
|
|
|
@ -224,15 +228,23 @@ function filterDataByCategory(data, category) {
|
|
|
|
|
|
|
|
|
|
|
|
async function loadFuturesList() {
|
|
|
|
async function loadFuturesList() {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
|
|
|
|
console.log('正在加载品种列表...');
|
|
|
|
const response = await fetch(`${API_BASE}/list`);
|
|
|
|
const response = await fetch(`${API_BASE}/list`);
|
|
|
|
const data = await response.json();
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
console.log('品种列表响应:', data);
|
|
|
|
if (data.success) {
|
|
|
|
if (data.success) {
|
|
|
|
allFuturesData = data.data.map(item => ({
|
|
|
|
allFuturesData = data.data.map(item => ({
|
|
|
|
...item,
|
|
|
|
...item,
|
|
|
|
hasAIAnalysis: false // 默认没有AI分析数据
|
|
|
|
hasAIAnalysis: false // 默认没有AI分析数据
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
|
|
|
|
console.log('加载的品种数据:', allFuturesData.length, '个');
|
|
|
|
renderFuturesGrid(allFuturesData);
|
|
|
|
renderFuturesGrid(allFuturesData);
|
|
|
|
updateStats(allFuturesData);
|
|
|
|
updateStats(allFuturesData);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 异步加载每个合约的最新AI分析结果
|
|
|
|
|
|
|
|
loadAllAIAnalysis();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
console.error('加载品种列表失败:', data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
console.error('加载品种列表失败:', error);
|
|
|
|
console.error('加载品种列表失败:', error);
|
|
|
|
@ -240,6 +252,64 @@ async function loadFuturesList() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function loadAllAIAnalysis() {
|
|
|
|
|
|
|
|
console.log('开始加载所有合约的AI分析结果...');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 分批加载,避免并发请求过多
|
|
|
|
|
|
|
|
const batchSize = 5;
|
|
|
|
|
|
|
|
for (let i = 0; i < allFuturesData.length; i += batchSize) {
|
|
|
|
|
|
|
|
const batch = allFuturesData.slice(i, i + batchSize);
|
|
|
|
|
|
|
|
const promises = batch.map(async (item) => {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const response = await fetch(`${API_BASE}/ai-analysis/${item.symbol}`);
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (data.success && data.data && data.data.result) {
|
|
|
|
|
|
|
|
const result = data.data.result;
|
|
|
|
|
|
|
|
const analysisItem = allFuturesData.find(d => d.symbol === item.symbol);
|
|
|
|
|
|
|
|
if (analysisItem) {
|
|
|
|
|
|
|
|
analysisItem.hasAIAnalysis = true;
|
|
|
|
|
|
|
|
analysisItem.aiResult = result;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 更新操作建议
|
|
|
|
|
|
|
|
if (result.trading_suggestion?.direction) {
|
|
|
|
|
|
|
|
analysisItem.suggestion = result.trading_suggestion.direction;
|
|
|
|
|
|
|
|
analysisItem.suggestionType = result.trading_suggestion.direction === '做多' ? 'up' : result.trading_suggestion.direction === '做空' ? 'down' : 'neutral';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 更新压力支撑位
|
|
|
|
|
|
|
|
if (result.pivot_points) {
|
|
|
|
|
|
|
|
if (result.pivot_points.r1) analysisItem.resistance = result.pivot_points.r1;
|
|
|
|
|
|
|
|
if (result.pivot_points.s1) analysisItem.support = result.pivot_points.s1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 更新多周期趋势
|
|
|
|
|
|
|
|
if (result.four_dimensional) {
|
|
|
|
|
|
|
|
const periodMap = { '60min': '60', '30min': '30', '15min': '15', '5min': '5' };
|
|
|
|
|
|
|
|
analysisItem.periods = {};
|
|
|
|
|
|
|
|
Object.entries(result.four_dimensional).forEach(([period, pdata]) => {
|
|
|
|
|
|
|
|
const periodNum = periodMap[period];
|
|
|
|
|
|
|
|
if (periodNum) {
|
|
|
|
|
|
|
|
const trend = pdata.conclusion || pdata.macd?.trend || 'neutral';
|
|
|
|
|
|
|
|
analysisItem.periods[periodNum] = trend.includes('多') || trend === 'up' ? 'up' : trend.includes('空') || trend === 'down' ? 'down' : 'neutral';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
console.error(`加载 ${item.symbol} AI分析失败:`, error);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 所有批次加载完成后只渲染一次
|
|
|
|
|
|
|
|
renderFuturesGrid(allFuturesData);
|
|
|
|
|
|
|
|
console.log('所有合约AI分析结果加载完成');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadFuturesFromConfig() {
|
|
|
|
async function loadFuturesFromConfig() {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const response = await fetch('/api/v1/config');
|
|
|
|
const response = await fetch('/api/v1/config');
|
|
|
|
@ -288,8 +358,10 @@ function generateMockFuturesData() {
|
|
|
|
|
|
|
|
|
|
|
|
function renderFuturesGrid(data) {
|
|
|
|
function renderFuturesGrid(data) {
|
|
|
|
const grid = document.getElementById('futures-grid');
|
|
|
|
const grid = document.getElementById('futures-grid');
|
|
|
|
|
|
|
|
console.log('渲染品种网格,数据量:', data.length);
|
|
|
|
if (data.length === 0) {
|
|
|
|
if (data.length === 0) {
|
|
|
|
grid.innerHTML = '<div class="empty-state">暂无数据</div>';
|
|
|
|
grid.innerHTML = '<div class="empty-state">暂无数据</div>';
|
|
|
|
|
|
|
|
console.log('显示暂无数据');
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -1595,29 +1667,7 @@ function syncAIToPanels(result) {
|
|
|
|
const kdjDiag = result.kdj_diagnosis || {};
|
|
|
|
const kdjDiag = result.kdj_diagnosis || {};
|
|
|
|
const scenarios = result.scenario_plans || {};
|
|
|
|
const scenarios = result.scenario_plans || {};
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 同步到AI交易建议卡片
|
|
|
|
// 1. 同步到技术指标卡片
|
|
|
|
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信息
|
|
|
|
// 从60min周期提取MACD和KDJ信息
|
|
|
|
const macd60 = fourDim['60min']?.macd || {};
|
|
|
|
const macd60 = fourDim['60min']?.macd || {};
|
|
|
|
const kdj60 = fourDim['60min']?.kdj || {};
|
|
|
|
const kdj60 = fourDim['60min']?.kdj || {};
|
|
|
|
|