英为财情定投计算器

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

目前為 2024-12-13 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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';
        }
    });

})();