📲 微信通知直连(支持 PushPlus / Server酱 / 企业微信机器人)
配置后,无论是网页端开着时还是电脑关机由云端哨兵值班时,一旦触发9:15–15:00 盘中急涨急跌/指标金叉,或到达每天下午 15:30 收盘复盘时刻,都会直接把消息和完整报表推送到你的微信上!
📓 云端 Gist 跨设备自动同步小账本(自选股永不丢失)
绑定免费的 GitHub Gist 后,你在手机上新加或删除一只自选股,网页会自动将你的自选股列表、收藏与微信推送配置同步到云端 Gist。周末回家打开电脑或换平板打开网页时,自选股列表自动同步完毕,连复制粘贴都不需要!同时云端 24h 哨兵也会自动读取这份 Gist 账本替你盯盘。
🛡️ 云端 24 小时盯盘哨兵(电脑完全关机、手机锁屏也能收微信预警 + 15:30日报)
下方代码已自动嵌入你当前的自选股列表 (2 只)、微信推送通道与 Gist 账本 ID,并同时包含:
- 交易日 9:15–15:00 盘中哨兵:每 5 分钟巡检自选股与科技ETF,触发急涨急跌、金叉或做T低吸高抛位时直推微信;
- 交易日 15:30 收盘复盘报表:每天下午 3:30 准时汇总大盘指数、两市成交量、市场情绪温度、科技龙头股与情绪标的推送到你微信!
// ============================================================================
// Cloudflare Workers 零成本 24h 云端盯盘哨兵 + 每日 15:30 微信收盘复盘报表
// 使用方法:
// 1. 登录 dash.cloudflare.com -> Workers & Pages -> Create Worker,粘贴本代码部署;
// 2. 在 Worker -> Settings -> Triggers -> Cron Triggers 添加两条定时规则:
// - 盘中盯盘 (北京时间 9:15-15:00 每5分钟): */5 1-7 * * 1-5
// - 15:30 收盘日报 (北京时间每天 15:30): 30 7 * * 1-5
// ============================================================================
const CONFIG = {
WECHAT_CHANNEL: "pushplus", // "pushplus" | "serverchan" | "wecom"
WECHAT_TOKEN: "280abf2a735c4bada962741fcf6dfea2",
GIST_ID: "e59597946a433fb9db21275e11c7ff69", // 若已绑定Gist跨端账本,云端会自动读取你在手机新加的自选股
DEFAULT_CODES: ["sh000001","sz399006","sh000688","sh588200","sh512480","sz159995","sh515880","sh515050","sz159994","sh588000","sz159819","sh512720","sh600601","sh603328"],
SURGE_PCT: 2,
PLUNGE_PCT: -2
};
export default {
async scheduled(event, env, ctx) {
ctx.waitUntil(runCloudSentinel(false));
},
async fetch(request, env, ctx) {
const url = new URL(request.url);
const forceReport = url.searchParams.get("report") === "1";
const result = await runCloudSentinel(forceReport);
return new Response(JSON.stringify(result, null, 2), {
headers: { "Content-Type": "application/json;charset=utf-8" }
});
}
};
async function getWatchCodes() {
const set = new Set(CONFIG.DEFAULT_CODES);
if (CONFIG.GIST_ID) {
try {
const r = await fetch(`https://api.github.com/gists/${CONFIG.GIST_ID}`, {
headers: { "User-Agent": "Cloudflare-Worker-Sentinel" }
});
const j = await r.json();
const f = j.files && (j.files["ashare_watchlist_sync.json"] || Object.values(j.files)[0]);
if (f && f.content) {
const parsed = JSON.parse(f.content);
(parsed.customItems || []).forEach(it => it && it.code && set.add(it.code));
}
} catch (e) {}
}
return Array.from(set);
}
async function fetchTencentQuotes(codes) {
const resp = await fetch(`https://qt.gtimg.cn/q=${codes.join(",")}`);
const buf = await resp.arrayBuffer();
const text = new TextDecoder("gbk").decode(buf);
const items = [];
for (const line of text.split(";")) {
const m = line.match(/v_(\w+)="([^"]+)"/);
if (!m) continue;
const code = m[1], f = m[2].split("~");
if (f.length < 45) continue;
const price = parseFloat(f[3]) || 0;
const prev = parseFloat(f[4]) || 0;
const high = parseFloat(f[33]) || price;
const low = parseFloat(f[34]) || price;
const changePct = parseFloat(f[32]) || 0;
const amtYi = (parseFloat(f[37]) || 0) / 10000;
const turn = parseFloat(f[38]) || 0;
const vr = parseFloat(f[49]) || 1.0;
const outer = parseFloat(f[7]) || 0, inner = parseFloat(f[8]) || 0;
const buyPct = (outer + inner) > 0 ? (outer / (outer + inner)) * 100 : 50;
const vwap = parseFloat(f[51]) || ((high + low + price * 2) / 4);
items.push({ code, name: f[1], symbol: f[2], price, prev, high, low, vwap, changePct, amtYi, turn, vr, buyPct });
}
return items;
}
async function pushToWechat(title, markdown) {
if (!CONFIG.WECHAT_TOKEN) return;
if (CONFIG.WECHAT_CHANNEL === "pushplus") {
await fetch("https://www.pushplus.plus/send", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token: CONFIG.WECHAT_TOKEN, title, content: markdown, template: "markdown" })
});
} else if (CONFIG.WECHAT_CHANNEL === "serverchan") {
const body = new URLSearchParams({ title, desp: markdown });
await fetch(`https://sctapi.ftqq.com/${CONFIG.WECHAT_TOKEN}.send`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: body.toString()
});
} else {
await fetch(CONFIG.WECHAT_TOKEN, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ msgtype: "markdown", markdown: { content: `### ${title}\n${markdown}` } })
});
}
}
async function runCloudSentinel(forceReport) {
const now = new Date(Date.now() + 8 * 3600 * 1000); // 北京时间
const hm = now.getUTCHours() * 100 + now.getUTCMinutes();
const dateStr = now.toISOString().slice(0, 10);
const codes = await getWatchCodes();
const list = await fetchTencentQuotes(codes);
const indices = list.filter(x => ["sh000001", "sz399006", "sh000688"].includes(x.code));
const trades = list.filter(x => !["sh000001", "sz399006", "sh000688"].includes(x.code));
// 1. 若到达 15:25-15:40 或手动触发 report=1,生成并推送 15:30 股市整体与科技股收盘日报
if (forceReport || (hm >= 1525 && hm <= 1540)) {
const sh = list.find(x => x.code === "sh000001") || { amtYi: 5500 };
const cyb = list.find(x => x.code === "sz399006") || { amtYi: 4000 };
const estTrillion = (sh.amtYi * 2.35 + cyb.amtYi * 0.45) / 10000;
const upCount = trades.filter(x => x.changePct > 0).length;
const winRate = trades.length ? Math.round((upCount / trades.length) * 100) : 50;
const leaders = trades.slice().sort((a, b) => b.changePct - a.changePct).slice(0, 4);
const hotSent = trades.slice().sort((a, b) => (b.turn + b.vr * 3) - (a.turn + a.vr * 3)).slice(0, 3);
const lines = [
`### 一、今日A股整体情况(指数 · 成交量 · 市场情绪)`,
...indices.map(i => `- **${i.name} (${i.symbol})**:**${i.price.toFixed(2)}** (**${i.changePct >= 0 ? '+' : ''}${i.changePct.toFixed(2)}%**) | 成交额 ${i.amtYi.toFixed(0)}亿`),
`- **两市估算总成交量**:约 **${estTrillion.toFixed(2)} 万亿元**`,
`- **市场情绪与赚钱效应**:监控池红盘率 **${winRate}%**(${upCount}涨 / ${trades.length - upCount}跌)`,
``,
`### 二、科技股方向情况(龙头股 · 情绪标的)`,
`**1. 科技领涨龙头股/ETF:**`,
...leaders.map(r => `- **${r.name} (${r.symbol})**:现价 **${r.price}** (**${r.changePct >= 0 ? '+' : ''}${r.changePct.toFixed(2)}%**) | 量比 ${r.vr.toFixed(2)}x | 外盘主动买盘 ${r.buyPct.toFixed(0)}%`),
`**2. 高换手情绪核心标的:**`,
...hotSent.map(r => `- **${r.name} (${r.symbol})**:换手率 **${r.turn.toFixed(2)}%** | 涨跌 **${r.changePct >= 0 ? '+' : ''}${r.changePct.toFixed(2)}%** | 分时均价 ${r.vwap.toFixed(3)}`)
];
const title = `【15:30收盘日报】${dateStr} A股全局与科技股龙头复盘`;
await pushToWechat(title, lines.join("\n"));
return { mode: "15:30_DAILY_REPORT", pushed: true, count: list.length };
}
// 2. 交易日 9:15-15:00 盘中异动巡检
const triggered = trades.filter(r => r.changePct >= CONFIG.SURGE_PCT || r.changePct <= CONFIG.PLUNGE_PCT || (r.vr >= 1.8 && r.buyPct >= 60));
if (triggered.length > 0 && hm >= 915 && hm <= 1505) {
const title = `【云哨兵盘中异动】${triggered[0].name} 等 ${triggered.length} 只标的触发预警`;
const md = triggered.map(r => `- **${r.name} (${r.symbol})**:现价 **${r.price}** (**${r.changePct >= 0 ? '+' : ''}${r.changePct.toFixed(2)}%**) | 均价 ${r.vwap.toFixed(3)} | 量比 ${r.vr.toFixed(2)}x | 外盘 ${r.buyPct.toFixed(0)}%`).join("\n");
await pushToWechat(title, md);
}
return { mode: "INTRADAY_SENTINEL", triggeredCount: triggered.length };
}
🌐 纯静态网页云端托管(生成专属固定网址,收藏在微信随时秒开)
点击下方按钮可一键导出已预置好你当前自选股、微信推送 Token 与 Gist 账本配置的独立单文件网页 index.html。把它拖入免费静态托管平台即可获得永久固定网址:
1. Cloudflare Pages
打开 dash.cloudflare.com → Workers & Pages → Create → Upload assets,拖入导出的 index.html,10秒生成国内直连网址。
2. GitHub Pages
把导出的 index.html 上传到 GitHub 仓库根目录,在 Settings → Pages 开启分支托管,即可通过固定网址访问。
3. Vercel / Netlify
直接拖拽文件夹上传,自动绑定 HTTPS 域名,在微信里点开收藏夹即可随时随地盯盘。