|
{% 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="fundamental-form" class="row g-2">
|
|
<div class="col-md-4">
|
|
<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">
|
|
<button type="submit" class="btn btn-primary btn-sm w-100">
|
|
<i class="fas fa-search"></i> 分析
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="fundamental-result" style="display: none;">
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-6">
|
|
<div class="card h-100">
|
|
<div class="card-header py-2">
|
|
<h5 class="mb-0">财务概况</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-7">
|
|
<h3 id="stock-name" class="mb-0 fs-4"></h3>
|
|
<p id="stock-info" class="text-muted mb-0 small"></p>
|
|
</div>
|
|
<div class="col-md-5 text-end">
|
|
<span id="fundamental-score" class="badge rounded-pill score-pill"></span>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>估值指标</h6>
|
|
<ul class="list-unstyled mt-1 mb-0 small">
|
|
<li><span class="text-muted">PE(TTM):</span> <span id="pe-ttm"></span></li>
|
|
<li><span class="text-muted">PB:</span> <span id="pb"></span></li>
|
|
<li><span class="text-muted">PS(TTM):</span> <span id="ps-ttm"></span></li>
|
|
</ul>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>盈利能力</h6>
|
|
<ul class="list-unstyled mt-1 mb-0 small">
|
|
<li><span class="text-muted">ROE:</span> <span id="roe"></span></li>
|
|
<li><span class="text-muted">毛利率:</span> <span id="gross-margin"></span></li>
|
|
<li><span class="text-muted">净利润率:</span> <span id="net-profit-margin"></span></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="card h-100">
|
|
<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>营收增长</h6>
|
|
<ul class="list-unstyled mt-1 mb-0 small">
|
|
<li><span class="text-muted">3年CAGR:</span> <span id="revenue-growth-3y"></span></li>
|
|
<li><span class="text-muted">5年CAGR:</span> <span id="revenue-growth-5y"></span></li>
|
|
</ul>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>利润增长</h6>
|
|
<ul class="list-unstyled mt-1 mb-0 small">
|
|
<li><span class="text-muted">3年CAGR:</span> <span id="profit-growth-3y"></span></li>
|
|
<li><span class="text-muted">5年CAGR:</span> <span id="profit-growth-5y"></span></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3">
|
|
<div id="growth-chart" style="height: 150px;"></div>
|
|
</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">
|
|
<h5 class="mb-0">估值评分</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="valuation-chart" style="height: 200px;"></div>
|
|
<p id="valuation-comment" class="small text-muted mt-2"></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card h-100">
|
|
<div class="card-header py-2">
|
|
<h5 class="mb-0">财务健康评分</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="financial-chart" style="height: 200px;"></div>
|
|
<p id="financial-comment" class="small text-muted mt-2"></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card h-100">
|
|
<div class="card-header py-2">
|
|
<h5 class="mb-0">成长性评分</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="growth-score-chart" style="height: 200px;"></div>
|
|
<p id="growth-comment" class="small text-muted mt-2"></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
$(document).ready(function() {
|
|
$('#fundamental-form').submit(function(e) {
|
|
e.preventDefault();
|
|
const stockCode = $('#stock-code').val().trim();
|
|
const marketType = $('#market-type').val();
|
|
|
|
if (!stockCode) {
|
|
showError('请输入股票代码!');
|
|
return;
|
|
}
|
|
|
|
fetchFundamentalAnalysis(stockCode);
|
|
});
|
|
});
|
|
|
|
function fetchFundamentalAnalysis(stockCode) {
|
|
showLoading();
|
|
|
|
$.ajax({
|
|
url: '/api/fundamental_analysis',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify({
|
|
stock_code: stockCode
|
|
}),
|
|
success: function(response) {
|
|
hideLoading();
|
|
renderFundamentalAnalysis(response, stockCode);
|
|
$('#fundamental-result').show();
|
|
},
|
|
error: function(xhr, status, error) {
|
|
hideLoading();
|
|
let errorMsg = '获取基本面分析失败';
|
|
if (xhr.responseJSON && xhr.responseJSON.error) {
|
|
errorMsg += ': ' + xhr.responseJSON.error;
|
|
} else if (error) {
|
|
errorMsg += ': ' + error;
|
|
}
|
|
showError(errorMsg);
|
|
}
|
|
});
|
|
}
|
|
|
|
function renderFundamentalAnalysis(data, stockCode) {
|
|
|
|
$('#stock-name').text(data.details.indicators.stock_name || stockCode);
|
|
$('#stock-info').text(data.details.indicators.industry || '未知行业');
|
|
|
|
|
|
const scoreClass = getScoreColorClass(data.total);
|
|
$('#fundamental-score').text(data.total).addClass(scoreClass);
|
|
|
|
|
|
$('#pe-ttm').text(formatNumber(data.details.indicators.pe_ttm, 2));
|
|
$('#pb').text(formatNumber(data.details.indicators.pb, 2));
|
|
$('#ps-ttm').text(formatNumber(data.details.indicators.ps_ttm, 2));
|
|
|
|
|
|
$('#roe').text(formatPercent(data.details.indicators.roe, 2));
|
|
$('#gross-margin').text(formatPercent(data.details.indicators.gross_margin, 2));
|
|
$('#net-profit-margin').text(formatPercent(data.details.indicators.net_profit_margin, 2));
|
|
|
|
|
|
$('#revenue-growth-3y').text(formatPercent(data.details.growth.revenue_growth_3y, 2));
|
|
$('#revenue-growth-5y').text(formatPercent(data.details.growth.revenue_growth_5y, 2));
|
|
$('#profit-growth-3y').text(formatPercent(data.details.growth.profit_growth_3y, 2));
|
|
$('#profit-growth-5y').text(formatPercent(data.details.growth.profit_growth_5y, 2));
|
|
|
|
|
|
$('#valuation-comment').text("估值处于行业" + (data.valuation > 20 ? "合理水平" : "偏高水平"));
|
|
$('#financial-comment').text("财务状况" + (data.financial_health > 30 ? "良好" : "一般"));
|
|
$('#growth-comment').text("成长性" + (data.growth > 20 ? "较好" : "一般"));
|
|
|
|
|
|
renderValuationChart(data.valuation);
|
|
renderFinancialChart(data.financial_health);
|
|
renderGrowthScoreChart(data.growth);
|
|
renderGrowthChart(data.details.growth);
|
|
}
|
|
|
|
function renderValuationChart(score) {
|
|
const options = {
|
|
series: [score],
|
|
chart: {
|
|
height: 200,
|
|
type: 'radialBar',
|
|
},
|
|
plotOptions: {
|
|
radialBar: {
|
|
hollow: {
|
|
size: '70%',
|
|
},
|
|
dataLabels: {
|
|
name: {
|
|
fontSize: '22px',
|
|
},
|
|
value: {
|
|
fontSize: '16px',
|
|
},
|
|
total: {
|
|
show: true,
|
|
label: '估值',
|
|
formatter: function() {
|
|
return score;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
colors: ['#1ab7ea'],
|
|
labels: ['估值'],
|
|
};
|
|
|
|
const chart = new ApexCharts(document.querySelector("#valuation-chart"), options);
|
|
chart.render();
|
|
}
|
|
|
|
function renderFinancialChart(score) {
|
|
const options = {
|
|
series: [score],
|
|
chart: {
|
|
height: 200,
|
|
type: 'radialBar',
|
|
},
|
|
plotOptions: {
|
|
radialBar: {
|
|
hollow: {
|
|
size: '70%',
|
|
},
|
|
dataLabels: {
|
|
name: {
|
|
fontSize: '22px',
|
|
},
|
|
value: {
|
|
fontSize: '16px',
|
|
},
|
|
total: {
|
|
show: true,
|
|
label: '财务',
|
|
formatter: function() {
|
|
return score;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
colors: ['#20E647'],
|
|
labels: ['财务'],
|
|
};
|
|
|
|
const chart = new ApexCharts(document.querySelector("#financial-chart"), options);
|
|
chart.render();
|
|
}
|
|
|
|
function renderGrowthScoreChart(score) {
|
|
const options = {
|
|
series: [score],
|
|
chart: {
|
|
height: 200,
|
|
type: 'radialBar',
|
|
},
|
|
plotOptions: {
|
|
radialBar: {
|
|
hollow: {
|
|
size: '70%',
|
|
},
|
|
dataLabels: {
|
|
name: {
|
|
fontSize: '22px',
|
|
},
|
|
value: {
|
|
fontSize: '16px',
|
|
},
|
|
total: {
|
|
show: true,
|
|
label: '成长',
|
|
formatter: function() {
|
|
return score;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
colors: ['#F9CE1D'],
|
|
labels: ['成长'],
|
|
};
|
|
|
|
const chart = new ApexCharts(document.querySelector("#growth-score-chart"), options);
|
|
chart.render();
|
|
}
|
|
|
|
function renderGrowthChart(growthData) {
|
|
const options = {
|
|
series: [{
|
|
name: '营收增长率',
|
|
data: [
|
|
growthData.revenue_growth_3y || 0,
|
|
growthData.revenue_growth_5y || 0
|
|
]
|
|
}, {
|
|
name: '利润增长率',
|
|
data: [
|
|
growthData.profit_growth_3y || 0,
|
|
growthData.profit_growth_5y || 0
|
|
]
|
|
}],
|
|
chart: {
|
|
type: 'bar',
|
|
height: 150,
|
|
toolbar: {
|
|
show: false
|
|
}
|
|
},
|
|
plotOptions: {
|
|
bar: {
|
|
horizontal: false,
|
|
columnWidth: '55%',
|
|
endingShape: 'rounded'
|
|
},
|
|
},
|
|
dataLabels: {
|
|
enabled: false
|
|
},
|
|
stroke: {
|
|
show: true,
|
|
width: 2,
|
|
colors: ['transparent']
|
|
},
|
|
xaxis: {
|
|
categories: ['3年CAGR', '5年CAGR'],
|
|
},
|
|
yaxis: {
|
|
title: {
|
|
text: '百分比 (%)'
|
|
}
|
|
},
|
|
fill: {
|
|
opacity: 1
|
|
},
|
|
tooltip: {
|
|
y: {
|
|
formatter: function(val) {
|
|
return val + "%"
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const chart = new ApexCharts(document.querySelector("#growth-chart"), options);
|
|
chart.render();
|
|
}
|
|
</script>
|
|
{% endblock %} |