您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
实时监控建行积存金价格,使用卡尔曼滤波预测价格,价格变化时输出日志和通知,保持会话活跃。
// ==UserScript== // @name 建行积存金价格持续监控(卡尔曼滤波预测版) // @namespace http://tampermonkey.net/ // @version 1.4.0 // @description 实时监控建行积存金价格,使用卡尔曼滤波预测价格,价格变化时输出日志和通知,保持会话活跃。 // @match https://ibsbjstar.ccb.com.cn/CCBIS/B2CMainPlat_07* // @grant GM_notification // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== (function () { 'use strict'; // 防止脚本重复执行 if (window.scriptHasRun) return; window.scriptHasRun = true; console.log("script start running"); // 配置参数 const SERVERCHAN_KEY = 'SCT170695TVgILvV5PXeNF7akHCyhT8V7d'; const ALERT_LOW = 750.00; const ALERT_HIGH = 770.00; const ALERT_HIGH_2 = 800.00; const ALERT_LOW_2 = 798.00; let lastPrice = null; let kalmanFilter = null; let hasNotifiedLow = false; // 是否已发送价格跌破760的通知 let hasNotifiedHigh = false; // 是否已发送价格涨过800的通知 let previousPrice = null; // 用于记录上次价格,确保价格变动才通知 // 卡尔曼滤波器类(自适应 Q 和 R) class AdaptiveKalmanFilter { /** * @param {number} initialPrice 初始状态估计 * @param {number} processNoise 初始过程噪声 Q * @param {number} measurementNoise 初始测量噪声 R * @param {number} alphaQ 过程噪声更新遗忘因子(0<alphaQ<1) * @param {number} alphaR 测量噪声更新遗忘因子(0<alphaR<1) */ constructor(initialPrice, processNoise = 0.01, measurementNoise = 0.1, alphaQ = 0.95, alphaR = 0.90) { this.estimate = initialPrice; // x̂ₖ|ₖ this.P = 1; // 估计协方差 Pₖ|ₖ this.Q = processNoise; // 过程噪声 Q this.R = measurementNoise; // 测量噪声 R this.alphaQ = alphaQ; // Q 更新遗忘因子 this.alphaR = alphaR; // R 更新遗忘因子 this.prevEstimate = initialPrice; // 上一次滤波后的状态 } // 预测步骤 predict() { // x̂ₖ|ₖ₋₁ = x̂ₖ₋₁|ₖ₋₁ // Pₖ|ₖ₋₁ = Pₖ₋₁|ₖ₋₁ + Q this.P += this.Q; return this.estimate; } // 更新步骤,并自适应更新 Q、R update(measurement) { // 计算卡尔曼增益 const K = this.P / (this.P + this.R); // 预测值与测量值之差(创新) const residual = measurement - this.estimate; // 状态更新:x̂ₖ|ₖ = x̂ₖ|ₖ₋₁ + K * residual this.estimate += K * residual; // 协方差更新:Pₖ|ₖ = (1 - K) * Pₖ|ₖ₋₁ this.P *= (1 - K); // -------- 自适应噪声更新 -------- // 1. 更新测量噪声 R:基于创新的平方 // R_new = alphaR * R_old + (1 - alphaR) * residual^2 this.R = this.alphaR * this.R + (1 - this.alphaR) * residual * residual; // 2. 更新过程噪声 Q:基于状态变化量 // stateDiff = x̂ₖ|ₖ - x̂ₖ₋₁|ₖ₋₁ const stateDiff = this.estimate - this.prevEstimate; // Q_new = alphaQ * Q_old + (1 - alphaQ) * stateDiff^2 this.Q = this.alphaQ * this.Q + (1 - this.alphaQ) * stateDiff * stateDiff; // 保存本次估计,以用于下一次 Q 计算 this.prevEstimate = this.estimate; return this.estimate; } } // 获取价格的函数,增加错误处理 function getPrice() { try { const priceRow = document.getElementById('realTimePriceTr'); if (!priceRow) return null; const priceTds = priceRow.querySelectorAll('td'); if (priceTds.length < 2) return null; const price = parseFloat(priceTds[1].textContent.match(/\d+\.\d+/)[0]); const refreshBtn = document.querySelector('img[onclick="refresh();"]'); if (refreshBtn) { refreshBtn.click(); } return isNaN(price) ? null : price; } catch (e) { console.error('获取价格失败:', e); return null; } } // 模拟用户操作函数 function simulateMouseActivity() { const randomX = Math.random() * window.innerWidth; const randomY = Math.random() * window.innerHeight; ['mousemove', 'mousedown', 'mouseup'].forEach(eventType => { document.dispatchEvent(new MouseEvent(eventType, { bubbles: true, clientX: randomX, clientY: randomY })); }); } function simulateKeyboardActivity() { ['keydown', 'keyup'].forEach(eventType => { document.dispatchEvent(new KeyboardEvent(eventType, { key: 'Shift', bubbles: true })); }); } // 价格监控与预测逻辑 const priceCheckInterval = setInterval(() => { const currentPrice = getPrice(); if (currentPrice === null) return; // 初始化卡尔曼滤波器 if (!kalmanFilter) { kalmanFilter = new AdaptiveKalmanFilter(currentPrice, /*processNoise=*/0.01, /*measurementNoise=*/0.1, /*alphaQ=*/0.95, /*alphaR=*/0.90); console.log('卡尔曼滤波器已初始化,初始价格:', currentPrice); return; } // 执行预测并更新 const predictedPrice = kalmanFilter.predict().toFixed(2); const updatedPrice = kalmanFilter.update(currentPrice).toFixed(2); if (currentPrice !== lastPrice) { console.log(`当前价格:${currentPrice.toFixed(2)} | 预测下次:${predictedPrice}`); lastPrice = currentPrice; // 价格跌破770并涨回772后再次跌破770通知 if (currentPrice < ALERT_HIGH && currentPrice >= ALERT_LOW && !hasNotifiedLow) { GM_xmlhttpRequest({ method: 'POST', url: 'https://www.zakiu.site/send-alert', data: JSON.stringify({ price: currentPrice.toFixed(2) }), headers: { 'Content-Type': 'application/json' }, onload: res => console.log(res.status === 200 ? '推送成功' : '推送失败'), onerror: () => console.error('推送失败,网络问题') }); GM_notification({ title: '价格跌破770', text: `当前价格:${currentPrice.toFixed(2)}(预测下次:${predictedPrice})`, timeout: 5000 }); hasNotifiedLow = true; } else if (currentPrice >= 772) { hasNotifiedLow = false; } // 价格涨过800并跌回798后再次涨过800通知 if (currentPrice > ALERT_HIGH_2 && previousPrice <= ALERT_HIGH_2 && !hasNotifiedHigh) { GM_xmlhttpRequest({ method: 'POST', url: 'https://www.zakiu.site/send-alert', data: JSON.stringify({ price: currentPrice.toFixed(2) }), headers: { 'Content-Type': 'application/json' }, onload: res => console.log(res.status === 200 ? '推送成功' : '推送失败'), onerror: () => console.error('推送失败,网络问题') }); GM_notification({ title: '价格涨过800', text: `当前价格:${currentPrice.toFixed(2)}(预测下次:${predictedPrice})`, timeout: 5000 }); hasNotifiedHigh = true; } else if (currentPrice < ALERT_LOW_2) { hasNotifiedHigh = false; } previousPrice = currentPrice; } }, 1000); // 保活操作 const keepAliveInterval = setInterval(() => { simulateMouseActivity(); simulateKeyboardActivity(); }, 10000); // 错误捕捉机制 window.addEventListener('error', function (e) { console.error('脚本发生错误:', e.message); // 可以选择清理定时器或重启脚本的逻辑 }); // 页面元素变化时,检查价格获取函数是否有效 const priceCheckMutationObserver = new MutationObserver(() => { const price = getPrice(); if (price !== null && price !== lastPrice) { clearInterval(priceCheckInterval); lastPrice = price; priceCheckInterval = setInterval(() => { /* 更新定时器的逻辑 */ }, 1000); } }); // 观察价格表格的变化 const priceTable = document.getElementById('realTimePriceTr'); if (priceTable) { priceCheckMutationObserver.observe(priceTable, { childList: true, subtree: true }); } })();