// ==UserScript==
// @name 天天基金数据抽取
// @namespace http://tampermonkey.net/
// @version 2.0.1
// @description 用以快捷显示累计净值,任期回报,平均回报,一年前累计净值,月高点,月低点
// @author aotmd
// @match http://fund.eastmoney.com/*
// @match http://*.eastmoney.com/*
// @exclude http://fund.eastmoney.com/manager/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @noframes
// @license MIT
// @downloadURL none
// ==/UserScript==
const setting = {
是否查看近一年净值: true,//批量查看时防止卡顿,建议关闭
自动关闭不满5年基金: false,//自动关闭基金经理任期低于5年的基金,选指数基金时关闭
自动关闭平均回报过低基金: false,//自动关闭业绩不好的基金
指数基金年化代替为成立以来年化: false,//用来选指数基金
指数用成立时间不满5年则关闭: false,//用来选指数基金
满五年日期常量: "2016-12-31",
回报率常量: 19,
};
//GM_setValue对象,通过6位数代码取出
let STATIC = {
max: -99999.9,//近20个交易日最大值累计净值
maxDate: '',//最大值的日期
min: 99999.9,//近20个交易日最小值累计净值
minDate: '',//最小值的日期
yearAgo: 0.0,//一年前的累计净值
yearAgoDate: '',//累计净值的日期
};
//装净值信息
let list = [];
(function () {
addLoadEvent(() => {
//window.scrollTo(0,2150);
const url = window.location.href;
//主页面
const str = /fund\.eastmoney\.com\/(\d{6})\.html/i;
//净值页面
const str2 = /.+\.eastmoney\.com\/jjjz_(\d{6}).html/i;
if (str.test(url)) {//为主页面
//视图元素生成
let divElement1 = document.createElement('div');
divElement1.className = "gs1";
document.body.appendChild(divElement1);
divElement1.style = "top: calc( 15% - 42px );font-size: 16px;";
let divElement2 = document.createElement('div');
divElement2.className = "gs1";
document.body.appendChild(divElement2);
let divElement3 = document.createElement('div');
divElement3.className = "gs1";
document.body.appendChild(divElement3);
divElement3.style = "top: calc( 15% + 190px );";
//显示基金名称,名称面板
const 基金名称 = document.querySelector("#body > div:nth-child(8) > div > div > a:nth-child(7)").innerText;
divElement1.innerHTML = 基金名称;
//基本数据处理与抓取
const 任期回报 = (document.querySelector("tr.noBorder > td.td04.bold").innerText.replace("%", "") / 100).toFixed(4);
const 任职时间 = document.querySelector("tr.noBorder > td.td01").innerText.replace("~至今", "");
const 累计净值 = document.querySelector("dl.dataItem03 > dd.dataNums > span").innerText;
const 净值时间 = document.querySelector("dl.dataItem02 > dt > p").innerText.split('(')[1].replace(')', '');
const 基金代码 = window.location.pathname.match(/(\d{6})/i)[1];
let 平均回报率 = ((Math.pow(Number(任期回报) + 1, 1 / (getNumberOfDays(new Date(), 任职时间) / 365)) - 1) * 100).toFixed(2);
const 成立时间 = document.querySelector("div.infoOfFund > table > tbody > tr:nth-child(2) > td:nth-child(1)").innerText.replace("成 立 日:", "");
/**-------------------------------------设置项判断---------------------------------------- **/
//指数基金年化代替,平均回报计算为成立以来年化
if (setting.指数基金年化代替为成立以来年化) {
平均回报率 = ((Math.pow(累计净值, 1 / (getNumberOfDays(new Date(), 成立时间) / 365)) - 1) * 100).toFixed(2);
}
//指数成立时间不满5年则关闭
if (setting.指数用成立时间不满5年则关闭 && 成立时间 > setting.满五年日期常量) {
window.close();
return;
}
//不满五年判断
if (setting.自动关闭不满5年基金 && 任职时间 > setting.满五年日期常量) {
window.close();
return;
}
//回报率判断
if (setting.自动关闭平均回报过低基金 && 平均回报率 < setting.回报率常量) {
window.close();
return;
}
//打开新窗口,获取净值
if (setting.是否查看近一年净值) {
const 净值网址 = document.querySelector("#Div2 > div.item_title.hd > div.item_more > a").href;
打开子窗口(净值网址 + "?ref=" + 基金代码, 5000, true,10, () => {
//更新完毕后立即执行
主要信息渲染();
});
}
/**-----------------------------------设置项判断结束-------------------------------------- **/
function 主要信息渲染() {
//如果信息没有更新则不运行
if (JSON.stringify(STATIC) === JSON.stringify(GM_getValue(基金代码))) return;
//如果数据存在则更新
if (GM_getValue(基金代码) !== undefined) {
STATIC = GM_getValue(基金代码);
次要信息渲染(true);
}
const 近一年 = ((累计净值 / STATIC.yearAgo - 1) * 100).toFixed(2) + "%";
divElement2.innerHTML =
"近一年: " + 近一年 +
"
净值时间: " + 净值时间 +
"
累计净值: " + 累计净值 +
"
取值时间: " + STATIC.yearAgoDate +
"
累计净值: " + STATIC.yearAgo +
"
任职时间: " + 任职时间 +
"
任期回报: " + 任期回报 +
"
平均回报: " + 平均回报率 + "%";
}
let flag = 0.0;
function 次要信息渲染(f) {
let 当前估值 = parseFloat((document.querySelector("#gz_gszzl").innerText.replace("%", "") / 100).toFixed(4));
//若估值不变,且不由主要信息渲染调用则停止函数执行
if (flag === 当前估值 && f !== true){
return;
}
flag = 当前估值;
//---最大值
const 与月高点的差距 = 1 - 累计净值 * (1 + (当前估值)) / STATIC.max;
//---最小值
const 与月低点的差距 = 累计净值 * (1 + (当前估值)) / STATIC.min - 1;
divElement3.innerHTML =
"当前估值: " + (当前估值 * 100).toFixed(2) + "%" +
"
近一个月最大累计净值:" +
"
取值时间:" + STATIC.maxDate +
"
累计净值: " + STATIC.max +
"
与月高点的差距:" + (与月高点的差距 * 100).toFixed(2) + "%" +
"
近一个月最小累计净值:" +
"
取值时间:" + STATIC.minDate +
"
累计净值: " + STATIC.min +
"
与月低点的差距:" + (与月低点的差距 * 100).toFixed(2) + "%";
}
//持续监听变化
window.setInterval(() => {
主要信息渲染();
次要信息渲染();
}, 500);
//立即执行
主要信息渲染();
次要信息渲染();
} else if (str2.test(url) && getQueryString("ref") != null) {
//为净值页面且由本程序打开.
const 基金代码 = getQueryString("ref");
//找到近20个交易日累计净值的最大值
净值收集(() => {
list.forEach((value) => {
if (value["累计净值"] > STATIC.max) {
STATIC.max = value["累计净值"];
STATIC.maxDate = value["净值日期"];
}
if (value["累计净值"] < STATIC.min) {
STATIC.min = value["累计净值"];
STATIC.minDate = value["净值日期"];
}
});
//找一年前的累计净值,从12页开始找
document.querySelector("#pagebar > div.pagebtns > input.pnum").value = 12;
document.querySelector("#pagebar > div.pagebtns > input.pgo").click();
//清空数组内容
list.length = 0;
//一年前的准确时间
let date = new Date();
date.setFullYear((date.getFullYear() - 1));
净值收集(function 回调1() {
for (let i = 0; i < list.length; i++) {
if (new Date(list[i]["净值日期"]) < date) {
STATIC.yearAgo = list[i]["累计净值"];
STATIC.yearAgoDate = list[i]["净值日期"];
break;
}
}
//如果没有找到数据
if (STATIC.yearAgoDate.length === 0) {
//清空数组内容
list.length = 0;
//翻页
document.querySelector("#pagebar > div.pagebtns > label:nth-child(9)").click();
//重新收集数据
净值收集(回调1);
} else {
//取得数据,程序出口
//设置数据
GM_setValue(基金代码, STATIC);
//关闭网页
window.close();
}
});
});
}
});
addStyle(`
.gs1 {
padding: 5px 5px;
font-size: 14px;
color: snow;
position: fixed;
text-align: left;
cursor: copy;
border-radius: 10px;
background-color: rgba(135, 134, 241, 0.84);
right: 5px;
top: 15%;
z-index: 999999;
//box-shadow: 0 0 7px 0 rgba(18, 80, 18,0.4), 0 0 0 1px rgba(0,0,0,0.3);
min-width: 144px;
}`);
/**
* 净值数据收集
*/
function 净值收集(func) {
//处理数据,获取不到数据时等待100ms后继续获取.
let trList = document.querySelectorAll("#jztable > table > tbody > tr");
if (trList === undefined || trList.length !== 20 || trList.length === 0) {
setTimeout(() => 净值收集(func), 100);
return;
}
for (let i = 0; i < trList.length; i++) {
let 净值日期, 单位净值, 累计净值;
deleteRedundantEmptyTextNodes(trList[i]);
净值日期 = trList[i].childNodes[0].innerText;
单位净值 = trList[i].childNodes[1].innerText;
累计净值 = trList[i].childNodes[2].innerText;
let temp = {净值日期: '', 单位净值: 1.0, 累计净值: 1.0};
temp["净值日期"] = 净值日期;
temp["单位净值"] = parseFloat(单位净值);
temp["累计净值"] = parseFloat(累计净值);
//添加到数组
list.push(temp);
}
func();
}
/**-------------------------------------------其他函数---------------------------------------------**/
/**
* 添加浏览器执行事件
* @param func 无参匿名函数
*/
function addLoadEvent(func) {
let oldOnload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function () {
try {
oldOnload();
} catch (e) {
console.log(e);
} finally {
func();
}
}
}
}
/**
* 获取网页参数
* @param name 参数名称
* @returns {string|null}
*/
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = decodeURI(window.location.search).substr(1).match(reg);
if (r != null) return (r[2]);
return null;
}
//添加css样式
function addStyle(rules) {
let styleElement = document.createElement('style');
styleElement["type"] = 'text/css';
document.getElementsByTagName('head')[0].appendChild(styleElement);
styleElement.appendChild(document.createTextNode(rules));
}
/**
* 两个时间相减,返回天数
* @param date1 较大的日期
* @param date2 较小的日期
* @returns {number}
*/
function getNumberOfDays(date1, date2) { //获得天数
var a1 = Date.parse(new Date(date1));
var a2 = Date.parse(new Date(date2));
var day = parseInt((a1 - a2) / (1000 * 60 * 60 * 24)); //核心:时间戳相减,然后除以天数
return day
}
/**
* 新窗口打开子窗口,为隐藏模式
* @param url url
* @param timeout 超时时间(ms)
* @param reopen 超时是否自动重开
* @param reopenLimit 重开次数上限
* @param func 回调函数,窗口关闭后回调
*/
function 打开子窗口(url, timeout, reopen,reopenLimit, func) {
let son = window.open(url, '_blank', "height=100,width=100,top=" +
window.screen.height + ",left=" + window.screen.width +
",location=0,menubar=0,status=0,titlebar=0,toolbar=0");
let start = new Date().getTime();//子窗口打开时的时间
let count=0;
//检测超时
const winTimer = window.setInterval(() => {
let end;
end = new Date().getTime();//现在的时间
if (end - start > timeout) {//超时
son.close();//关闭窗口
if (reopen&&count