一、技能总览

Stock Monitor 是基于 Tushare / 新浪 / 腾讯财经 深度二次开发的A 股全市场实时监控与智能告警系统,主打毫秒级行情轮询、多维度异动监控、自定义告警策略、全指标盯盘、实时推送、可视化看板、量化风控。覆盖沪深 A 股、指数、ETF、期货、港股,是盘中盯盘、异动捕捉、风控预警、量化信号、实盘监控的核心工具。

深度对接 OpenClaw 全生态:联动 tushare-finance、Agent Browser、飞书推送、图表生成、市场调研,实现行情采集→指标计算→异动识别→告警推送→报表归档→策略执行全链路自动化,提供完整可直接部署的二次开发代码

二、传统盯盘痛点 & 二开惊喜升级

(一)传统工具短板

  1. 延迟高、刷新慢:3–5 秒刷新,错过盘中异动、拉升、跳水。
  2. 监控单一:仅价格 / 涨跌幅,无成交量、指标、龙虎榜、资金流监控。
  3. 告警简陋:仅固定阈值,无复合条件、防抖、冷却、分级告警
  4. 无自动化:手动盯盘、手动截图、手动发消息,效率极低。
  5. 数据孤立:行情、指标、财报、舆情无法联动分析。
  6. 无风控体系:无持仓监控、回撤预警、仓位风控、暴雷提醒。

(二)本工具・二开核心惊喜

全市场毫秒级轮询:1 秒级刷新,A 股全市场 + 自选股双模式,延迟 < 1.5 秒。

多维异动引擎:价格、涨跌、振幅、成交量、成交额、换手率、资金流向、龙虎榜、大宗、财报、舆情全维度监控。

智能告警系统(核心)自定义阈值、复合条件、多级告警、防抖冷却、防轰炸、触发日志、回溯分析

全指标实时计算MACD/RSI/KDJ/BOLL/MA/EMA/ATR/OBV 实时刷新,金叉死叉、突破跌破自动识别。

持仓 + 风控一体化:持仓盈亏、成本价、回撤预警、仓位管理、止损止盈、暴雷预警。

全自动推送:飞书 / 企业微信 / 钉钉 / 邮件 / 短信 / 语音播报,异动截图 + 行情 + 指标 + 触发原因一键推送。

可视化监控看板:实时行情、涨跌排行、异动队列、指标面板、资金流向、K 线分时图。

完整二开代码可直接部署、自定义指标、自定义策略、自定义数据源、插件化拓展

生态全联动:对接浏览器爬虫、金融数据、研报生成、量化回测、自动交易信号。

三、核心监控覆盖(全市场无死角)

(一)标的覆盖

  • 沪深 A 股:主板、创业板、科创板、北交所
  • 指数:上证、深成、沪深 300、中证 500、创业板指、行业指数
  • ETF/LOF/ 基金:全市场 ETF、货币 / 债券 / 股票基金
  • 期货 / 期权:国内期货主力、连续、期权
  • 港股 / 美股:港股主板、美股 ADR
  • 自选股分组:自定义多分组、批量导入导出

(二)监控维度(核心)

  1. 价格监控:最高价、最低价、现价、涨跌、涨跌幅、振幅、昨收、今开
  2. 量能监控:成交量、成交额、换手率、量比、内外盘、资金净流入
  3. 技术指标:MA5/10/20/60、MACD、RSI、KDJ、BOLL、ATR、OBV、金叉 / 死叉
  4. 异动事件涨跌停、炸板、地天板、天地板、放量拉升、跳水、突破新高 / 新低、均线突破、背离、龙虎榜、大宗、融资融券
  5. 基本面:业绩预告、财报发布、分红送股、解禁、减持、质押、暴雷风险
  6. 舆情:新闻、公告、研报、股吧情绪、热搜

四、核心功能模块(二开增强)

(一)实时行情引擎

  • 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、研报生成、浏览器爬虫,自动生成每日复盘、异动报告、持仓分析。

八、二开惊喜拓展

  1. AI 智能选股:接入大模型,自动筛选强势股、低位股、突破股,生成每日股票池。
  2. 量化策略回测:内置MACD/RSI/ 均线 / 突破策略回测,自动优化参数、生成实盘信号。
  3. 多维度风控体系舆情风控、财务风控、行业风控、黑天鹅预警,全维度风险拦截。
  4. WebSocket 实时推送:前端看板毫秒级实时行情、异动推送,专业交易级体验。
  5. 自动交易对接:对接券商 API,告警触发→自动下单→止损止盈全自动化。
  6. 私有化行情服务器:本地部署,全市场行情私有云,低延迟、高并发、安全可控。