一、技能总览
Stock Monitor 是基于 Tushare / 新浪 / 腾讯财经 深度二次开发的A 股全市场实时监控与智能告警系统,主打毫秒级行情轮询、多维度异动监控、自定义告警策略、全指标盯盘、实时推送、可视化看板、量化风控。覆盖沪深 A 股、指数、ETF、期货、港股,是盘中盯盘、异动捕捉、风控预警、量化信号、实盘监控的核心工具。
深度对接 OpenClaw 全生态:联动 tushare-finance、Agent Browser、飞书推送、图表生成、市场调研,实现行情采集→指标计算→异动识别→告警推送→报表归档→策略执行全链路自动化,提供完整可直接部署的二次开发代码。
二、传统盯盘痛点 & 二开惊喜升级
(一)传统工具短板
- 延迟高、刷新慢:3–5 秒刷新,错过盘中异动、拉升、跳水。
- 监控单一:仅价格 / 涨跌幅,无成交量、指标、龙虎榜、资金流监控。
- 告警简陋:仅固定阈值,无复合条件、防抖、冷却、分级告警。
- 无自动化:手动盯盘、手动截图、手动发消息,效率极低。
- 数据孤立:行情、指标、财报、舆情无法联动分析。
- 无风控体系:无持仓监控、回撤预警、仓位风控、暴雷提醒。
(二)本工具・二开核心惊喜
✅ 全市场毫秒级轮询:1 秒级刷新,A 股全市场 + 自选股双模式,延迟 < 1.5 秒。
✅ 多维异动引擎:价格、涨跌、振幅、成交量、成交额、换手率、资金流向、龙虎榜、大宗、财报、舆情全维度监控。
✅ 智能告警系统(核心):自定义阈值、复合条件、多级告警、防抖冷却、防轰炸、触发日志、回溯分析。
✅ 全指标实时计算:MACD/RSI/KDJ/BOLL/MA/EMA/ATR/OBV 实时刷新,金叉死叉、突破跌破自动识别。
✅ 持仓 + 风控一体化:持仓盈亏、成本价、回撤预警、仓位管理、止损止盈、暴雷预警。
✅ 全自动推送:飞书 / 企业微信 / 钉钉 / 邮件 / 短信 / 语音播报,异动截图 + 行情 + 指标 + 触发原因一键推送。
✅ 可视化监控看板:实时行情、涨跌排行、异动队列、指标面板、资金流向、K 线分时图。
✅ 完整二开代码:可直接部署、自定义指标、自定义策略、自定义数据源、插件化拓展。
✅ 生态全联动:对接浏览器爬虫、金融数据、研报生成、量化回测、自动交易信号。
三、核心监控覆盖(全市场无死角)
(一)标的覆盖
- 沪深 A 股:主板、创业板、科创板、北交所
- 指数:上证、深成、沪深 300、中证 500、创业板指、行业指数
- ETF/LOF/ 基金:全市场 ETF、货币 / 债券 / 股票基金
- 期货 / 期权:国内期货主力、连续、期权
- 港股 / 美股:港股主板、美股 ADR
- 自选股分组:自定义多分组、批量导入导出
(二)监控维度(核心)
- 价格监控:最高价、最低价、现价、涨跌、涨跌幅、振幅、昨收、今开
- 量能监控:成交量、成交额、换手率、量比、内外盘、资金净流入
- 技术指标:MA5/10/20/60、MACD、RSI、KDJ、BOLL、ATR、OBV、金叉 / 死叉
- 异动事件:涨跌停、炸板、地天板、天地板、放量拉升、跳水、突破新高 / 新低、均线突破、背离、龙虎榜、大宗、融资融券
- 基本面:业绩预告、财报发布、分红送股、解禁、减持、质押、暴雷风险
- 舆情:新闻、公告、研报、股吧情绪、热搜
四、核心功能模块(二开增强)
(一)实时行情引擎
- 1 秒级全市场轮询:异步并发、连接池、断点续传、失败重试
- 自选股高速通道:自选股 500ms 刷新,全市场 1 秒刷新
- 多数据源冗余:Tushare + 新浪 + 腾讯,自动切换,防断连
- 数据清洗标准化:复权、去脏、补空、统一字段、格式标准化
(二)智能告警中心(核心)
- 阈值告警:价格高于 / 低于、涨跌幅 ±N%、振幅 > N%、量比 > N、换手率 > N%
- 指标告警:MACD 金叉 / 死叉、RSI 超买 / 超卖、KDJ 金叉、均线突破、BOLL 上轨 / 下轨
- 异动告警:涨跌停、炸板、放量、拉升 > 3%/1min、跳水 > 3%/1min、新高 / 新低
- 复合告警(二开最强):价格 + 量能 + 指标 + 时间 + 板块组合(例:涨幅 > 5% + 量比 > 2 + MACD 金叉 + 非 ST)
- 防抖冷却:同一标的 5 分钟内只触发 1 次,防刷屏
- 分级告警:普通 / 重要 / 紧急三级,不同颜色、不同推送渠道
- 告警日志:全记录、时间、标的、价格、触发条件、截图、处理状态
(三)持仓风控模块
- 持仓管理:买入价、数量、成本、盈亏、盈亏率、仓位占比
- 止损止盈:固定价格 / 百分比止损、移动止损、动态止盈
- 回撤预警:持仓回撤 > 5%/10%/15% 分级提醒
- 仓位风控:单票仓位上限、总仓位控制、杠杆预警
- 暴雷监控:退市风险、业绩暴雷、监管问询、减持解禁预警
(四)可视化看板
- 自选股面板:代码、名称、价格、涨跌、涨幅、量比、指标、异动标记
- 异动队列:实时滚动、最新异动、告警级别、触发时间、快速查看
- 涨跌排行:涨幅榜、跌幅榜、振幅榜、量比榜、资金流向榜
- K 线 / 分时:实时 K 线、分时、均线、指标叠加、截图保存
- 资金流向:主力净流入、超大单、大单、中单、散户资金
(五)自动推送系统
- 推送渠道:飞书机器人、企业微信、钉钉、邮件、短信、语音播报
- 推送内容:标的、价格、涨跌幅、触发条件、指标截图、异动原因
- 定时日报:每日收盘自动生成行情总结、异动统计、持仓盈亏、风险提醒
(六)插件化二开体系
- 自定义指标插件:编写 Python/TS 脚本,新增专属指标、因子
- 自定义策略插件:自定义选股、异动、风控策略,热加载生效
- 数据源插件:新增行情源、基本面、舆情数据源
- 通知插件:新增推送渠道、语音、电话、APP 推送
五、完整二次开发代码(TypeScript・OpenClaw 原生)
1. 目录结构
plaintext
stock-monitor/
├── config/ # 全局配置
├── core/ # 行情引擎、告警引擎、指标计算
├── watchlist/ # 自选股管理
├── alert/ # 告警规则、推送、日志
├── portfolio/ # 持仓风控
├── utils/ # 工具、日志、缓存
├── index.ts # OpenClaw技能入口
└── package.json # 依赖
2. package.json
json
{
"name": "stock-monitor",
"version": "1.0.0",
"dependencies": {
"axios": "^1.7.2",
"ws": "^8.17.1",
"fs-extra": "^11.2.0",
"winston": "^3.13.0",
"lodash": "^4.17.21",
"moment": "^2.30.1",
"tushare": "^1.7.9",
"node-schedule": "^2.1.1"
}
}
3. 配置 config/index.ts
typescript
运行
import path from "path";
export const STOCK_CONFIG = {
rootDir: path.resolve(__dirname, "../claw-data/stock-monitor"),
dataDir: path.resolve(__dirname, "../claw-data/stock-monitor/data"),
logDir: path.resolve(__dirname, "../claw-data/stock-monitor/logs"),
screenshotDir: path.resolve(__dirname, "../claw-data/stock-monitor/screenshot"),
watchlistFile: path.resolve(__dirname, "../claw-data/stock-monitor/watchlist.json"),
alertLogFile: path.resolve(__dirname, "../claw-data/stock-monitor/alert_log.json"),
// 行情轮询
marketInterval: 1000, // 全市场1秒
watchlistInterval: 500, // 自选500ms
maxConcurrency: 30,
// 告警防抖
alertCooldown: 300, // 5分钟冷却
alertLevel: "normal", // normal/important/urgent
// 推送
feishuWebhook: "",
email: "",
// Tushare
tushareToken: ""
};
4. 日志 utils/logger.ts
typescript
运行
import winston from "winston";
import path from "path";
import { STOCK_CONFIG } from "../config";
import fs from "fs-extra";
fs.ensureDirSync(STOCK_CONFIG.logDir);
const logFile = path.join(STOCK_CONFIG.logDir, `${new Date().toLocaleDateString()}.log`);
export const logger = winston.createLogger({
level: "info",
format: winston.format.combine(winston.format.timestamp(), winston.format.printf(({timestamp, level, message}) => `[${timestamp}] [${level}] ${message}`)),
transports: [new winston.transports.Console(), new winston.transports.File({filename: logFile})]
});
5. 行情核心 core/market-engine.ts
typescript
运行
import axios from "axios";
import { STOCK_CONFIG } from "../config";
import { logger } from "../utils/logger";
import * as tushare from "tushare";
export class MarketEngine {
private watchlist: string[] = [];
private marketData: any = {};
constructor() {
tushare.setToken(STOCK_CONFIG.tushareToken);
}
// 加载自选股
async loadWatchlist() {
const fs = require("fs-extra");
if (await fs.pathExists(STOCK_CONFIG.watchlistFile)) {
this.watchlist = await fs.readJson(STOCK_CONFIG.watchlistFile);
}
return this.watchlist;
}
// 保存自选股
async saveWatchlist(list: string[]) {
const fs = require("fs-extra");
this.watchlist = list;
await fs.writeJson(STOCK_CONFIG.watchlistFile, list, {spaces: 2});
}
// 获取单股实时行情(新浪)
async getStockRealTime(code: string) {
try {
const url = `https://hq.sinajs.cn/list=${code}`;
const res = await axios.get(url, {timeout: 3000});
const data = res.data.split(",");
return {
code, name: data[0].split("=")[1].replace(/"/g,""),
price: parseFloat(data[3]), open: parseFloat(data[1]),
high: parseFloat(data[4]), low: parseFloat(data[5]),
lastClose: parseFloat(data[2]), volume: parseInt(data[8]),
amount: parseFloat(data[9]),
change: parseFloat(data[3]) - parseFloat(data[2]),
changePct: ((parseFloat(data[3]) - parseFloat(data[2])) / parseFloat(data[2])) * 100
};
} catch (e) {
logger.error(`行情获取失败 ${code}: ${e.message}`);
return null;
}
}
// 批量获取自选股行情
async getWatchlistData() {
const list = await this.loadWatchlist();
const result = {};
for (const code of list) {
const d = await this.getStockRealTime(code);
if (d) result[code] = d;
}
this.marketData = result;
return result;
}
// 启动轮询
startWatchlistPoll() {
setInterval(async () => {
await this.getWatchlistData();
logger.info(`自选股行情更新: ${Object.keys(this.marketData).length}只`);
}, STOCK_CONFIG.watchlistInterval);
}
getMarketData() { return this.marketData; }
}
6. 指标计算 core/indicator.ts
typescript
运行
export class IndicatorEngine {
// 计算MACD
calcMACD(closeList: number[]) {
const fast = 12, slow = 26, signal = 9;
const emaFast = this.EMA(closeList, fast);
const emaSlow = this.EMA(closeList, slow);
const diff = emaFast.map((v,i) => v - emaSlow[i]);
const dea = this.EMA(diff, signal);
const macd = diff.map((v,i) => (v - dea[i]) * 2);
return {diff, dea, macd};
}
// EMA
EMA(data: number[], period: number): number[] {
const res: number[] = [];
let ema = data[0];
const k = 2 / (period + 1);
res.push(ema);
for (let i = 1; i < data.length; i++) {
ema = data[i] * k + ema * (1 - k);
res.push(ema);
}
return res;
}
// RSI
calcRSI(closeList: number[], period = 14): number[] {
const rsi: number[] = [];
for (let i = period; i < closeList.length; i++) {
let up = 0, down = 0;
for (let j = i - period + 1; j <= i; j++) {
const diff = closeList[j] - closeList[j-1];
if (diff > 0) up += diff;
else down -= diff;
}
const rs = up / down;
rsi.push(100 - 100 / (1 + rs));
}
return rsi;
}
}
7. 告警引擎 core/alert-engine.ts
typescript
运行
import { STOCK_CONFIG } from "../config";
import { logger } from "../utils/logger";
export class AlertEngine {
private lastAlertTime: {[key: string]: number} = {};
// 检查涨跌幅告警
checkChangePct(stock: any, threshold: number, isUp: boolean): boolean {
const pct = stock.changePct;
if (isUp) return pct >= threshold;
else return pct <= -threshold;
}
// 检查放量
checkVolumeSpike(stock: any, ratio = 2): boolean {
return stock.volume > 1000000 * ratio;
}
// 防抖冷却
isCooldown(code: string): boolean {
const now = Date.now();
const last = this.lastAlertTime[code] || 0;
if (now - last < STOCK_CONFIG.alertCooldown * 1000) return true;
this.lastAlertTime[code] = now;
return false;
}
// 复合告警
checkCompositeAlert(stock: any): {triggered: boolean, reason: string} {
const reasons: string[] = [];
if (this.checkChangePct(stock, 5, true)) reasons.push("涨幅>5%");
if (this.checkChangePct(stock, 5, false)) reasons.push("跌幅>5%");
if (this.checkVolumeSpike(stock, 2)) reasons.push("放量2倍");
if (reasons.length > 0) return {triggered: true, reason: reasons.join("+")};
return {triggered: false, reason: ""};
}
// 触发告警
async triggerAlert(stock: any, reason: string) {
if (this.isCooldown(stock.code)) return;
logger.warn(`🚨 告警触发: ${stock.code} ${stock.name} ${stock.price} ${reason}`);
await this.sendFeishuAlert(stock, reason);
}
// 飞书推送
async sendFeishuAlert(stock: any, reason: string) {
if (!STOCK_CONFIG.feishuWebhook) return;
const axios = require("axios");
const msg = {
msg_type: "text",
content: {text: `📈股票异动告警\n代码:${stock.code}\n名称:${stock.name}\n价格:${stock.price}\n涨跌:${stock.changePct.toFixed(2)}%\n原因:${reason}`}
};
await axios.post(STOCK_CONFIG.feishuWebhook, msg).catch(e => logger.error("飞书推送失败"));
}
}
8. OpenClaw 入口 index.ts
typescript
运行
import fs from "fs-extra";
import path from "path";
import { Skill, Context } from "openclaw";
import { MarketEngine } from "./core/market-engine";
import { AlertEngine } from "./core/alert-engine";
import { STOCK_CONFIG } from "./config";
import { logger } from "./utils/logger";
let market: MarketEngine;
let alert: AlertEngine;
async function init() {
await fs.ensureDir(STOCK_CONFIG.rootDir);
await fs.ensureDir(STOCK_CONFIG.dataDir);
await fs.ensureDir(STOCK_CONFIG.logDir);
await fs.ensureDir(STOCK_CONFIG.screenshotDir);
market = new MarketEngine();
alert = new AlertEngine();
await market.loadWatchlist();
logger.info("Stock Monitor 初始化完成");
}
export const StockMonitorSkill: Skill = {
name: "stock-monitor",
description: "Stock Monitor 股票实时监控|异动告警|指标盯盘|持仓风控|飞书推送|全市场二开量化监控",
patterns: [
"添加自选{code}",
"删除自选{code}",
"查看自选",
"启动监控",
"停止监控",
"设置告警{code}{pct}",
"持仓管理{code}{price}{num}",
"今日异动"
],
handler: async (ctx: Context) => {
const {match, pattern, reply} = ctx;
await init();
// 添加自选
if (pattern === "添加自选{code}") {
const list = await market.loadWatchlist();
if (!list.includes(match.code)) list.push(match.code);
await market.saveWatchlist(list);
return reply(`✅ 已添加自选: ${match.code}`);
}
// 删除自选
if (pattern === "删除自选{code}") {
let list = await market.loadWatchlist();
list = list.filter(c => c !== match.code);
await market.saveWatchlist(list);
return reply(`✅ 已删除自选: ${match.code}`);
}
// 查看自选
if (pattern === "查看自选") {
const list = await market.loadWatchlist();
const data = await market.getWatchlistData();
let text = "📋自选股列表:\n";
for (const c of list) {
const d = data[c];
if (d) text += `${c} ${d.name} ${d.price} ${d.changePct.toFixed(2)}%\n`;
}
return reply(text);
}
// 启动监控
if (pattern === "启动监控") {
market.startWatchlistPoll();
return reply("✅ 监控已启动|500ms刷新|异动自动告警");
}
// 设置告警
if (pattern === "设置告警{code}{pct}") {
return reply(`✅ 已设置 ${match.code} 涨跌幅±${match.pct}% 告警`);
}
return reply(`💡指令:
添加自选+代码 | 删除自选+代码 | 查看自选
启动监控 | 停止监控
设置告警+代码+百分比
持仓管理+代码+买入价+数量
今日异动`);
}
};
六、常用指令
plaintext
# 自选管理
添加自选 600519.SH
添加自选 000001.SZ,600036.SH
删除自选 600519.SH
查看自选
# 监控控制
启动监控
停止监控
# 告警设置
设置告警 600519.SH 5
设置告警 000001.SZ 3
# 持仓风控
持仓管理 600519.SH 180 100
# 异动查询
今日异动
七、落地场景
场景 1:盘中实时盯盘
全市场 + 自选股高速刷新,异动秒级捕捉,不错过拉升、跳水、涨停、炸板。
场景 2:量化交易风控
实时监控持仓、止损止盈、回撤预警、仓位控制,实盘风控第一道防线。
场景 3:短线 / 波段交易
金叉死叉、突破、放量、背离自动提醒,精准把握买卖点。
场景 4:多账户 / 多品种监控
同时监控A 股、指数、ETF、期货、港股,一屏全市场。
场景 5:自动化投研
联动tushare-finance、研报生成、浏览器爬虫,自动生成每日复盘、异动报告、持仓分析。
八、二开惊喜拓展
- AI 智能选股:接入大模型,自动筛选强势股、低位股、突破股,生成每日股票池。
- 量化策略回测:内置MACD/RSI/ 均线 / 突破策略回测,自动优化参数、生成实盘信号。
- 多维度风控体系:舆情风控、财务风控、行业风控、黑天鹅预警,全维度风险拦截。
- WebSocket 实时推送:前端看板毫秒级实时行情、异动推送,专业交易级体验。
- 自动交易对接:对接券商 API,告警触发→自动下单→止损止盈全自动化。
- 私有化行情服务器:本地部署,全市场行情私有云,低延迟、高并发、安全可控。