英为财情定投计算器,用于写定投报告用,输入起止时间和定投金额,计算回报率等数据
当前为
// ==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';
}
});
})();