英为财情定投计算器

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

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

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         英为财情定投计算器
// @namespace    http://tampermonkey.net/
// @version      2024-12-13
// @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: 98%;
            height: 95%;
            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: 1400px;
                        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() {


var iframeWidth = window.innerWidth
            || document.documentElement.clientWidth
            || document.body.clientWidth;

        const chartContainer =document.getElementById('chartContainer');
        if (chartContainer) {
        iframeWidth=iframeWidth<1400?1400:iframeWidth;
            chartContainer.style.width = iframeWidth + 'px';  // 调整 chartContainer 的宽度
        }


                            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(),
                                sharesPurchased: sharesPurchased.toNumber(),
                                accumulatedShares: accumulatedShares.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.sharesPurchased.toFixed(8), item.accumulatedShares.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';
        }
    });

})();