diff --git a/frontend/paper-trading.html b/frontend/paper-trading.html
index c835b8b..23f5ac1 100644
--- a/frontend/paper-trading.html
+++ b/frontend/paper-trading.html
@@ -1907,9 +1907,11 @@
const data = await response.json();
if (data.success) {
this.dailyReturns = data.data;
- // 等待 DOM 更新后渲染图表
+ // 等待 DOM 更新后渲染图表,添加额外延迟确保 canvas 完全渲染
this.$nextTick(() => {
- this.renderCharts();
+ setTimeout(() => {
+ this.renderCharts();
+ }, 100);
});
}
} catch (e) {
@@ -1928,154 +1930,198 @@
// 渲染净值曲线图
renderBalanceChart() {
const canvas = document.getElementById('balanceChart');
- if (!canvas) return;
+ if (!canvas) {
+ console.warn('balanceChart canvas not found');
+ return;
+ }
// 销毁已存在的图表
if (this.balanceChart) {
this.balanceChart.destroy();
+ this.balanceChart = null;
}
const ctx = canvas.getContext('2d');
+ if (!ctx) {
+ console.warn('Cannot get 2d context');
+ return;
+ }
+
+ // 确保有数据
+ if (!this.dailyReturns || this.dailyReturns.length === 0) {
+ console.warn('No daily returns data');
+ return;
+ }
+
const labels = this.dailyReturns.map(d => d.date);
const balanceData = this.dailyReturns.map(d => d.balance);
- this.balanceChart = new Chart(ctx, {
- type: 'line',
- data: {
- labels: labels,
- datasets: [{
- label: '账户净值',
- data: balanceData,
- borderColor: '#00ff41',
- backgroundColor: 'rgba(0, 255, 65, 0.1)',
- borderWidth: 2,
- fill: true,
- tension: 0.4,
- pointRadius: 2,
- pointHoverRadius: 5
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false
- },
- tooltip: {
- mode: 'index',
- intersect: false,
- callbacks: {
- label: function(context) {
- return '净值: $' + context.parsed.y.toFixed(2);
+ console.log('Rendering balance chart:', { labels, balanceData });
+
+ try {
+ this.balanceChart = new Chart(ctx, {
+ type: 'line',
+ data: {
+ labels: labels,
+ datasets: [{
+ label: '账户净值',
+ data: balanceData,
+ borderColor: '#00ff41',
+ backgroundColor: 'rgba(0, 255, 65, 0.1)',
+ borderWidth: 2,
+ fill: true,
+ tension: 0.4,
+ pointRadius: 2,
+ pointHoverRadius: 5
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ display: false
+ },
+ tooltip: {
+ mode: 'index',
+ intersect: false,
+ callbacks: {
+ label: function(context) {
+ return '净值: $' + context.parsed.y.toFixed(2);
+ }
}
}
- }
- },
- scales: {
- x: {
- display: true,
- grid: {
- color: 'rgba(255, 255, 255, 0.1)'
- },
- ticks: {
- color: 'rgba(255, 255, 255, 0.6)',
- maxRotation: 45,
- minRotation: 45
- }
},
- y: {
- display: true,
- grid: {
- color: 'rgba(255, 255, 255, 0.1)'
+ scales: {
+ x: {
+ display: true,
+ grid: {
+ color: 'rgba(255, 255, 255, 0.1)'
+ },
+ ticks: {
+ color: 'rgba(255, 255, 255, 0.6)',
+ maxRotation: 45,
+ minRotation: 45
+ }
},
- ticks: {
- color: 'rgba(255, 255, 255, 0.6)',
- callback: function(value) {
- return '$' + value.toFixed(0);
+ y: {
+ display: true,
+ grid: {
+ color: 'rgba(255, 255, 255, 0.1)'
+ },
+ ticks: {
+ color: 'rgba(255, 255, 255, 0.6)',
+ callback: function(value) {
+ return '$' + value.toFixed(0);
+ }
}
}
+ },
+ interaction: {
+ mode: 'nearest',
+ axis: 'x',
+ intersect: false
}
- },
- interaction: {
- mode: 'nearest',
- axis: 'x',
- intersect: false
}
- }
- });
+ });
+ console.log('Balance chart rendered successfully');
+ } catch (e) {
+ console.error('Error rendering balance chart:', e);
+ }
},
// 渲染收益率柱状图
renderReturnsChart() {
const canvas = document.getElementById('returnsChart');
- if (!canvas) return;
+ if (!canvas) {
+ console.warn('returnsChart canvas not found');
+ return;
+ }
// 销毁已存在的图表
if (this.returnsChart) {
this.returnsChart.destroy();
+ this.returnsChart = null;
}
const ctx = canvas.getContext('2d');
+ if (!ctx) {
+ console.warn('Cannot get 2d context');
+ return;
+ }
+
+ // 确保有数据
+ if (!this.dailyReturns || this.dailyReturns.length === 0) {
+ console.warn('No daily returns data');
+ return;
+ }
+
const labels = this.dailyReturns.map(d => d.date);
const returnsData = this.dailyReturns.map(d => d.return_percent);
const colors = returnsData.map(v => v >= 0 ? '#00ff41' : '#ff4444');
- this.returnsChart = new Chart(ctx, {
- type: 'bar',
- data: {
- labels: labels,
- datasets: [{
- label: '日收益率',
- data: returnsData,
- backgroundColor: colors,
- borderColor: colors,
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false
- },
- tooltip: {
- callbacks: {
- label: function(context) {
- const value = context.parsed.y;
- return '收益率: ' + (value >= 0 ? '+' : '') + value.toFixed(2) + '%';
- }
- }
- }
+ console.log('Rendering returns chart:', { labels, returnsData });
+
+ try {
+ this.returnsChart = new Chart(ctx, {
+ type: 'bar',
+ data: {
+ labels: labels,
+ datasets: [{
+ label: '日收益率',
+ data: returnsData,
+ backgroundColor: colors,
+ borderColor: colors,
+ borderWidth: 1
+ }]
},
- scales: {
- x: {
- display: true,
- grid: {
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
display: false
},
- ticks: {
- color: 'rgba(255, 255, 255, 0.6)',
- maxRotation: 45,
- minRotation: 45
+ tooltip: {
+ callbacks: {
+ label: function(context) {
+ const value = context.parsed.y;
+ return '收益率: ' + (value >= 0 ? '+' : '') + value.toFixed(2) + '%';
+ }
+ }
}
},
- y: {
- display: true,
- grid: {
- color: 'rgba(255, 255, 255, 0.1)'
+ scales: {
+ x: {
+ display: true,
+ grid: {
+ display: false
+ },
+ ticks: {
+ color: 'rgba(255, 255, 255, 0.6)',
+ maxRotation: 45,
+ minRotation: 45
+ }
},
- ticks: {
- color: 'rgba(255, 255, 255, 0.6)',
- callback: function(value) {
- return value.toFixed(1) + '%';
+ y: {
+ display: true,
+ grid: {
+ color: 'rgba(255, 255, 255, 0.1)'
+ },
+ ticks: {
+ color: 'rgba(255, 255, 255, 0.6)',
+ callback: function(value) {
+ return value.toFixed(1) + '%';
+ }
}
}
}
}
- }
- });
+ });
+ console.log('Returns chart rendered successfully');
+ } catch (e) {
+ console.error('Error rendering returns chart:', e);
+ }
}
},
computed: {
@@ -2161,9 +2207,11 @@
if (this.dailyReturns.length === 0) {
this.fetchDailyReturns();
} else {
- // 数据已存在,需要重新渲染图表
+ // 数据已存在,需要重新渲染图表,添加延迟确保 DOM 显示
this.$nextTick(() => {
- this.renderCharts();
+ setTimeout(() => {
+ this.renderCharts();
+ }, 100);
});
}
}