stock / templates /scenario_predict.html
gitdeem's picture
Upload 32 files
f5d52f6 verified
{% extends "layout.html" %}
{% block title %}情景预测 - {{ stock_code }} - 智能分析系统{% endblock %}
{% block content %}
<div class="container-fluid py-3">
<div id="alerts-container"></div>
<div class="row mb-3">
<div class="col-12">
<div class="card">
<div class="card-header py-2">
<h5 class="mb-0">多情景预测</h5>
</div>
<div class="card-body py-2">
<form id="scenario-form" class="row g-2">
<div class="col-md-3">
<div class="input-group input-group-sm">
<span class="input-group-text">股票代码</span>
<input type="text" class="form-control" id="stock-code" placeholder="例如: 600519" required>
</div>
</div>
<div class="col-md-3">
<div class="input-group input-group-sm">
<span class="input-group-text">市场</span>
<select class="form-select" id="market-type">
<option value="A" selected>A股</option>
<option value="HK">港股</option>
<option value="US">美股</option>
</select>
</div>
</div>
<div class="col-md-3">
<div class="input-group input-group-sm">
<span class="input-group-text">预测天数</span>
<select class="form-select" id="days">
<option value="30">30天</option>
<option value="60" selected>60天</option>
<option value="90">90天</option>
<option value="180">180天</option>
</select>
</div>
</div>
<div class="col-md-3">
<button type="submit" class="btn btn-primary btn-sm w-100">
<i class="fas fa-chart-line"></i> 预测
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div id="loading-panel" class="text-center py-5" style="display: none;">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3 mb-0">正在生成预测结果...</p>
<p class="text-muted small mt-2">
<i class="fas fa-info-circle"></i>
AI分析需要一些时间,请耐心等待
</p>
</div>
<div id="scenario-result" style="display: none;">
<div class="row g-3 mb-3">
<div class="col-12">
<div class="card">
<div class="card-header py-2">
<h5 class="mb-0">价格预测图</h5>
</div>
<div class="card-body p-0">
<div id="price-prediction-chart" style="height: 400px;"></div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-4">
<div class="card h-100">
<div class="card-header py-2 bg-success text-white">
<h5 class="mb-0">乐观情景</h5>
</div>
<div class="card-body">
<div class="d-flex justify-content-between mb-3">
<div>
<h6>目标价</h6>
<h3 id="optimistic-price" class="text-success">--</h3>
</div>
<div>
<h6>预期涨幅</h6>
<h3 id="optimistic-change" class="text-success">--</h3>
</div>
</div>
<p id="optimistic-analysis" class="small"></p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card h-100">
<div class="card-header py-2 bg-primary text-white">
<h5 class="mb-0">中性情景</h5>
</div>
<div class="card-body">
<div class="d-flex justify-content-between mb-3">
<div>
<h6>目标价</h6>
<h3 id="neutral-price" class="text-primary">--</h3>
</div>
<div>
<h6>预期涨幅</h6>
<h3 id="neutral-change" class="text-primary">--</h3>
</div>
</div>
<p id="neutral-analysis" class="small"></p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card h-100">
<div class="card-header py-2 bg-danger text-white">
<h5 class="mb-0">悲观情景</h5>
</div>
<div class="card-body">
<div class="d-flex justify-content-between mb-3">
<div>
<h6>目标价</h6>
<h3 id="pessimistic-price" class="text-danger">--</h3>
</div>
<div>
<h6>预期涨幅</h6>
<h3 id="pessimistic-change" class="text-danger">--</h3>
</div>
</div>
<p id="pessimistic-analysis" class="small"></p>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-12">
<div class="card">
<div class="card-header py-2">
<h5 class="mb-0">风险与机会</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6 class="text-danger"><i class="fas fa-exclamation-triangle"></i> 风险因素</h6>
<ul id="risk-factors" class="small"></ul>
</div>
<div class="col-md-6">
<h6 class="text-success"><i class="fas fa-lightbulb"></i> 有利因素</h6>
<ul id="opportunity-factors" class="small"></ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function() {
$('#scenario-form').submit(function(e) {
e.preventDefault();
const stockCode = $('#stock-code').val().trim();
const marketType = $('#market-type').val();
const days = $('#days').val();
if (!stockCode) {
showError('请输入股票代码!');
return;
}
fetchScenarioPrediction(stockCode, marketType, days);
});
});
function fetchScenarioPrediction(stockCode, marketType, days) {
$('#loading-panel').show();
$('#scenario-result').hide();
$.ajax({
url: '/api/scenario_predict',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
stock_code: stockCode,
market_type: marketType,
days: parseInt(days)
}),
success: function(response) {
$('#loading-panel').hide();
renderScenarioPrediction(response, stockCode);
$('#scenario-result').show();
},
error: function(xhr, status, error) {
$('#loading-panel').hide();
let errorMsg = '获取情景预测失败';
if (xhr.responseJSON && xhr.responseJSON.error) {
errorMsg += ': ' + xhr.responseJSON.error;
} else if (error) {
errorMsg += ': ' + error;
}
showError(errorMsg);
}
});
}
function renderScenarioPrediction(data, stockCode) {
// 设置乐观情景数据
$('#optimistic-price').text('¥' + formatNumber(data.optimistic.target_price, 2));
$('#optimistic-change').text(formatPercent(data.optimistic.change_percent, 2));
$('#optimistic-analysis').text(data.optimistic_analysis || '暂无分析');
// 设置中性情景数据
$('#neutral-price').text('¥' + formatNumber(data.neutral.target_price, 2));
$('#neutral-change').text(formatPercent(data.neutral.change_percent, 2));
$('#neutral-analysis').text(data.neutral_analysis || '暂无分析');
// 设置悲观情景数据
$('#pessimistic-price').text('¥' + formatNumber(data.pessimistic.target_price, 2));
$('#pessimistic-change').text(formatPercent(data.pessimistic.change_percent, 2));
$('#pessimistic-analysis').text(data.pessimistic_analysis || '暂无分析');
// 设置风险与机会因素
setDefaultRiskOpportunityFactors();
// 渲染价格预测图表
renderPricePredictionChart(data);
}
function setDefaultRiskOpportunityFactors() {
// 示例风险因素
const riskFactors = [
'宏观经济下行压力增大',
'行业政策收紧可能性',
'原材料价格上涨',
'市场竞争加剧',
'技术迭代风险'
];
// 示例有利因素
const opportunityFactors = [
'行业景气度持续向好',
'公司新产品上市',
'成本控制措施见效',
'产能扩张计划',
'国际市场开拓机会'
];
// 填充HTML
$('#risk-factors').html('');
riskFactors.forEach(factor => {
$('#risk-factors').append(`<li>${factor}</li>`);
});
$('#opportunity-factors').html('');
opportunityFactors.forEach(factor => {
$('#opportunity-factors').append(`<li>${factor}</li>`);
});
}
function renderPricePredictionChart(data) {
// 准备数据
const currentPrice = data.current_price;
// 提取日期和价格路径
const dates = Object.keys(data.optimistic.path);
const optimisticPrices = Object.values(data.optimistic.path);
const neutralPrices = Object.values(data.neutral.path);
const pessimisticPrices = Object.values(data.pessimistic.path);
const options = {
series: [
{
name: '乐观情景',
data: optimisticPrices.map((price, i) => ({
x: new Date(dates[i]),
y: price
}))
},
{
name: '中性情景',
data: neutralPrices.map((price, i) => ({
x: new Date(dates[i]),
y: price
}))
},
{
name: '悲观情景',
data: pessimisticPrices.map((price, i) => ({
x: new Date(dates[i]),
y: price
}))
}
],
chart: {
height: 400,
type: 'line',
zoom: {
enabled: true
},
toolbar: {
show: true
}
},
colors: ['#20E647', '#2E93fA', '#FF4560'],
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth',
width: [3, 3, 3]
},
title: {
text: '多情景预测',
align: 'left'
},
grid: {
borderColor: '#e7e7e7',
row: {
colors: ['#f3f3f3', 'transparent'],
opacity: 0.5
},
},
markers: {
size: 1
},
xaxis: {
type: 'datetime',
title: {
text: '日期'
}
},
yaxis: {
title: {
text: '价格 (¥)'
},
labels: {
formatter: function(val) {
return formatNumber(val, 2);
}
}
},
legend: {
position: 'top',
horizontalAlign: 'right'
},
tooltip: {
shared: true,
intersect: false,
y: {
formatter: function(value) {
return '¥' + formatNumber(value, 2);
}
}
},
annotations: {
yaxis: [
{
y: currentPrice,
borderColor: '#000',
label: {
borderColor: '#000',
style: {
color: '#fff',
background: '#000'
},
text: '当前价格: ¥' + formatNumber(currentPrice, 2)
}
}
]
}
};
const chart = new ApexCharts(document.querySelector("#price-prediction-chart"), options);
chart.render();
}
</script>
{% endblock %}