复利投资的 echarts 收益图

65 阅读3分钟

初始 10w, 每月定投 5000

image.png

初始 10w 不定投

image.png

代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>复利投资增长图表</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <style>
        #main {
            width: 1200px;
            height: 700px;
            margin: 30px auto;
        }
        .center {
            text-align: center;
            margin: 10px;
        }
        input {
            margin-right: 30px;
        }
    </style>
</head>
<body>

<div class="center">
    初始资金(元)
    <input type="number" id="initialCapital" placeholder="初始资金(元)" value="100000"/>
    年化收益率(%)
    <input type="number" id="annualReturn" placeholder="年化收益率(%)" value="12"/>
    投资期限(年)
    <input type="number" id="years" placeholder="投资期限(年)" value="30"/>
</div>
<div class="center">
    每月定投金额(元)
    <input type="number" id="monthlyInvestment" placeholder="每月定投金额" value="5000"/>
    定投期限(年)
    <input type="number" id="yearInvestmentCount" placeholder="定投期限(年)" value="10"/>
</div>


<div id="main"></div>

<script>

    // 投资参数
    var initialCapital = 100000; // 初始资金10万
    var monthlyInvestment = 5000; // 每月定投5000元
    var yearInvestmentCount = 10; // 定投期限(年), 0 代表不定投
    var years = 30; // 投资期限
    var annualReturn = 0.12; // 年化收益率12%

    updateParams()
    render()


    Array.from(document.querySelectorAll('input')).forEach(input => {
        input.addEventListener('input', function () {
            updateParams()
            render()
        })
    })


    // 监听输入框输入, 然后更新投资参数
    function updateParams() {
        initialCapital = +document.getElementById('initialCapital').value;
        annualReturn = +document.getElementById('annualReturn').value / 100;
        years = +document.getElementById('years').value;
        monthlyInvestment = +document.getElementById('monthlyInvestment').value;
        yearInvestmentCount = +document.getElementById('yearInvestmentCount').value;
    }

    function render() {
        // 初始化ECharts实例
        var chartDom = document.getElementById('main');
        var myChart = window.echarts.init(chartDom);

        var monthlyReturn = Math.pow(1 + annualReturn, 1/12) - 1; // 月收益率
        var totalMonths = years * 12;


        // 计算数据
        var yearData = [];
        var totalValueData = [];
        var profitData = [];
        var totalInvestmentData = [];
        var monthlyData = [];

        var currentTotal = initialCapital;
        var totalInvested = initialCapital;
        var addMonthlyInvestment = monthlyInvestment;

        // 按月计算复利
        for (var month = 0; month <= totalMonths; month++) {
            if (typeof yearInvestmentCount === 'number' && month > yearInvestmentCount * 12) {
                addMonthlyInvestment = 0;
            }
            if (month > 0) {
                // 每月先计算收益,再追加投资
                currentTotal = currentTotal * (1 + monthlyReturn);
                currentTotal += addMonthlyInvestment;
                totalInvested += addMonthlyInvestment;
            }

            // 每年记录一次数据
            if (month % 12 === 0) {
                var year = month / 12;
                var profit = currentTotal - totalInvested;

                yearData.push('第' + year + '年');
                totalValueData.push(parseFloat(currentTotal.toFixed(2)));
                profitData.push(parseFloat(profit.toFixed(2)));
                totalInvestmentData.push(parseFloat(totalInvested.toFixed(2)));
                monthlyData.push(month);
            }
        }

        // 关键年份数据
        var keyYears = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50].filter(year => year <= years);
        var keyYearData = keyYears.map(year => {
            var index = year;
            return {
                year: year,
                total: totalValueData[index],
                profit: profitData[index],
                invested: totalInvestmentData[index]
            };
        });

        var monthlyInvestmentText = (monthlyInvestment && yearInvestmentCount) ? ` + 每月定投${monthlyInvestment}元,定投期限${yearInvestmentCount}年` : '';

        // 配置项
        var option = {
            title: {
                text: `初始${initialCapital}${monthlyInvestmentText},年化收益率${annualReturn*100}%,${years}年复利增长`,
                subtext: '复利的惊人效应',
                left: 'center'
            },
            tooltip: {
                trigger: 'axis',
                formatter: function(params) {
                    var year = params[0].name;
                    var total = parseFloat(params[0].value).toLocaleString();
                    var profit = parseFloat(params[1].value).toLocaleString();
                    var invested = parseFloat(params[2].value).toLocaleString();

                    var returnRate = ((parseFloat(params[0].value) / parseFloat(params[2].value) - 1) * 100).toFixed(1);

                    return year + '<br/>' +
                        '总资产: ¥' + total + '元<br/>' +
                        '总投资: ¥' + invested + '元<br/>' +
                        '总收益: ¥' + profit + '元<br/>' +
                        '累计收益率: ' + returnRate + '%';
                }
            },
            legend: {
                data: ['总资产', '投资收益', '总投资本金'],
                top: '8%'
            },
            xAxis: {
                type: 'category',
                data: yearData,
                axisLabel: {
                    interval: 3,
                    rotate: 45
                }
            },
            yAxis: {
                type: 'value',
                name: '金额 (万元)',
                axisLabel: {
                    formatter: function(value) {
                        return (value / 10000).toFixed(0);
                    }
                }
            },
            series: [
                {
                    name: '总资产',
                    type: 'line',
                    data: totalValueData,
                    lineStyle: {
                        width: 4
                    },
                    itemStyle: {
                        color: '#5470c6'
                    },
                    areaStyle: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                            { offset: 0, color: 'rgba(84, 112, 198, 0.6)' },
                            { offset: 1, color: 'rgba(84, 112, 198, 0.1)' }
                        ])
                    },
                    smooth: true
                },
                {
                    name: '投资收益',
                    type: 'line',
                    data: profitData,
                    lineStyle: {
                        width: 3,
                        type: 'dashed'
                    },
                    itemStyle: {
                        color: '#91cc75'
                    },
                    smooth: true
                },
                {
                    name: '总投资本金',
                    type: 'line',
                    data: totalInvestmentData,
                    lineStyle: {
                        width: 2,
                        type: 'dotted'
                    },
                    itemStyle: {
                        color: '#ee6666'
                    },
                    smooth: true
                }
            ],
            grid: {
                left: '3%',
                right: '4%',
                bottom: '12%',
                top: '18%',
                containLabel: true
            },
            dataZoom: [
                {
                    type: 'inside',
                    start: 0,
                    end: 100
                },
                {
                    start: 0,
                    end: 100,
                    bottom: '5%'
                }
            ],
            graphic: {
                type: 'text',
                left: 'center',
                top: '12%',
                style: {
                    text: '关键年份数据:\n' + keyYearData.map(item =>
                        `第${item.year}年: 总资产${(item.total/10000).toFixed(0)}万 (收益${(item.profit/10000).toFixed(0)}万)`
                    ).join('\n'),
                    font: '12px Arial',
                    fill: '#333',
                    width: 400,
                    overflow: 'break'
                }
            }
        };

        // 使用配置项和数据显示图表
        myChart.setOption(option);

        // 响应窗口大小变化
        window.addEventListener('resize', function() {
            myChart.resize();
        });
    }

</script>
</body>
</html>