英为财情定投计算器

英为财情定投计算器,用于写定投报告用,输入起止时间和定投金额,计算回报率等数据

当前为 2024-12-12 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         英为财情定投计算器
// @namespace    http://tampermonkey.net/
// @version      2024-12-12
// @description  英为财情定投计算器,用于写定投报告用,输入起止时间和定投金额,计算回报率等数据
// @author       meteor
// @match        https://cn.investing.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=investing.com
// @grant        none
// @license GPLv3
// ==/UserScript==
(function() {
    'use strict';

    const styleContent = `
        #calculateButton {
            position: fixed;
            top: 50px;
            right: 10px;
            z-index: 10000;
            background-color: #28a745;
            color: white;
            padding: 10px 20px;
            border-radius: 5px;
            cursor: pointer;
            font-size: 16px;
        }
        #calculatorIframeOverlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            z-index: 9999;
            display: none;
        }
        #calculatorIframe {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 80%;
            height: 80%;
            border: none;
        }
    `;
    const styleElement = document.createElement('style');
    styleElement.textContent = styleContent;
    document.head.appendChild(styleElement);

    const calculateButton = document.createElement('div');
    calculateButton.id = 'calculateButton';
    calculateButton.textContent = '计算';
    document.body.appendChild(calculateButton);

    const overlay = document.createElement('div');
    overlay.id = 'calculatorIframeOverlay';
    const iframe = document.createElement('iframe');
    iframe.id = 'calculatorIframe';
    overlay.appendChild(iframe);
    document.body.appendChild(overlay);

    calculateButton.addEventListener('click', function() {
        const currentYear = new Date().getFullYear();

        iframe.srcdoc = `
            <!DOCTYPE html>
            <html style=" background: aliceblue; ">
            <head>
                <meta charset="UTF-8">
                <title>价格均值计算</title>
                <script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/9.0.1/bignumber.min.js"></script>
                <script src="https://registry.npmmirror.com/echarts/5.5.1/files/dist/echarts.min.js"></script>
                <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
                <style>
                    #chartContainer {
                        width: 1000px;
                        height: 1000px;
                    }
                </style>
                <script>
                    function findLastInstrumentIdValue(jsonString) {
                        const regex = /"instrument_id":\\s*(\\d+)/g;
                        let match;
                        let lastValue = null;
                        while ((match = regex.exec(jsonString)) !== null) {
                            lastValue = Number(match[1]);
                        }
                        return lastValue;
                    }

                    function getDateOfISOWeek(week, year) {
                        const simple = new Date(year, 0, 1 + (week - 1) * 7);
                        const dow = simple.getDay();
                        const ISOweekStart = simple;
                        if (dow <= 4)
                            ISOweekStart.setDate(simple.getDate() - simple.getDay() + 1);
                        else
                            ISOweekStart.setDate(simple.getDate() + 8 - simple.getDay());
                        return ISOweekStart;
                    }

                    document.addEventListener('DOMContentLoaded', function() {
                        const url = "https://api.investing.com/api/financialdata/historical/" + findLastInstrumentIdValue(JSON.stringify(top.google_tag_manager));

                        function fetchData() {
                            const startYear = parseInt(document.getElementById("startYear").value);
                            const endYear = parseInt(document.getElementById("endYear").value);
                            const startWeek = parseInt(document.getElementById("startWeek").value);
                            const endWeek = parseInt(document.getElementById("endWeek").value);

                            const startDate = getDateOfISOWeek(startWeek, startYear);
                            const endDate = getDateOfISOWeek(endWeek, endYear);
                            endDate.setDate(endDate.getDate() + 6);

                            const params = {
                                "start-date": startDate.toISOString().split('T')[0],
                                "end-date": endDate.toISOString().split('T')[0],
                                "time-frame": "Daily",
                                "add-missing-rows": false
                            };

                            const queryString = new URLSearchParams(params).toString();
                            const requestUrl = url + "?" + queryString;

                            fetch(requestUrl, {
                                method: 'GET',
                                headers: {
                                    'accept': '*/*',
                                    'accept-encoding': 'gzip, deflate, br, zstd',
                                    'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6,ru;q=0.5',
                                    'cache-control': 'no-cache',
                                    'content-type': 'application/json',
                                    'domain-id': 'cn',
                                    'origin': 'https://cn.investing.com',
                                    'pragma': 'no-cache',
                                    'priority': 'u=1, i',
                                    'referer': 'https://cn.investing.com/',
                                    'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
                                    'sec-ch-ua-mobile': '?0',
                                    'sec-ch-ua-platform': '"Windows"',
                                    'sec-fetch-dest': 'empty',
                                    'sec-fetch-mode': 'cors',
                                    'sec-fetch-site': 'same-site',
                                    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
                                }
                            })
                            .then(response => {
                                if (!response.ok) {
                                    throw new Error('Network response was not ok');
                                }
                                return response.json();
                            })
                            .then(data => {
                                console.log('Success:', data);
                                const klines = data.data.map(entry => ({
                                    date: entry.rowDateTimestamp,
                                    close: new BigNumber(entry.last_closeRaw)
                                }));
                                window.klines = klines;
                                calculateAverage();
                            });
                        }

                        document.getElementById("fetchDataBtn").addEventListener("click", fetchData);

                        function calculateAverage() {
                            const BigNumber = window.BigNumber;
                            const investmentAmount = new BigNumber(document.getElementById("investmentAmount").value);
                            let tableBody = document.getElementById("resultTable").getElementsByTagName("tbody")[0];
                            tableBody.innerHTML = "";

                            let klines = Array.from(window.klines);
                            klines.sort((a, b) => new Date(a.date) - new Date(b.date)); // Sort by date

                            let weekData = {};
                            klines.forEach(entry => {
                                let date = new Date(entry.date);
                                let year = date.getFullYear();
                                let weekNumber = getWeekNumber(date);
                                let closePrice = entry.close;

                                const key = \`\${year}-\${weekNumber}\`;
                                if (!weekData[key]) {
                                    weekData[key] = { year, week: weekNumber, prices: [] };
                                }
                                weekData[key].prices.push(closePrice);
                            });

                            let accumulatedShares = new BigNumber(0);
                            let totalPrincipal = new BigNumber(0);
                            let chartData = [];
                            Object.entries(weekData).forEach(([key, data], index) => {
                                let sum = data.prices.reduce((acc, val) => acc.plus(val), new BigNumber(0));
                                let average = sum.dividedBy(data.prices.length);

                                let sharesPurchased = investmentAmount.dividedBy(average);
                                accumulatedShares = accumulatedShares.plus(sharesPurchased);
                                totalPrincipal = totalPrincipal.plus(investmentAmount);

                                let npv = accumulatedShares.times(average);
                                let returnRate = npv.minus(totalPrincipal).dividedBy(totalPrincipal).times(100);

                                let row = document.createElement("tr");
                                let serialCell = document.createElement("td");
                                let yearCell = document.createElement("td");
                                let weekCell = document.createElement("td");
                                let averageCell = document.createElement("td");
                                let sharesCell = document.createElement("td");
                                let totalSharesCell = document.createElement("td");
                                let npvCell = document.createElement("td");
                                let principalCell = document.createElement("td");
                                let returnRateCell = document.createElement("td");

                                serialCell.textContent = index + 1;
                                yearCell.textContent = data.year;
                                weekCell.textContent = \`第\${data.week}周\`;
                                averageCell.textContent = average.toFixed(8);
                                sharesCell.textContent = sharesPurchased.toFixed(8);
                                totalSharesCell.textContent = accumulatedShares.toFixed(8);
                                npvCell.textContent = npv.toFixed(8);
                                principalCell.textContent = totalPrincipal.toFixed(8);
                                returnRateCell.textContent = returnRate.toFixed(2) + '%';

                                row.appendChild(serialCell);
                                row.appendChild(yearCell);
                                row.appendChild(weekCell);
                                row.appendChild(averageCell);
                                row.appendChild(sharesCell);
                                row.appendChild(totalSharesCell);
                                row.appendChild(npvCell);
                                row.appendChild(principalCell);
                                row.appendChild(returnRateCell);

                                tableBody.appendChild(row);

                                chartData.push({
                                    week: \`\${data.year}年第\${data.week}周\`,
                                    average: average.toNumber(),
                                    npv: npv.toNumber(),
                                    principal: totalPrincipal.toNumber(),
                                    returnRate: returnRate.toNumber()
                                });
                            });

                            drawChart(chartData);

                            document.getElementById("exportBtn").onclick = function() {
                                exportToExcel(chartData);
                            };
                        }

                        function getWeekNumber(date) {
                            const onejan = new Date(date.getFullYear(), 0, 1);
                            const dayOffset = (date.getDay() + 6) % 7; // ISO day of the week
                            const startDayOffset = (onejan.getDay() + 6) % 7;
                            return Math.floor(((date - onejan) / (24 * 60 * 60 * 1000) + startDayOffset) / 7) + 1;
                        }

                        function drawChart(chartData) {
                            let chartDom = document.getElementById('chartContainer');
                            let myChart = echarts.init(chartDom);

                            let option = {
                                title: {
                                    text: '每周价格与投资'
                                },
                                        legend: {
            data: ['均价', '净现值', '总本金', '回报率']
        },
                                tooltip: {},
                                xAxis: {
                                    type: 'category',
                                    data: chartData.map(data => data.week)
                                },
                                yAxis: [
                                    {
                                        type: 'value',
                                        name: '均价 (美元)'
                                    },
                                    {
                                        type: 'value',
                                        name: '金额 (美元)',
                                        position: 'right'
                                    },
                                    {
                                        type: 'value',
                                        name: '回报率 (%)',
                                        position: 'right',
                                        offset: 60
                                    }
                                ],
                                series: [
                                    {
                                        name: '均价',
                                        data: chartData.map(data => data.average),
                                        type: 'line',
                                        yAxisIndex: 0,
                                        itemStyle: {color: 'blue'}
                                    },
                                    {
                                        name: '净现值',
                                        data: chartData.map(data => data.npv),
                                        type: 'line',
                                        yAxisIndex: 1,
                                        itemStyle: {color: 'green'}
                                    },
                                    {
                                        name: '总本金',
                                        data: chartData.map(data => data.principal),
                                        type: 'line',
                                        yAxisIndex: 1,
                                        itemStyle: {color: 'yellow'}
                                    },
                                    {
                                        name: '回报率',
                                        data: chartData.map(data => data.returnRate),
                                        type: 'line',
                                        yAxisIndex: 2,
                                        itemStyle: {color: 'red'}
                                    }
                                ]
                            };

                            myChart.setOption(option);
                        }

                        function exportToExcel(chartData) {
                            let workbook = XLSX.utils.book_new();

                            let weeklySheetData = [['投资周数', '年份', '周数', '均价', '投资股份', '累计股份', '净现值', '总本金', '投资回报率']];
                            chartData.forEach((item, index) => weeklySheetData.push([index + 1, item.week.split('年')[0], item.week.split('第')[1], item.average.toFixed(8), '-', '-', item.npv.toFixed(8), item.principal.toFixed(8), item.returnRate.toFixed(2) + '%']));
                            let weeklySheet = XLSX.utils.aoa_to_sheet(weeklySheetData);
                            XLSX.utils.book_append_sheet(workbook, weeklySheet, '每周投资数据');

                            XLSX.writeFile(workbook, '价格与投资数据.xlsx');
                        }
                    });
                </script>
            </head>
            <body>
                <h1>价格均值与投资计算</h1>

                <label for="startYear">开始年份:</label>
                <input type="number" id="startYear" value="${currentYear - 2}"><br>

                <label for="endYear">结束年份:</label>
                <input type="number" id="endYear" value="${currentYear}"><br><br>

                <label for="startWeek">开始周数:</label>
                <select id="startWeek">${Array.from({length: 52}, (_, i) => `<option value="${i+1}">${i+1}</option>`).join('')}</select><br>

                <label for="endWeek">结束周数:</label>
                <select id="endWeek">${Array.from({length: 52}, (_, i) => `<option value="${i+1}">${i+1}</option>`).join('')}</select><br><br>

                <label for="investmentAmount">定投金额:</label>
                <input type="number" id="investmentAmount" value="100"><br><br>

                <button id="fetchDataBtn">获取数据</button>
                <button id="exportBtn">导出 Excel</button>

                <h2>结果</h2>
                <table id="resultTable" border="1" style="display: inline-block;">
                    <thead>
                        <tr>
                            <th>投资周数</th>
                            <th>年份</th>
                            <th>周数</th>
                            <th>均价 (美元)</th>
                            <th>投资股份</th>
                            <th>累计股份</th>
                            <th>净现值 (美元)</th>
                            <th>总本金 (美元)</th>
                            <th>投资回报率 (%)</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>

                <div id="chartContainer" style="display: inline-block;"></div>
            </body>
            </html>
        `;
        overlay.style.display = 'block';
    });

    overlay.addEventListener('click', function(event) {
        if (event.target === overlay) {
            overlay.style.display = 'none';
        }
    });

})();