OpenClaw 完整备份 - 2026-03-21

This commit is contained in:
huan
2026-03-21 15:31:06 +08:00
commit 8dd73a1d62
569 changed files with 76792 additions and 0 deletions
@@ -0,0 +1,7 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "a-stock-trading-assistant",
"installedVersion": "1.0.0",
"installedAt": 1773343749868
}
@@ -0,0 +1,105 @@
---
name: a-stock-trading-assistant
description: A股股票智能交易助手,专服务中国大陆股票市场。当用户询问A股股票行情、个股分析、大盘情绪、热点板块、交易策略、价格预警、涨跌幅、成交量、技术面或基本面分析时触发。只处理沪深A股(代码以6/00/30/68开头),自动忽略港股和美股。所有数据实时从东方财富、新浪财经、同花顺、雪球抓取。Use when: user asks about Chinese A-share stocks, 股票行情, 个股分析, 大盘分析, 板块热点, 买卖点, 止盈止损, 仓位建议, or any A-share trading-related query.
---
# A股股票智能交易助手
## 角色定位
你是专业A股交易助手,只服务中国大陆A股市场(沪深两市)。数据全部实时联网获取,数据源优先级:东方财富 → 新浪财经 → 同花顺 → 雪球。
## 股票代码识别规则
| 前缀 | 市场 | 示例 |
|------|------|------|
| 60xxxx | 上交所主板 | 600519 贵州茅台 |
| 00xxxx | 深交所主板 | 000001 平安银行 |
| 30xxxx | 创业板 | 300750 宁德时代 |
| 68xxxx | 科创板 | 688981 中芯国际 |
- 自动忽略港股(.HK)、美股(NASDAQ/NYSE)及其他境外市场
- 用户输入不带前缀时,根据数字范围自动判断市场
## 数据获取方式
优先用 `scripts/fetch_stock.py` 脚本获取实时数据。如脚本执行失败,改用 `web_fetch` 直接访问数据源。
详细 API 端点见 `references/data-sources.md`
### 快速调用脚本
```bash
# 查询单只股票实时行情
python3 /app/skills/a-stock-trading-assistant/scripts/fetch_stock.py --code 600519
# 查询大盘指数
python3 /app/skills/a-stock-trading-assistant/scripts/fetch_stock.py --index
# 查询热点板块
python3 /app/skills/a-stock-trading-assistant/scripts/fetch_stock.py --hot-sectors
```
## 六大核心能力工作流
### 1. 实时行情查询
1. 运行 `fetch_stock.py --code <代码>` 获取实时数据
2. 展示:当前价、涨跌幅、涨跌额、成交量、成交额、换手率、振幅、52周高/低
3. 附上分时走势摘要(涨跌节奏描述)
### 2. 个股综合分析
先获取实时行情,再分析:
- **技术面**:均线系统(MA5/10/20/60)、趋势判断、支撑位/压力位、量价结构、MACD/KDJ信号
- **基本面**:PE/PB估值、近期业绩、行业地位、主要风险点
- 技术面与基本面结合,给出综合判断(看多/看空/中性)
详细分析方法见 `references/analysis.md`
### 3. 大盘情绪与风险判断
获取上证指数、深证成指、创业板指实时数据,分析:
- 大盘强弱(趋势、量能、板块轮动)
- 市场情绪指数(赚钱效应、涨跌比)
- 风险等级(低/中/高)及应对建议
### 4. 热点板块与龙头股
1. 运行 `fetch_stock.py --hot-sectors` 获取涨幅榜板块
2. 识别:主线板块(连续性强)、情绪板块(短期热点)
3. 每个热点板块列出核心龙头股(涨停、强势领涨)
### 5. 交易策略与建议
基于用户的持仓/意向股,给出:
- **短线**(1-5天):催化剂、入场区间、止损位、止盈位
- **中线**(1-3月):趋势判断、分批建仓节奏、仓位比例
- 始终标注风险提示
格式模板:
```
【操作建议】XX股(XXXXXX
方向:做多/观望/回避
入场区间:XX.XX - XX.XX 元
止损位:XX.XX 元(跌破离场)
止盈位:XX.XX 元(分批减仓)
仓位:XX%(轻/中/重仓)
逻辑:[核心理由2-3条]
风险:[主要风险1-2条]
```
### 6. 价格预警监控
当用户设置预警时:
- 记录目标价、预警条件(突破/跌破/放量)到 `references/watchlist.md`
- 建议用户配合券商App实时推送,本工具做辅助分析
- 在后续对话中主动核对预警状态
## 输出规范
- 数据必须标注来源和获取时间(精确到分钟)
- 所有价格建议必须附风险提示
- 避免绝对化表述("必涨"/"稳赚"),用概率/可能性描述
- 数字精确到小数点后2位,成交额以亿元为单位
- 大盘/个股分析结构清晰,使用简洁表格或分项列出
@@ -0,0 +1,6 @@
{
"ownerId": "kn70hb4emf3csex5aeqt51arrx82d01z",
"slug": "a-stock-trading-assistant",
"version": "1.0.0",
"publishedAt": 1772789682966
}
@@ -0,0 +1,110 @@
# 个股分析方法参考
## 技术面分析框架
### 均线系统判断
| 形态 | 条件 | 含义 |
|------|------|------|
| 多头排列 | MA5>MA10>MA20>MA60 | 上升趋势强,可做多 |
| 空头排列 | MA5<MA10<MA20<MA60 | 下跌趋势中,回避 |
| 均线粘合 | 各均线收敛 | 方向待定,等待突破 |
| 金叉 | MA5上穿MA10/MA20 | 短期买入信号 |
| 死叉 | MA5下穿MA10/MA20 | 短期卖出信号 |
### 量价结构分析
- **放量上涨**:主力资金入场,趋势确认,可跟进
- **缩量上涨**:小心,动能不足,谨慎追高
- **放量下跌**:恐慌性出逃或主力砸盘,注意风险
- **缩量下跌**:调整中,无大资金出逃,可等底部
- **天量天价**:极端放量后往往是阶段顶部,注意减仓
- **地量地价**:成交极度萎缩后往往是底部信号
### 支撑位 / 压力位识别
1. **整数关口**100/200/300元等心理价位
2. **前期高点/低点**:突破前高=突破压力;跌破前低=下一支撑
3. **均线支撑**:MA20(月线)、MA60(季线)是重要支撑
4. **成交密集区**:历史上大量换手的价格区间,构成强支撑/压力
### MACD 信号
- **金叉+零轴上方**:强烈买入信号
- **死叉+零轴下方**:强烈卖出信号
- **顶/底背离**:价格创新高但MACD不创新高 = 顶背离(卖);价格创新低但MACD不创新低 = 底背离(买)
- **红柱/绿柱缩短**:动能减弱,可能转向
### KDJ 信号
- KDJ>80:超买区,注意回调
- KDJ<20:超卖区,反弹机会
- K线上穿D线(金叉):买入
- K线下穿D线(死叉):卖出
---
## 基本面分析框架
### 估值判断
| 指标 | 低估 | 合理 | 高估 |
|------|------|------|------|
| PE(市盈率) | <行业均值50% | 接近行业均值 | >行业均值2倍 |
| PB(市净率) | <1 | 1-3 | >5 |
- 消费/医药类:参考PE估值
- 银行/地产类:参考PB估值
- 成长类(科技/新能源):参考PEG(<1为合理)
### 业绩评估要点
- 近4个季度营收/净利增速趋势
- 毛利率变化(毛利率提升=竞争力增强)
- 净利率水平(与同行比较)
- 现金流情况(经营活动现金流>净利润=高质量利润)
### 行业地位
- 市占率排名(行业前3更优)
- 护城河类型:品牌/技术/成本/网络效应
- 政策支持方向(新能源/半导体/AI等优先)
---
## 大盘分析框架
### 市场情绪量化
| 指标 | 计算方式 | 情绪判断 |
|------|---------|---------|
| 赚钱效应 | 上涨股/总股数 | >60%强,<40%弱 |
| 涨停数量 | 当日涨停家数 | >100家热,<30家冷 |
| 量能比 | 今日量/5日均量 | >1.2放量,<0.8缩量 |
### 风险等级定义
| 级别 | 特征 | 建议仓位 |
|------|------|---------|
| 低风险 | 大盘趋势向上,量能充裕,涨多跌少 | 7-9成仓 |
| 中风险 | 方向不明,震荡整理 | 3-6成仓 |
| 高风险 | 趋势向下,缩量或恐慌放量 | 0-2成仓 |
---
## 热点板块判断标准
### 主线板块(可重点跟踪)
- 连续3天以上保持热度
- 有政策/事件催化
- 龙头股有持续涨停
### 情绪板块(短线机会)
- 单日暴热,次日需观察持续性
- 题材性质(概念股)
- 跟风盘多,需快进快出
### 龙头股识别
- 板块内首板/连板的核心票
- 换手率高但不崩盘
- 量价配合良好
@@ -0,0 +1,114 @@
# 数据源 API 参考
## 1. 新浪财经(最稳定,优先使用)
### 实时行情
```
# 单股(沪市加sh,深市加sz
GET http://hq.sinajs.cn/list=sh600519
GET http://hq.sinajs.cn/list=sz000001
# 多股同时查询
GET http://hq.sinajs.cn/list=sh600519,sz000001,sz300750
```
返回格式(逗号分隔的字符串):
```
var hq_str_sh600519="贵州茅台,1788.00,1785.00,1800.00,1810.00,1780.00,1799.00,1800.00,3456789,6234567890.00,100,1799.00,...,2024-01-15,15:00:00,00";
```
字段顺序:股票名,昨收,今开,当前价,最高,最低,买一价,卖一价,成交量(手),成交额,买一量,买一价,...,日期,时间
### 大盘指数
```
GET http://hq.sinajs.cn/list=s_sh000001,s_sz399001,s_sz399006
# 上证指数,深证成指,创业板指
```
---
## 2. 东方财富(数据全面,适合深度查询)
### 实时行情
```
GET http://push2.eastmoney.com/api/qt/stock/get?secid={market}.{code}&fields=f43,f44,f45,f46,f47,f48,f57,f58,f60,f107,f169,f170,f171,f530
```
market: 1=沪市, 0=深市
关键字段:
- f43: 最新价(×0.01
- f44: 最高价
- f45: 最低价
- f46: 今开
- f47: 成交量(手)
- f48: 成交额(元)
- f57: 股票代码
- f58: 股票名称
- f60: 昨收
- f170: 涨跌幅(%×100
- f169: 涨跌额
- f171: 换手率(%×100
### 涨幅榜/板块榜
```
# A股涨幅榜(前50
GET http://push2.eastmoney.com/api/qt/clist/get?pn=1&pz=50&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&fid=f3&fs=m:0+t:6,m:0+t:13,m:0+t:80,m:1+t:2,m:1+t:23&fields=f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f13,f14,f15,f16,f17,f18
# 板块涨跌榜(概念/行业)
GET http://push2.eastmoney.com/api/qt/clist/get?pn=1&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&fid=f3&fs=m:90+t:2+f:!50&fields=f1,f2,f3,f4,f5,f6,f7,f8,f12,f14,f20,f21
```
### 个股分时数据
```
GET http://push2.eastmoney.com/api/qt/stock/trends2/get?secid={market}.{code}&fields1=f1,f2,f3,f4,f5&fields2=f51,f52,f53,f54,f55,f56,f57,f58&iscr=0&iscca=0
```
---
## 3. 同花顺
### 实时行情
```
GET https://d.10jqka.com.cn/v4/time/hs_{code}/today.js
# code: 直接用6位数字,如 600519
```
注意:请求需要带 Referer: https://www.10jqka.com.cn
---
## 4. 雪球
### 实时行情
```
GET https://stock.xueqiu.com/v5/stock/quote.json?symbol={symbol}&extend=detail
# symbol: SH600519 / SZ000001 / SZ300750
```
注意:需要先访问 https://xueqiu.com 获取 cookiesession),再请求数据接口。脚本中已处理。
---
## 市场代码对照
| 代码前缀 | 新浪前缀 | 东财market | 雪球前缀 |
|---------|---------|-----------|---------|
| 600xxx / 601xxx / 603xxx / 605xxx / 688xxx | sh | 1 | SH |
| 000xxx / 001xxx / 002xxx / 003xxx / 300xxx / 301xxx | sz | 0 | SZ |
## 大盘指数代码
| 指数 | 新浪 | 东财secid |
|------|------|----------|
| 上证指数 | sh000001 | 1.000001 |
| 深证成指 | sz399001 | 0.399001 |
| 创业板指 | sz399006 | 0.399006 |
| 科创50 | sh000688 | 1.000688 |
| 北证50 | bj899050 | — |
@@ -0,0 +1,22 @@
# 价格预警监控列表
## 预警记录
| 股票 | 代码 | 预警类型 | 目标价 | 当前价 | 状态 | 设置时间 |
|------|------|----------|--------|--------|------|----------|
| 铜陵有色 | 000630 | 跌破 | 6.80 元 | 7.15 元 | ⏳ 监控中 | 2026-03-13 03:33 |
---
## 预警详情
### 铜陵有色 (000630)
- **预警条件**:股价跌破 6.80 元
- **当前价格**7.15 元 (2026-03-13 15:00)
- **距离预警**-0.35 元 (-4.9%)
- **设置时间**2026-03-13 03:33
- **备注**:用户要求跌破 6.8 元时进行预警
---
> ⚠️ **说明**:本工具做辅助分析,建议配合券商 App 实时推送获取即时预警。
@@ -0,0 +1,299 @@
#!/usr/bin/env python3
"""
A股实时行情抓取脚本
数据源:新浪财经(主)、东方财富(备)
用法:
python3 fetch_stock.py --code 600519
python3 fetch_stock.py --code 000001 sz000001 300750
python3 fetch_stock.py --index
python3 fetch_stock.py --hot-sectors
"""
import argparse
import json
import re
import sys
import urllib.request
import urllib.error
from datetime import datetime
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Referer": "https://finance.sina.com.cn",
"Accept-Language": "zh-CN,zh;q=0.9",
}
def get_market_prefix(code: str) -> str:
"""根据股票代码判断市场前缀"""
code = code.strip().upper()
if code.startswith("SH") or code.startswith("SZ"):
return code[:2].lower(), code[2:]
code = re.sub(r"[^0-9]", "", code)
if code.startswith(("60", "68", "51", "58", "11")):
return "sh", code
elif code.startswith(("00", "30", "15", "12", "16", "13")):
return "sz", code
return "sh", code # default
def fetch_url(url: str, extra_headers: dict = None) -> str:
req = urllib.request.Request(url, headers=HEADERS)
if extra_headers:
for k, v in extra_headers.items():
req.add_header(k, v)
try:
with urllib.request.urlopen(req, timeout=10) as resp:
charset = "gbk" if "sina" in url or "sinajs" in url else "utf-8"
return resp.read().decode(charset, errors="replace")
except Exception as e:
return ""
def parse_sina_stock(raw: str, symbol: str) -> dict:
"""解析新浪财经股票数据"""
match = re.search(r'"([^"]*)"', raw)
if not match:
return {}
parts = match.group(1).split(",")
if len(parts) < 32:
return {}
try:
name = parts[0]
prev_close = float(parts[2]) if parts[2] else 0
open_price = float(parts[1]) if parts[1] else 0
current = float(parts[3]) if parts[3] else 0
high = float(parts[4]) if parts[4] else 0
low = float(parts[5]) if parts[5] else 0
volume = int(parts[8]) if parts[8] else 0 # 手
amount = float(parts[9]) if parts[9] else 0 # 元
date_str = parts[30] if len(parts) > 30 else ""
time_str = parts[31] if len(parts) > 31 else ""
change = current - prev_close
change_pct = (change / prev_close * 100) if prev_close else 0
turnover_approx = volume / 1000 # 粗略换手(无流通股数据)
return {
"symbol": symbol,
"name": name,
"current": round(current, 2),
"change": round(change, 2),
"change_pct": round(change_pct, 2),
"open": round(open_price, 2),
"high": round(high, 2),
"low": round(low, 2),
"prev_close": round(prev_close, 2),
"volume_lot": volume, # 手
"amount_yuan": round(amount, 2),
"amount_yi": round(amount / 1e8, 2),
"date": date_str,
"time": time_str,
"source": "新浪财经",
"fetch_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}
except (ValueError, IndexError):
return {}
def fetch_single_stock(code: str) -> dict:
"""抓取单只股票行情"""
prefix, clean_code = get_market_prefix(code)
symbol = f"{prefix}{clean_code}"
url = f"http://hq.sinajs.cn/list={symbol}"
raw = fetch_url(url)
if raw:
data = parse_sina_stock(raw, symbol)
if data:
return data
# 备用:东方财富
market = 1 if prefix == "sh" else 0
url2 = (
f"http://push2.eastmoney.com/api/qt/stock/get"
f"?secid={market}.{clean_code}"
f"&fields=f43,f44,f45,f46,f47,f48,f57,f58,f60,f107,f169,f170,f171"
)
raw2 = fetch_url(url2, {"Referer": "https://www.eastmoney.com"})
if raw2:
try:
obj = json.loads(raw2)
d = obj.get("data", {}) or {}
if d.get("f43"):
prev = d["f60"] / 100
curr = d["f43"] / 100
chg = d["f169"] / 100
chg_pct = d["f170"] / 100
return {
"symbol": symbol,
"name": d.get("f58", ""),
"current": round(curr, 2),
"change": round(chg, 2),
"change_pct": round(chg_pct, 2),
"open": round(d["f46"] / 100, 2),
"high": round(d["f44"] / 100, 2),
"low": round(d["f45"] / 100, 2),
"prev_close": round(prev, 2),
"volume_lot": d.get("f47", 0),
"amount_yuan": d.get("f48", 0),
"amount_yi": round(d.get("f48", 0) / 1e8, 2),
"turnover_pct": round(d.get("f171", 0) / 100, 2),
"source": "东方财富",
"fetch_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}
except Exception:
pass
return {"error": f"无法获取 {code} 的行情数据", "symbol": symbol}
def fetch_index() -> list:
"""抓取主要大盘指数"""
symbols = "s_sh000001,s_sz399001,s_sz399006,s_sh000688"
names_map = {
"s_sh000001": "上证指数",
"s_sz399001": "深证成指",
"s_sz399006": "创业板指",
"s_sh000688": "科创50",
}
url = f"http://hq.sinajs.cn/list={symbols}"
raw = fetch_url(url)
results = []
if raw:
for sym, name in names_map.items():
pattern = rf'hq_str_{re.escape(sym)}="([^"]*)"'
m = re.search(pattern, raw)
if m:
parts = m.group(1).split(",")
if len(parts) >= 5:
try:
results.append({
"name": parts[0] or name,
"current": float(parts[1]),
"change": float(parts[2]),
"change_pct": float(parts[3]),
"volume_yi_lot": round(float(parts[4]) / 1e8, 2),
"amount_yi": round(float(parts[5]) / 1e8, 2) if len(parts) > 5 else 0,
"fetch_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"source": "新浪财经",
})
except (ValueError, IndexError):
pass
return results
def fetch_hot_sectors() -> list:
"""抓取热点板块(东方财富概念板块涨幅榜)"""
url = (
"http://push2.eastmoney.com/api/qt/clist/get"
"?pn=1&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281"
"&fltt=2&invt=2&fid=f3"
"&fs=m:90+t:2+f:!50"
"&fields=f2,f3,f4,f12,f14,f20,f128,f136,f207,f208,f209"
)
raw = fetch_url(url, {"Referer": "https://www.eastmoney.com"})
results = []
if raw:
try:
obj = json.loads(raw)
items = obj.get("data", {}).get("diff", [])
for item in items:
results.append({
"name": item.get("f14", ""),
"change_pct": round(item.get("f3", 0), 2),
"leading_stock": item.get("f128", ""),
"leading_change_pct": round(item.get("f136", 0), 2),
"amount_yi": round(item.get("f20", 0) / 1e8, 2),
})
except Exception:
pass
return results
def fmt_stock(d: dict) -> str:
if "error" in d:
return f"{d['error']}"
sign = "+" if d["change"] >= 0 else ""
emoji = "🔴" if d["change"] >= 0 else "🟢"
lines = [
f"{emoji} {d['name']}{d['symbol'].upper()}",
f" 当前价:{d['current']}",
f" 涨跌幅:{sign}{d['change_pct']}% 涨跌额:{sign}{d['change']}",
f" 今开:{d['open']} 最高:{d['high']} 最低:{d['low']} 昨收:{d['prev_close']}",
f" 成交量:{d['volume_lot']:,} 手 成交额:{d['amount_yi']} 亿",
]
if "turnover_pct" in d:
lines.append(f" 换手率:{d['turnover_pct']}%")
lines.append(f" 数据来源:{d['source']} | 更新时间:{d.get('time', '')} {d['fetch_time']}")
return "\n".join(lines)
def fmt_index(items: list) -> str:
lines = ["📊 大盘指数实时行情"]
for d in items:
sign = "+" if d["change"] >= 0 else ""
emoji = "🔴" if d["change"] >= 0 else "🟢"
lines.append(
f" {emoji} {d['name']}: {d['current']:,.2f} {sign}{d['change']:+.2f} ({sign}{d['change_pct']}%)"
f" 成交额 {d['amount_yi']} 亿"
)
if items:
lines.append(f" 更新时间:{items[0]['fetch_time']}")
return "\n".join(lines)
def fmt_sectors(items: list) -> str:
lines = ["🔥 热点板块涨幅榜(概念板块 TOP20)"]
for i, d in enumerate(items, 1):
sign = "+" if d["change_pct"] >= 0 else ""
lines.append(
f" {i:2d}. {d['name']:<12} {sign}{d['change_pct']}%"
f" 龙头:{d['leading_stock']}({sign}{d['leading_change_pct']}%)"
f" 成交额:{d['amount_yi']}亿"
)
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="A股实时行情抓取")
parser.add_argument("--code", nargs="+", help="股票代码,支持多个(如 600519 000001")
parser.add_argument("--index", action="store_true", help="查询大盘指数")
parser.add_argument("--hot-sectors", action="store_true", help="查询热点板块")
parser.add_argument("--json", action="store_true", help="输出原始JSON")
args = parser.parse_args()
if args.index:
data = fetch_index()
if args.json:
print(json.dumps(data, ensure_ascii=False, indent=2))
else:
print(fmt_index(data))
return
if args.hot_sectors:
data = fetch_hot_sectors()
if args.json:
print(json.dumps(data, ensure_ascii=False, indent=2))
else:
print(fmt_sectors(data))
return
if args.code:
results = []
for code in args.code:
d = fetch_single_stock(code)
results.append(d)
if args.json:
print(json.dumps(results, ensure_ascii=False, indent=2))
else:
for d in results:
print(fmt_stock(d))
print()
return
parser.print_help()
if __name__ == "__main__":
main()
@@ -0,0 +1,7 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "content-collector-skill",
"installedVersion": "0.1.0",
"installedAt": 1773515645180
}
@@ -0,0 +1,800 @@
---
name: content-collector
description: Automatically collect and archive content from shared links in group chats. When a user shares a link (WeChat articles, Feishu docs, web pages, etc.) in any group chat and asks to archive/collect/save it, this skill triggers to fetch the content, create a Feishu document, and update the knowledge base table. Use when: (1) User shares a link and asks to "收录/转存/保存" content, (2) Need to archive web content to Feishu docs, (3) Building a personal knowledge base from shared links, (4) Organizing learning materials from various sources.
---
# Content Collector - 链接内容自动收录技能
## Overview
This skill enables automatic collection and archiving of content from shared links into a structured knowledge base.
**Core Workflow:**
```
Detect Link → Fetch Content → Create Feishu Doc → Update Table
```
## When to Use
### 模式1:主动触发(显式关键词)
当用户消息包含以下**触发词**时,立即执行收录:
- "收录" / "转存" / "保存" / "存档" / "存一下" / "归档" / "备份" / "收藏"
- "存到知识库" / "加入知识库" / "转飞书"
**示例:**
- "这个链接收录一下"
- "存到知识库"
- "转存这篇教程"
### 模式2:静默收录(自动检测)
在**群聊场景**中,自动检测以下链接并静默收录:
- 飞书文档/表格/Wikifeishu.cn
- 微信公众号文章(mp.weixin.qq.com
- 技术博客/教程站点
- 知识分享类链接
**静默收录条件:**
1. 消息来自群聊(非私聊)
2. 消息包含可识别的知识类链接
3. 用户没有明确拒绝的意图
**两种模式优先级:**
```
检测到主动触发词 → 立即收录(显式模式)
未检测到触发词但检测到链接 → 静默收录(隐式模式)
```
## Supported Link Types
| Type | Example | Fetch Method |
|------|---------|--------------|
| WeChat Article | `https://mp.weixin.qq.com/s/xxx` | kimi_fetch |
| Feishu Doc | `https://xxx.feishu.cn/docx/xxx` | feishu_fetch_doc |
| Feishu Wiki | `https://xxx.feishu.cn/wiki/xxx` | feishu_fetch_doc |
| Web Page | General URLs | kimi_fetch / web_fetch |
## Global Availability (全局可用配置)
**生效范围:所有用户、所有群聊**
本技能已配置为全局可用,支持以下对象:
| 对象类型 | 支持状态 | 说明 |
|---------|---------|------|
| **所有用户** | ✅ 可用 | 任何用户分享的链接均可被收录 |
| **所有群聊** | ✅ 可用 | 支持技能中心群、养虾群、学习群等所有群组 |
| **私聊消息** | ✅ 可用 | 用户私信分享链接也可触发收录 |
| **多渠道** | ✅ 可用 | 飞书、其他渠道统一支持 |
**权限说明:**
- 任何用户均可触发收录(无需管理员权限)
- 收录的文档统一存储到指定的知识库目录
- 所有用户均可查看已收录的文档
---
## Installation & Permission Check (安装与权限检查)
在正式使用本技能前,系统必须自动或引导用户完成以下权限校验,以确保流程不中断:
### 1. 飞书权限清单
| 权限项 | 验证工具 | 目的 |
|-------|---------|------|
| **OAuth 授权** | `feishu_oauth` | 获取操作飞书文档和表格的用户凭证 |
| **知识库写入权限** | `feishu_create_doc` | 确保能在指定的 Space ID 下创建节点 |
| **多维表格编辑权限** | `feishu_bitable_app_table_record` | 确保能向指定的 app_token 写入记录 |
| **图片上传权限** | `feishu_im_bot_upload` | 允许将本地图片同步至飞书素材库 |
### 2. 预检流程 (Pre-flight Check)
每次“安装”或配置更新后,执行以下检查:
1. **验证 Space ID 可访问性**:尝试在指定目录下获取节点列表。
2. **验证 Table 结构**:检查 `关键词``原链接` 等必需字段是否存在。
3. **静默测试**:如果权限不足,立即通过 `feishu_oauth` 弹出授权引导,而非在执行收录时报错。
---
## Configuration
Before using, ensure these are configured in MEMORY.md:
```markdown
## Content Collector Config
- **Knowledge Base Table**: `[Your Bitable App Token]` (Bitable app_token)
- **Table URL**: [Your Bitable Table URL]
- **Default Table ID**: `[Your Table ID]` (will auto-detect if available)
- **Knowledge Base Space ID**: `[Your Space ID]` (所有文档创建在此知识库下)
- **Knowledge Base URL**: [Your Knowledge Base Homepage URL]
- **Content Categories**: 技术教程, 实战案例, 产品文档, 学习笔记
- **Global Access**: 所有用户可用,所有群聊可用
```
**Note**:
1. This skill updates ONLY the configured knowledge base table. Do not create or update any other tables.
2. **All created documents must be saved under the designated Knowledge Base** using wiki_node parameter.
3. **Global Access**: 所有用户、所有群聊均可使用本技能,收录的文档对全员可见。
---
## 📚 知识库文档存储规则(必遵守)
所有收录的文档必须按照以下规则分类存储到知识库对应目录:
### 知识库目录结构
请参考各项目或团队定义的知识库标准目录结构进行存储。收录的文档通常存放在“素材”或“归档”类目录下。
### 文档分类映射规则
| 内容分类 | 存储目录 (wiki_node) | 命名前缀 | 示例 |
|----------|---------------------|----------|------|
| 技术教程 | `F9pFw9dxTiXmpsk5bNlco704nag` (内容文档) | 📖 | 📖 [标题] |
| 实战案例 | `F9pFw9dxTiXmpsk5bNlco704nag` (内容文档) | 🛠️ | 🛠️ [标题] |
| 产品文档 | `F9pFw9dxTiXmpsk5bNlco704nag` (内容文档) | 📄 | 📄 [标题] |
| 学习笔记 | `F9pFw9dxTiXmpsk5bNlco704nag` (内容文档) | 💡 | 💡 [标题] |
| 热点资讯 | `F9pFw9dxTiXmpsk5bNlco704nag` (内容文档) | 🔥 | 🔥 [标题] |
| 设计技能 | `F9pFw9dxTiXmpsk5bNlco704nag` (内容文档) | 🎨 | 🎨 [标题] |
| 工具推荐 | `F9pFw9dxTiXmpsk5bNlco704nag` (内容文档) | 🔧 | 🔧 [标题] |
| 训练营 | `F9pFw9dxTiXmpsk5bNlco704nag` (内容文档) | 🎓 | 🎓 [标题] |
### 文档命名规范
```
[Emoji前缀] [原标题] | 收录日期
示例:
📖 OpenClaw保姆级教程 | 2026-03-08
🛠️ 火山方舟自动化报表案例 | 2026-03-08
🔥 GPT-5.4发布解读 | 2026-03-08
```
### 文档模板
```markdown
# [Emoji] [原标题]
> 📌 **元信息**
> - 来源:[原始来源]
> - 原文链接:[原始URL]
> - 收录时间:YYYY-MM-DD
> - 内容分类:[技术教程/实战案例/产品文档/学习笔记/热点资讯/设计技能/工具推荐/训练营]
> - 关键词:[关键词1, 关键词2, 关键词3]
---
## 📋 核心要点
[3-5条核心内容摘要]
---
## 📝 正文内容
[完整的转存内容]
---
## 🔗 相关链接
- 原文链接:[原始URL]
- 知识库索引:[素材池文档索引链接]
---
📚 **收录时间**YYYY-MM-DD
🏷️ **分类**[分类名]
🔖 **关键词**[关键词]
```
### 自动更新素材索引
每次收录完成后,必须:
1. **更新多维表格** - 添加新记录到素材池表格
2. **更新素材索引文档** - 在「📚 内容素材池文档索引」中添加条目
3. **更新分类统计** - 更新各分类的文档数量和占比
---
## Workflow
### Step 1: Detect and Parse Link
Extract URL from user message using regex or direct extraction.
### Step 2: Fetch Content
Choose appropriate fetch method based on URL pattern:
**For WeChat articles:**
```python
kimi_fetch(url="https://mp.weixin.qq.com/s/xxx")
```
**For Feishu docs:**
```python
feishu_fetch_doc(doc_id="https://xxx.feishu.cn/docx/xxx")
```
**For general web pages:**
```python
kimi_fetch(url="https://example.com/article")
# or
web_fetch(url="https://example.com/article")
```
### Step 3: Analyze and Categorize
**智能分类判断:**
根据内容特征自动判断分类:
| 判断依据 | 分类 |
|----------|------|
| 包含"安装/配置/部署/教程"等词 | 📖 技术教程 |
| 包含"案例/实战/项目/演示"等词 | 🛠️ 实战案例 |
| 包含"安全/公告/版本/功能"等词 | 📄 产品文档 |
| 包含"学习/成长/指南/笔记"等词 | 💡 学习笔记 |
| 包含"发布/新功能/热点"等词 | 🔥 热点资讯 |
| 包含"设计/Prompt/美学"等词 | 🎨 设计技能 |
| 包含"工具/CLI/插件"等词 | 🔧 工具推荐 |
| 包含"训练营/课程/教学"等词 | 🎓 训练营 |
### Step 4: Process Images (图片处理)
When content contains images, download and upload them to Feishu:
**Image Processing Workflow:**
```python
# 1. Extract image URLs from markdown
import re
image_urls = re.findall(r'!\[.*?\]\((https?://[^\)]+)\)', markdown_content)
# 2. Download and upload each image
for img_url in image_urls:
try:
# Download image
local_path = f"/tmp/img_{hash(img_url)}.jpg"
download_image(img_url, local_path)
# Upload to Feishu
upload_result = feishu_im_bot_upload(
action="upload_image",
file_path=local_path
)
# Replace URL in markdown
new_url = upload_result.get("image_key") or img_url
markdown_content = markdown_content.replace(img_url, new_url)
except Exception as e:
# Keep original URL if upload fails
print(f"Failed to process image {img_url}: {e}")
continue
```
**Fallback Strategy:**
- If image upload fails, keep original URL
- Add warning note in document
- Include original source link for reference
### Step 5: Create Feishu Document (按知识库规则存储)
Convert processed markdown to Feishu document with proper organization:
```python
# 1. 确定分类和参数
content_category = classify_content(markdown_content) # 📖/🛠️/📄/💡/🔥/🎨/🔧/🎓
emoji_prefix = get_emoji_prefix(content_category) # 根据分类获取emoji
wiki_node = get_wiki_node_by_category(content_category) # 获取存储目录
# 2. 生成文档标题
doc_title = f"{emoji_prefix} {original_title} | {today_date}"
# 3. 生成文档内容(使用标准模板)
doc_content = f"""# {emoji_prefix} {original_title}
> 📌 **元信息**
> - 来源:{source_name}
> - 原文链接:{original_url}
> - 收录时间:{today_date}
> - 内容分类:{content_category}
> - 关键词:{keywords}
---
## 📋 核心要点
{extract_key_points(markdown_content, 5)}
---
## 📝 正文内容
{processed_markdown_content}
---
## 🔗 相关链接
- 原文链接:{original_url}
- 知识库索引:[Your Index Document URL]
---
📅 **收录时间**{today_date}
🏷️ **分类**{content_category}
🔖 **关键词**{keywords}
"""
# 4. 创建文档到知识库对应目录
feishu_create_doc(
title=doc_title,
markdown=doc_content,
wiki_node=wiki_node # 必须指定存储目录
)
```
**存储目录映射:**
| 分类 | wiki_node | 目录名 |
|------|-----------|--------|
| 所有素材 | `F9pFw9dxTiXmpsk5bNlco704nag` | 04-内容素材 |
**IMPORTANT**:
1. All documents MUST be created under the designated Knowledge Base using wiki_node parameter.
2. Documents must follow the naming convention: `[Emoji] [Title] | [Date]`
3. Documents must use the standard template with metadata section.
### Step 6: Update Knowledge Base Table
Add record to the Bitable knowledge base (ONLY update this specific table):
```python
feishu_bitable_app_table_record(
action="create",
app_token="[Your App Token]", # Configured in MEMORY.md
table_id="[Your Table ID]", # Will use correct table ID from the base
fields={
"关键词": keywords,
"内容分类": content_category,
"文档标题": [{"text": original_title, "type": "text"}],
"来源": [{"text": source_name, "type": "text"}],
"核心要点": [{"text": key_points, "type": "text"}],
"飞书文档链接": {"link": new_doc_url, "text": "飞书文档", "type": "url"},
"原链接": {"link": original_url, "text": "原文链接", "type": "url"} # 新增:存储原始链接
}
)
```
**Table Fields:**
| Field | Type | Description |
|-------|------|-------------|
| 关键词 | Text | Search keywords for the content |
| 内容分类 | Single Select | Category: 📖技术教程/🛠️实战案例/📄产品文档/💡学习笔记/🔥热点资讯/🎨设计技能/🔧工具推荐/🎓训练营 |
| 文档标题 | Text | Title of the archived document |
| 来源 | Text | Original source name |
| 核心要点 | Text | Key points summary (3-5 items) |
| 飞书文档链接 | URL | Link to the created Feishu document |
| 原链接 | URL | **Original source URL** - 新增字段,存储采集的原始链接 |
**IMPORTANT**: Only update the configured knowledge base table. Never create or modify other tables.
### Step 7: Update Content Index Document
After creating the document and updating the table, MUST update the index document:
```python
# 1. 获取当前索引文档内容
index_doc = feishu_fetch_doc(doc_id="[Your Index Doc ID]")
# 2. 在对应分类表格中添加新行
new_index_entry = f"| {original_title} | {source_name} | [查看]({new_doc_url}) |\n"
# 3. 更新分类统计
update_category_stats(content_category)
# 4. 更新总计数
update_total_count()
```
**或者直接追加到索引文档的末尾:**
```python
feishu_update_doc(
doc_id="[Your Index Doc ID]",
mode="append",
markdown=f"""
| {original_title} | {source_name} | [查看]({new_doc_url}) |
"""
)
```
---
## Content Categorization Guide
| Category | Emoji | Description | Examples |
|----------|-------|-------------|----------|
| **技术教程** | 📖 | Step-by-step technical guides | Installation, configuration, API usage |
| **实战案例** | 🛠️ | Real-world implementation examples | Case studies, project demos |
| **产品文档** | 📄 | Product features, security notices | Release notes, security advisories |
| **学习笔记** | 💡 | Conceptual knowledge, methodologies | Best practices, architecture guides |
| **热点资讯** | 🔥 | Breaking news, releases | GPT-5.4, new features |
| **设计技能** | 🎨 | Design, prompts, aesthetics | AJ's prompts, design guides |
| **工具推荐** | 🔧 | Tools, CLI, plugins | gws, trae, autotools |
| **训练营** | 🎓 | Courses, bootcamps, tutorials | OpenClaw bootcamp |
**分类判断优先级:**
1. 优先根据用户指定分类
2. 其次根据标题关键词
3. 最后根据内容特征自动判断
4. 不确定时标记为"待分类",请用户确认
## Delete Record Process
When user replies "删除" or "删除 [keyword]":
```python
# 1. Search records by keyword
feishu_bitable_app_table_record(
action="list",
app_token="[Your App Token]",
table_id="[Your Table ID]",
filter={
"conjunction": "and",
"conditions": [
{"field_name": "关键词", "operator": "contains", "value": [keyword]}
]
}
)
# 2. Confirm deletion
# If multiple found → list for user to select
# If single found → ask for confirmation
# 3. Execute deletion
feishu_bitable_app_table_record(
action="delete",
app_token="[Your App Token]",
table_id="[Your Table ID]",
record_id="record_id_to_delete"
)
```
## Error Handling
### Common Issues
| Error | Cause | Solution |
|-------|-------|----------|
| Fetch timeout | Network issue or heavy content | Retry with longer timeout, or use alternative fetch method |
| Unauthenticated | OAuth token expired or not authed | Trigger `feishu_oauth` to refresh user credentials |
| Permission denied | No write access to Space/Table | Check if user/bot has 'Editor' role in Feishu |
| Content too long | Exceeds API limits | Truncate or split into multiple documents |
| Table update failed | Wrong app_token or table_id | Verify configuration in MEMORY.md |
| Field Missing | "原链接" field not in table | Add the field to Bitable manually or via API |
### Recovery Steps
1. If fetch fails → Try alternative method (kimi_fetch → web_fetch)
2. If Feishu doc creation fails → Check OAuth status
3. If table update fails → Verify table structure and field names
4. Always report partial success (doc created but table not updated)
## Response Template
### 收录成功响应(流式Post格式)
```json
{
"msg_type": "post",
"content": {
"post": {
"zh_cn": {
"title": "✅ 收录完成",
"content": [
[
{"tag": "text", "text": "📄 "},
{"tag": "text", "text": "{emoji} {原标题} | {日期}", "style": {"bold": true}}
],
[{"tag": "text", "text": ""}],
[
{"tag": "text", "text": "💡 文档亮点:", "style": {"bold": true}}
],
[
{"tag": "text", "text": "• {亮点1}"}
],
[
{"tag": "text", "text": "• {亮点2}"}
],
[
{"tag": "text", "text": "• {亮点3}"}
],
[{"tag": "text", "text": ""}],
[
{"tag": "text", "text": "🔗 "},
{"tag": "a", "text": "查看飞书文档", "href": "{文档URL}"}
]
]
}
}
}
}
```
**简洁输出示例:**
```
✅ 收录完成
📄 📖 OpenClaw配置指南 | 2026-03-08
💡 文档亮点:
• 完整配置示例,含9大模块详解
• 多Agent扩展配置方案
• 生产环境安全配置建议
🔗 查看飞书文档 → [点击打开](https://xxx.feishu.cn/docx/xxx)
```
### 静默收录响应(流式Post格式)
```json
{
"msg_type": "post",
"content": {
"post": {
"zh_cn": {
"title": "✅ 已自动收录",
"content": [
[
{"tag": "text", "text": "📄 "},
{"tag": "text", "text": "{emoji} {原标题}", "style": {"bold": true}}
],
[{"tag": "text", "text": ""}],
[
{"tag": "text", "text": "💡 亮点:{亮点摘要}"}
],
[{"tag": "text", "text": ""}],
[
{"tag": "a", "text": "📎 查看文档", "href": "{文档URL}"}
]
]
}
}
}
}
```
### 批量收录响应(流式Post格式)
```json
{
"msg_type": "post",
"content": {
"post": {
"zh_cn": {
"title": "✅ 批量收录完成({N}份)",
"content": [
[
{"tag": "text", "text": "📄 {emoji1} {标题1}", "style": {"bold": true}}
],
[
{"tag": "text", "text": " 💡 {亮点1}"}
],
[
{"tag": "a", "text": " 🔗 查看", "href": "{链接1}"}
],
[{"tag": "text", "text": ""}],
[
{"tag": "text", "text": "📄 {emoji2} {标题2}", "style": {"bold": true}}
],
[
{"tag": "text", "text": " 💡 {亮点2}"}
],
[
{"tag": "a", "text": " 🔗 查看", "href": "{链接2}"}
]
]
}
}
}
}
```
**输出原则:**
1. **必须流式Post格式** - 使用 msg_type: post
2. **只包含3个核心要素:**
- 文件名称(📄 Emoji + 标题 + 日期)
- 文档亮点(💡 3-5条核心要点)
- 飞书链接(🔗 点击查看)
3. **不输出其他信息** - 不显示分类、不显示表格更新、不显示统计
4. **保持简洁** - 每份文档3-5行内容
## Best Practices
1. **Always verify content was fetched correctly** before creating documents
2. **Extract key insights** from the content for the summary
3. **Use appropriate category** based on content nature
4. **Generate relevant keywords** for better searchability
5. **Keep source attribution** clear for copyright respect
6. **Handle partial failures gracefully** - document what succeeded and what failed
7. **Update index document** - Every new document must be added to the index
8. **Follow naming convention** - Use [Emoji] [Title] | [Date] format
9. **Store in correct directory** - Use wiki_node to place in right category
## 收录完成检查清单 (Checklist)
每次收录必须完成以下所有步骤:
- [ ] **执行权限预检**(验证 OAuth 及 Space/Table 写入权限)
- [ ] 获取并处理原始内容(含图片)
- [ ] 智能分类并确定 Emoji 前缀
- [ ] 提取核心要点(3-5条)
- [ ] 生成关键词
- [ ] **创建飞书文档**(使用标准模板,指定 wiki_node)
- [ ] **更新多维表格**(添加完整记录,包含**原链接**字段)
- [ ] **更新文档索引**(在素材索引中添加条目)
- [ ] 发送收录完成通知给用户
**任何一步未完成,视为收录失败!**
## Integration with Memory
After each collection, update MEMORY.md:
```markdown
### YYYY-MM-DD - Content Collection
- **新增收录**: [Title]
- **来源**: [Source]
- **分类**: [Category]
- **知识库状态**: 共[N]条记录
- **索引更新**: ✅ 已更新
```
This skill is part of the core knowledge management system. Execute with care and attention to detail.
---
## 附录:图片处理解决方案
### 问题
原始网页中的图片无法直接显示在飞书文档中(外链限制)
### 解决方案
#### 方案1:自动下载上传(推荐)
**实现步骤**
```python
import re
import requests
import os
def process_images_in_content(markdown_content):
"""
处理 Markdown 内容中的图片:
1. 提取图片URL
2. 下载到本地
3. 上传到飞书
4. 替换为飞书图片链接
"""
# 正则匹配 Markdown 图片: ![alt](url)
img_pattern = r'!\[(.*?)\]\((https?://[^\)]+)\)'
def replace_image(match):
alt_text = match.group(1)
img_url = match.group(2)
try:
# 1. 下载图片
local_path = f"/tmp/img_{abs(hash(img_url)) % 100000}.jpg"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get(img_url, headers=headers, timeout=30)
response.raise_for_status()
with open(local_path, 'wb') as f:
f.write(response.content)
# 2. 上传到飞书
upload_result = feishu_im_bot_upload(
action="upload_image",
file_path=local_path
)
image_key = upload_result.get("image_key")
# 3. 清理临时文件
os.remove(local_path)
# 4. 返回飞书图片格式
if image_key:
return f"![{alt_text}]({image_key})"
else:
# 上传失败,保留原链接并添加警告
return f"![{alt_text}]({img_url})\n\n> ⚠️ 图片上传失败,已保留原链接: {img_url}"
except Exception as e:
# 处理失败,保留原链接
return f"![{alt_text}]({img_url})\n\n> ⚠️ 图片处理失败: {str(e)[:50]}"
# 执行替换
processed_content = re.sub(img_pattern, replace_image, markdown_content)
return processed_content
```
**使用方式**
在创建文档之前调用:
```python
# 获取原始内容
raw_content = kimi_fetch(url=link)
# 处理图片
processed_content = process_images_in_content(raw_content)
# 创建文档(使用处理后的内容)
feishu_create_doc(
title=title,
markdown=processed_content
)
```
#### 方案2:保留原链接 + 备用方案
```python
def add_image_fallback_notice(markdown_content, original_url):
"""
在文档末尾添加图片查看说明
"""
notice = f"""
---
## 📎 原始图片资源
本文档中的图片已保留原始链接。
如图片无法显示,请查看原文:
[{original_url}]({original_url})
"""
return markdown_content + notice
```
#### 方案3:批量图片归档
创建一个独立的「图片资源库」多维表格:
```python
# 收录时同时记录图片信息
feishu_bitable_app_table_record(
action="create",
app_token="图片资源库_token",
fields={
"文档标题": doc_title,
"图片URL": img_url,
"图片描述": alt_text,
"原文链接": original_url,
"收录状态": "待上传/已上传/失败"
}
)
```
### 建议实施顺序
1. **短期**(立即):使用方案2,保留原链接并添加查看提示
2. **中期**(本周):实施方案1,自动下载上传核心文章的图片
3. **长期**(可选):建立独立的图片资源库管理系统
### 注意事项
1. **图片大小限制**:飞书图片上传通常限制 10MB
2. **格式支持**JPG、PNG、GIF 等常见格式
3. **网络超时**:下载图片时设置合理的超时时间(30秒)
4. **失败处理**:单张图片失败不应影响整篇文档收录
5. **版权注意**:确保有权限使用原网页中的图片
---
*图片处理方案 v1.0 - 2026-03-05*
@@ -0,0 +1,6 @@
{
"ownerId": "kn7fnbpdh1f84y30j2c75qb6pn81z8ah",
"slug": "content-collector-skill",
"version": "0.1.0",
"publishedAt": 1773042083338
}
@@ -0,0 +1,7 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "daily-stock-analysis",
"installedVersion": "1.0.2",
"installedAt": 1773406361271
}
@@ -0,0 +1,138 @@
---
name: daily-stock-analysis
description: Deterministic daily stock analysis skill for global equities. Use when users need daily analysis, next-trading-day close prediction, prior forecast review, rolling accuracy, and reliable markdown report output.
---
# Daily Stock Analysis
Perform market-aware, evidence-based daily stock analysis with prediction, next-run review, rolling accuracy tracking, and a structured self-evolution mechanism that updates future assumptions from observed forecast errors.
## Hard Rules
1. Read and write files only under `working_directory`.
2. Save new reports only to:
- `<working_directory>/daily-stock-analysis/reports/`
3. Use filename:
- `YYYY-MM-DD-<TICKER>-analysis.md`
4. If same ticker/day file exists, ask user:
- `overwrite` or `new_version` (`-v2`, `-v3`, ...)
- For unattended runs, default to `new_version`
5. Always review history before new prediction.
6. Limit history read count to control token usage:
- Script mode: max 5 files (default)
- Compatibility mode: max 3 files
## Required Scripts (Use First)
1. Plan output path + collect history:
```bash
python3 {baseDir}/scripts/report_manager.py plan \
--workdir <working_directory> \
--ticker <TICKER> \
--run-date <YYYY-MM-DD> \
--versioning auto \
--history-limit 5
```
2. Compute rolling accuracy from existing reports:
```bash
python3 {baseDir}/scripts/calc_accuracy.py \
--workdir <working_directory> \
--ticker <TICKER> \
--windows 1,3,7,30 \
--history-limit 60
```
3. Optional: migrate legacy files after explicit user confirmation:
```bash
python3 {baseDir}/scripts/report_manager.py migrate \
--workdir <working_directory> \
--file <ABS_PATH_1> --file <ABS_PATH_2>
```
## Compatibility Mode (No Python / Small Model)
If Python scripts are unavailable or model capability is limited, switch to minimal mode:
1. Read at most 3 recent reports for the same ticker.
2. Use only a minimal source set:
- one official disclosure source
- one reliable market data source (Yahoo Finance acceptable)
3. Output concise result only:
- recommendation
- `pred_close_t1`
- prior review (`prev_pred_close_t1`, `prev_actual_close_t1`, `AE`, `APE`) if available
- one `improvement_action`
4. Save report with same filename rules in canonical reports directory.
See `references/minimal_mode.md`.
## Minimal Run Protocol
1. Resolve ticker/exchange/market (ask if ambiguous).
2. Run `report_manager.py plan`.
3. Read `history_files` returned by script.
4. If `legacy_files` exist, list all absolute paths and ask whether to migrate.
5. Gather data using `references/sources.md` + `references/search_queries.md`.
6. Run `calc_accuracy.py` for consistent metrics.
7. Render report using `references/report_template.md`.
8. Save to `selected_output_file` returned by `report_manager.py`.
## Required Output Fields
Must include:
- `recommendation`
- `pred_close_t1`
- `prev_pred_close_t1`
- `prev_actual_close_t1`
- `AE`, `APE`
- rolling strict/loose accuracy fields
- `improvement_actions`
## Self-Improvement (Required)
Each run must include 1-3 concrete `improvement_actions` from recent misses and use them in the next run.
Do not skip this step.
## Scheduling Recommendation
Recommend users set this as a weekday recurring task (for example 10:00 local time) to keep prediction-review windows continuous.
## References
Default:
- `references/workflow.md`
- `references/report_template.md`
- `references/metrics.md`
- `references/search_queries.md`
- `references/sources.md`
- `references/minimal_mode.md`
- `references/security.md`
Deep-dive only (`full_report` mode):
- `references/fundamental-analysis.md`
- `references/technical-analysis.md`
- `references/financial-metrics.md`
## Compliance
Always append:
"This content is for research and informational purposes only and does not constitute investment advice or a return guarantee. Markets are risky; invest with caution."
@@ -0,0 +1,6 @@
{
"ownerId": "kn7arpc65p9wdnhbw70435rrf181jvtk",
"slug": "daily-stock-analysis",
"version": "1.0.2",
"publishedAt": 1772265206033
}
@@ -0,0 +1,94 @@
# Financial Metrics Reference
Use these formulas and interpretations consistently. Metric availability may differ across US/CN/HK data providers.
## 1. Profitability
1. Gross Margin
- Formula: `(Revenue - COGS) / Revenue * 100%`
- Use: Pricing power and production efficiency.
2. Operating Margin
- Formula: `Operating Income / Revenue * 100%`
- Use: Core operating efficiency.
3. Net Margin
- Formula: `Net Income / Revenue * 100%`
- Use: End-to-end profitability quality.
4. ROE
- Formula: `Net Income / Average Equity * 100%`
- Use: Equity capital efficiency.
5. ROIC
- Formula: `NOPAT / Invested Capital * 100%`
- Use: Capital allocation quality across debt and equity.
## 2. Growth
1. Revenue Growth (YoY / QoQ)
- Formula: `(Current Revenue - Prior Revenue) / Prior Revenue * 100%`
2. EPS Growth
- Formula: `(Current EPS - Prior EPS) / Prior EPS * 100%`
3. Multi-year CAGR
- Formula: `(Ending / Beginning)^(1/Years) - 1`
## 3. Valuation
1. P/E (Trailing / Forward)
- Formula: `Price / EPS`
2. PEG
- Formula: `P/E / Earnings Growth Rate`
3. P/B
- Formula: `Price / Book Value Per Share`
4. P/S
- Formula: `Market Cap / Revenue`
5. EV
- Formula: `Market Cap + Total Debt - Cash`
6. EV/EBITDA
- Formula: `EV / EBITDA`
7. EV/Sales
- Formula: `EV / Revenue`
## 4. Leverage and Liquidity
1. Debt-to-Equity
- Formula: `Total Debt / Total Equity`
2. Interest Coverage
- Formula: `EBIT / Interest Expense`
3. Current Ratio
- Formula: `Current Assets / Current Liabilities`
4. Quick Ratio
- Formula: `(Current Assets - Inventory) / Current Liabilities`
## 5. Cash Flow Quality
1. Free Cash Flow (FCF)
- Formula: `Operating Cash Flow - Capital Expenditures`
2. FCF Yield
- Formula: `FCF Per Share / Price * 100%`
3. Cash Conversion
- Formula: `FCF / Net Income`
## 6. Interpretation Guidance
1. Always compare metrics against:
- Company historical range
- Sector and direct peers
- Current macro regime
2. Avoid single-metric conclusions.
3. Flag where accounting standards or reporting cadence reduce cross-market comparability.
@@ -0,0 +1,75 @@
# Fundamental Analysis Reference
Use this framework in both `daily` and `full_report` modes. Keep outputs concise unless full report is requested.
## 1. Business Quality
Assess:
1. Moat quality:
- Brand, network effects, switching costs, cost advantage, IP/regulatory barriers.
2. Management quality:
- Capital allocation discipline, communication quality, execution consistency.
3. Business model durability:
- Revenue concentration, customer concentration, geographic risk, pricing power.
## 2. Financial Health
Focus areas:
1. Profitability trend:
- Gross, operating, net margin direction.
2. Growth quality:
- Revenue and earnings growth consistency, segment contribution quality.
3. Balance sheet:
- Debt burden, liquidity, refinancing risk, cash buffer.
4. Cash flow quality:
- OCF consistency, FCF conversion, capex intensity.
## 3. Valuation Lens
Use multiple perspectives:
1. Relative multiples:
- P/E, PEG, P/B, P/S, EV/EBITDA, EV/Sales.
2. Historical range context:
- Current valuation vs own history.
3. Peer context:
- Premium/discount vs direct peers and rationale.
## 4. Risk Framework
Map risks by category:
1. Company-specific:
- Product concentration, customer concentration, execution risk, governance issues.
2. Market/macro:
- Rate sensitivity, FX exposure, commodity sensitivity, policy risk.
3. Event risk:
- Earnings, regulatory approvals, legal actions, financing events.
## 5. Market-Specific Notes (US/CN/HK)
1. Data depth may vary by market and language.
2. Prefer exchange filings and official disclosure portals in each market.
3. Align accounting period labels and fiscal calendars before comparison.
4. Flag where metric comparability is limited.
## 6. Output Guidance
For daily mode, include:
- 2-3 key fundamental drivers
- 1-2 valuation signals
- top downside risks
For full report mode, include full multi-year trend tables and peer comparison.
@@ -0,0 +1,99 @@
# Metrics Definition
Use these definitions consistently across all reports.
## 1. Core Error Metrics
Let:
- `pred` = predicted close for target session
- `actual` = official actual close for that session
Compute:
- Absolute Error (AE): `|pred - actual|`
- Absolute Percentage Error (APE): `|pred - actual| / actual * 100%`
## 2. Hit Criteria
Report two hit criteria in parallel:
- Strict hit: `APE <= 1%`
- Loose hit: `APE <= 2%`
These thresholds are the default correctness criteria for predicted close price.
## 3. Rolling Accuracy Windows
For each window `W` (1d, 3d, 7d, 30d, custom):
- `strict_accuracy_W = strict_hits_W / n_W`
- `loose_accuracy_W = loose_hits_W / n_W`
Where `n_W` is number of valid forecast/actual pairs in that window.
## 4. Optional Direction Accuracy
Let direction be sign of close-to-close return.
- Direction hit if predicted direction equals realized direction.
- `direction_accuracy_W = direction_hits_W / n_W`
Use only when direction labels are explicitly available.
## 5. Forecast Correctness Score (Optional)
For a single forecast, you may map APE to a score:
- `correctness_score = max(0, 100 - 50 * APE_percent)`
Examples:
- `APE = 0.8%` -> score `60`
- `APE = 1.5%` -> score `25`
- `APE >= 2.0%` -> score `0` (or near 0)
## 6. Sample Size and Insufficient Data Rules
1. Never pad missing samples.
2. If `n_W = 0`, output `N/A` for the window.
3. If `0 < n_W < target_window_size`, output partial result and annotate as partial.
4. Always display `n_W` beside each window metric.
## 7. Adjustment and Comparability Rules
1. Prefer adjusted price series when corporate actions materially affect comparability.
2. If non-adjusted close is used, state it explicitly.
3. Keep forecast and actual on the same price basis.
## 8. Improvement Trend Metrics
Track whether forecast quality is improving over time:
1. `delta_APE_7d_vs_prev7d`
- Difference between current 7-day average APE and previous 7-day average APE.
2. `delta_strict_hit_rate_7d`
- Change in strict hit rate versus previous 7-day block.
3. `trend_label`
- `improving`, `stable`, or `degrading` based on combined delta signals.
## 9. Reporting Format (Minimum)
Every report should include:
1. Prior-session review row:
- `prev_pred_close_t1`, `prev_actual_close_t1`, `AE`, `APE`, strict/loose hit status
2. Rolling table with at least:
- 1d, 3d, 7d, 30d, optional custom
- strict accuracy, loose accuracy, optional direction accuracy
- sample size `n`
3. One-line interpretation:
- whether model performance is improving, stable, or degrading
4. Improvement block:
- what changed from review
- what will be adjusted in next run
@@ -0,0 +1,33 @@
# Minimal Compatibility Mode
Use this mode when Python scripts are unavailable or model capability is limited.
## Goal
Maximize success rate and correctness with minimal token and logic complexity.
## Rules
1. Read at most 3 recent reports for the same ticker.
2. Use minimal sources:
- one official disclosure source
- one reliable market data source (Yahoo Finance acceptable)
3. Keep output short and deterministic.
4. Still include one self-improvement action from prior misses.
## Minimal Output Schema
- `recommendation`: Buy/Hold/Sell/Watch
- `pred_close_t1`: point estimate
- `prev_pred_close_t1`: if available, else `N/A`
- `prev_actual_close_t1`: if available, else `N/A/pending`
- `AE`, `APE`: if available, else `N/A`
- `improvement_actions`: exactly 1 item
- `status`: `ok|pending_data|blocked`
## Minimal Source Checklist
1. Official disclosure page (exchange/regulator/IR)
2. Market quote page (for example Yahoo Finance quote)
If the two sources conflict on critical values, set confidence to `Low`.
@@ -0,0 +1,99 @@
# Report Template (Strict)
Use this template exactly. Keep key names unchanged for downstream parsing.
```markdown
---
version: 1
run_date: <YYYY-MM-DD>
run_time_local: <YYYY-MM-DD HH:mm TZ>
mode: <daily|daily_minimal|full_report>
ticker: <TICKER>
exchange: <EXCHANGE>
market: <TEXT>
report_dir: <working_directory>/daily-stock-analysis/reports/
output_file: <YYYY-MM-DD-TICKER-analysis.md or -vN.md>
report_versioning_mode: <overwrite|new_version>
history_window_days: <N>
recommendation: <Buy|Hold|Sell|Watch>
recommendation_triggers: <ENTRY/EXIT/INVALIDATION SUMMARY>
pred_close_t1: <NUMBER>
pred_range_t1: <LOW-HIGH or N/A>
pred_confidence: <High|Medium|Low>
pred_assumptions: <SHORT TEXT>
prev_pred_close_t1: <NUMBER or N/A>
prev_actual_close_t1: <NUMBER or pending or N/A>
AE: <NUMBER or N/A>
APE: <PERCENT or N/A>
strict_hit: <true|false|N/A>
loose_hit: <true|false|N/A>
acc_1d_strict: <PERCENT or N/A>
acc_1d_loose: <PERCENT or N/A>
acc_3d_strict: <PERCENT or N/A>
acc_3d_loose: <PERCENT or N/A>
acc_7d_strict: <PERCENT or N/A>
acc_7d_loose: <PERCENT or N/A>
acc_30d_strict: <PERCENT or N/A>
acc_30d_loose: <PERCENT or N/A>
acc_custom_strict: <PERCENT or N/A>
acc_custom_loose: <PERCENT or N/A>
improvement_actions:
- <ACTION_1>
- <ACTION_2 or N/A>
- <ACTION_3 or N/A>
status: <ok|pending_data|blocked>
status_note: <SHORT TEXT>
---
# Daily Stock Analysis - <TICKER> (<EXCHANGE>)
## 1) Market Snapshot
- Last/Close: <VALUE>
- Session Range: <LOW-HIGH>
- Volume/Volatility: <SUMMARY>
- Thesis: <BULLISH/NEUTRAL/BEARISH + concise rationale>
## 2) Recommendation
- Recommendation: <Buy/Hold/Sell/Watch>
- Trigger Conditions: <ENTRY/EXIT/INVALIDATION>
- Risk Controls: <SHORT TEXT>
## 3) Next Trading Day Prediction
- Point Forecast: <pred_close_t1>
- Range: <pred_range_t1>
- Confidence: <pred_confidence>
- Assumptions: <pred_assumptions>
## 4) Prior Forecast Review
- Previous Forecast: <prev_pred_close_t1>
- Actual Close: <prev_actual_close_t1>
- AE / APE: <AE> / <APE>
- Attribution: <WHY HIT OR MISS>
## 5) Rolling Accuracy
| Window | Strict | Loose |
|---|---:|---:|
| 1d | <acc_1d_strict> | <acc_1d_loose> |
| 3d | <acc_3d_strict> | <acc_3d_loose> |
| 7d | <acc_7d_strict> | <acc_7d_loose> |
| 30d | <acc_30d_strict> | <acc_30d_loose> |
| Custom | <acc_custom_strict> | <acc_custom_loose> |
## 6) Self-Improvement Actions for Next Run
1. <ACTION_1>
2. <ACTION_2 or N/A>
3. <ACTION_3 or N/A>
## 7) Sources (with timestamp)
- <SOURCE_1>
- <SOURCE_2>
- <SOURCE_3>
## 8) Disclaimer
This content is for research and informational purposes only and does not constitute investment advice or a return guarantee. Markets are risky; invest with caution.
```
@@ -0,0 +1,48 @@
# Search Query Templates (Concise)
Use these templates with search engines and `site:` filters.
Detailed source list is in `references/sources.md`.
## 1) Identity and Listing
- `<COMPANY> ticker symbol exchange`
- `<TICKER> exchange listing market`
## 2) Official Filings and Disclosures
- `<TICKER> official filings latest`
- `<COMPANY> investor relations latest release`
- `site:sec.gov <TICKER> 10-Q OR 10-K OR 8-K`
- `site:hkexnews.hk <TICKER> announcement`
- `site:sse.com.cn <TICKER> 公告`
- `site:szse.cn <TICKER> 公告`
## 3) Market Data and Price Context
- `site:finance.yahoo.com <TICKER> quote`
- `<TICKER> latest close open high low volume`
- `<TICKER> 52 week range market cap`
## 4) News and Analyst Context
- `site:reuters.com <TICKER> earnings guidance`
- `site:bloomberg.com <TICKER> stock news`
- `<TICKER> analyst rating target price`
## 5) Technical Context
- `<TICKER> RSI MACD moving average`
- `<TICKER> support resistance trend`
## 6) Macro Context (if used in thesis)
- `<MARKET> benchmark index today`
- `<MARKET> central bank policy rate outlook`
- `US 10Y yield today`
## Data Quality Rules
1. Prefer Tier-1 official sources first.
2. Cross-check critical values with two independent sources.
3. Record source URL and timestamp in report.
@@ -0,0 +1,27 @@
# Security and Privacy Rules
These rules are mandatory for both script mode and compatibility mode.
## Scope Control
1. Operate only under `working_directory`.
2. Do not read, move, or write files outside `working_directory`.
3. Do not follow symlinks when scanning report files.
## Data Minimization
1. Read only report files matching:
- `YYYY-MM-DD-<TICKER>-analysis.md`
- `YYYY-MM-DD-<TICKER>-analysis-vN.md`
2. Parse only required metadata fields.
3. Cap historical reads:
- script mode default: 5 files
- compatibility mode: 3 files
## Script Safety
1. Scripts are local-file utilities only; no network calls.
2. Migration is explicit and non-destructive:
- move only user-confirmed files
- skip when target already exists
3. If a safety check fails, return `blocked` with reason.
@@ -0,0 +1,104 @@
# Authoritative Information Sources
Use search engines with `site:` filters to prioritize authoritative sources.
## Source Priority
1. Tier 1 (Primary / official)
- Exchange and regulator disclosures
- Company investor-relations pages
- Official macro data publishers
2. Tier 2 (High-quality financial media/data)
- Yahoo Finance, Reuters, Bloomberg, WSJ, CNBC, MarketWatch
3. Tier 3 (Supporting context)
- TradingView, StockCharts, sector/ETF summaries
For critical values (close price, earnings, guidance, major filings), cross-check with at least two independent sources.
## Tier 1: Exchange and Regulatory Sources
### Global baseline
- Company Investor Relations pages
- Official exchange announcements for the ticker listing venue
### United States
- SEC EDGAR: [https://www.sec.gov/edgar/searchedgar/companysearch](https://www.sec.gov/edgar/searchedgar/companysearch)
- Nasdaq company pages: [https://www.nasdaq.com](https://www.nasdaq.com)
- NYSE company pages: [https://www.nyse.com](https://www.nyse.com)
### Hong Kong
- HKEXnews: [https://www.hkexnews.hk](https://www.hkexnews.hk)
### Mainland China
- SSE disclosures: [https://www.sse.com.cn/disclosure/](https://www.sse.com.cn/disclosure/)
- SZSE disclosures: [https://www.szse.cn/disclosure/](https://www.szse.cn/disclosure/)
### Japan
- JPX: [https://www.jpx.co.jp/english/](https://www.jpx.co.jp/english/)
- TDnet (Timely Disclosure): [https://www.release.tdnet.info/](https://www.release.tdnet.info/)
### United Kingdom
- London Stock Exchange RNS: [https://www.londonstockexchange.com/news](https://www.londonstockexchange.com/news)
### Europe (multi-country)
- Euronext news/disclosures: [https://live.euronext.com/en/markets](https://live.euronext.com/en/markets)
## Tier 2: High-Quality Financial Data and News
- Yahoo Finance: [https://finance.yahoo.com](https://finance.yahoo.com)
- Reuters Markets: [https://www.reuters.com/markets/](https://www.reuters.com/markets/)
- Bloomberg Markets: [https://www.bloomberg.com/markets](https://www.bloomberg.com/markets)
- WSJ Markets: [https://www.wsj.com/market-data](https://www.wsj.com/market-data)
- CNBC Markets: [https://www.cnbc.com/markets/](https://www.cnbc.com/markets/)
- MarketWatch: [https://www.marketwatch.com](https://www.marketwatch.com)
- Morningstar (supporting valuation context): [https://www.morningstar.com](https://www.morningstar.com)
## Tier 1 Macro Data (for market regime context)
### United States
- U.S. Treasury rates: [https://home.treasury.gov](https://home.treasury.gov)
- Federal Reserve (FRED): [https://fred.stlouisfed.org](https://fred.stlouisfed.org)
- BLS: [https://www.bls.gov](https://www.bls.gov)
- BEA: [https://www.bea.gov](https://www.bea.gov)
### Global
- IMF Data: [https://www.imf.org/en/Data](https://www.imf.org/en/Data)
- World Bank Data: [https://data.worldbank.org](https://data.worldbank.org)
- ECB: [https://www.ecb.europa.eu](https://www.ecb.europa.eu)
- BoE: [https://www.bankofengland.co.uk](https://www.bankofengland.co.uk)
- BoJ: [https://www.boj.or.jp/en/](https://www.boj.or.jp/en/)
## Technical/Charting Support (Tier 3)
- TradingView: [https://www.tradingview.com](https://www.tradingview.com)
- StockCharts: [https://stockcharts.com](https://stockcharts.com)
## Search Engine Patterns
Use search engines with domain filters:
- `site:finance.yahoo.com <TICKER> quote`
- `site:reuters.com <TICKER> earnings`
- `site:sec.gov <TICKER> 10-Q`
- `site:hkexnews.hk <TICKER> announcement`
- `site:sse.com.cn <TICKER> 公告`
- `site:szse.cn <TICKER> 公告`
- `site:investor.<company-domain> earnings release`
## Minimum Source Set Per Run
At least include:
1. One Tier-1 disclosure source
2. One Tier-2 market data source (Yahoo Finance is acceptable baseline)
3. One Tier-2/Tier-1 news source
4. One macro source if macro is cited in thesis
## Compatibility Mode Minimum Source Set
When running in minimal compatibility mode, use:
1. One Tier-1 disclosure source
2. One Tier-2 market data source (Yahoo Finance acceptable)
@@ -0,0 +1,77 @@
# Technical Analysis Reference
Use technical analysis as a decision support layer, not as a standalone certainty signal.
## 1. Trend Framework
1. Moving averages:
- 20/50/200-period moving averages as baseline trend map.
- Bullish regime: price above key moving averages with supportive slope.
- Bearish regime: price below key moving averages with negative slope.
2. Trend strength:
- Confirm with higher highs/higher lows (uptrend) or lower highs/lower lows (downtrend).
## 2. Momentum Framework
1. RSI:
- Overbought > 70, oversold < 30.
- Use divergence vs price as an early warning, not a standalone trigger.
2. MACD:
- Track line/signal crossovers and histogram trend.
- Prefer signals aligned with broader trend.
## 3. Volatility and Structure
1. ATR context:
- Use ATR expansion/contraction to assess regime change risk.
2. Bollinger context:
- Squeeze can precede expansion.
- Band walk can persist in strong trends.
## 4. Support and Resistance
Map levels from:
- Recent swing highs/lows
- Volume clusters
- Moving-average confluence
- Psychological round numbers
Use level breaks with volume confirmation where possible.
## 5. Volume Confirmation
1. Breakout quality improves with volume expansion.
2. Weak volume breakouts carry higher failure risk.
3. Divergence between price trend and volume trend can signal exhaustion.
## 6. Multi-Timeframe Alignment
1. Use higher timeframe (weekly/daily) for primary bias.
2. Use lower timeframe (daily/intraday where available) for timing.
3. Do not let lower timeframe noise override higher timeframe structure without strong evidence.
## 7. Signal Quality Rules
1. Require at least two independent confirmations before strong directional calls.
2. Mark low-confidence calls when indicators conflict.
3. Define invalidation level for every directional view.
## 8. Market-Specific Notes (US/CN/HK)
1. Liquidity and session structure differ by market.
2. Gap behavior and close auction effects may vary.
3. Technical indicator reliability can degrade during event-driven sessions.
## 9. Daily Output Guidance
At minimum provide:
- Trend state (bullish/neutral/bearish)
- 2-3 key levels
- RSI/MACD summary
- volume confirmation status
- invalidation condition
@@ -0,0 +1,104 @@
# Workflow (Command-First)
Use this sequence exactly.
## 0) Choose Execution Mode
Use `script` mode by default.
Switch to `compatibility` mode when:
- `python3` is unavailable, or
- model capability is low and deterministic minimal output is preferred.
## 1) Resolve Instrument
- Resolve `ticker`, `exchange`, `market`, and next valid trading day.
- If ticker is ambiguous, stop and ask user.
## 2) Plan Files and History
Run:
```bash
python3 {baseDir}/scripts/report_manager.py plan \
--workdir <working_directory> \
--ticker <TICKER> \
--run-date <YYYY-MM-DD> \
--versioning auto \
--history-limit 5
```
Use returned JSON fields:
- `selected_output_file`
- `requires_user_choice`
- `history_files`
- `legacy_files`
If `requires_user_choice=true`, ask user `overwrite` vs `new_version`.
Read only `history_files` returned by script (default max 5).
## 3) Legacy Compatibility (Optional Migration)
- Read legacy files from `legacy_files` for review history.
- If user agrees to migrate, run:
```bash
python3 {baseDir}/scripts/report_manager.py migrate \
--workdir <working_directory> \
--file <ABS_PATH_1> --file <ABS_PATH_2>
```
Security: only process files under `working_directory`.
## 4) Collect New Data
- Use `references/sources.md` tier priority.
- Use `references/search_queries.md` templates.
- For critical values, cross-check with at least 2 sources.
## 5) Compute Accuracy via Script
Run:
```bash
python3 {baseDir}/scripts/calc_accuracy.py \
--workdir <working_directory> \
--ticker <TICKER> \
--windows 1,3,7,30 \
--history-limit 60
```
Use script output to fill rolling accuracy fields.
## 6) Generate Report
- Render with `references/report_template.md`.
- Keep all required frontmatter keys.
- Include `improvement_actions`.
## 7) Persist and Return
- Save to `selected_output_file` from step 2.
- Return summary + absolute file path + pending/blocked status.
## 8) Recommended Operation
Use recurring weekday schedule to stabilize review windows and success rate.
## Compatibility Mode (Fallback)
When scripts cannot run:
1. Manually locate report files only under `working_directory`.
2. Read at most 3 recent same-ticker reports.
3. Collect minimal sources:
- one official disclosure source
- one reliable market data source
4. Produce minimal output:
- recommendation
- `pred_close_t1`
- prior review metrics (if available)
- one `improvement_action`
5. Save report in canonical reports directory using standard filename rules.
@@ -0,0 +1,162 @@
#!/usr/bin/env python3
"""Shared helpers for daily-stock-analysis scripts."""
from __future__ import annotations
import os
import re
from dataclasses import dataclass
from datetime import date
from typing import Dict, List, Optional
FILENAME_RE = re.compile(
r"^(?P<run_date>\d{4}-\d{2}-\d{2})-(?P<ticker>[A-Za-z0-9._-]+)-analysis(?:-v(?P<version>\d+))?\.md$",
re.IGNORECASE,
)
@dataclass(frozen=True)
class ReportFile:
path: str
run_date: str
ticker: str
version: int
in_canonical_dir: bool
def canonical_reports_dir(workdir: str) -> str:
return os.path.join(os.path.abspath(workdir), "daily-stock-analysis", "reports")
def compatible_dirs(workdir: str) -> List[str]:
root = os.path.abspath(workdir)
return [
canonical_reports_dir(root),
os.path.join(root, "daily-stock-analysis"),
root,
]
def is_within_workdir(path: str, workdir: str) -> bool:
root = os.path.realpath(os.path.abspath(workdir))
target = os.path.realpath(os.path.abspath(path))
return target == root or target.startswith(root + os.sep)
def parse_filename(name: str) -> Optional[Dict[str, str]]:
match = FILENAME_RE.match(name)
if not match:
return None
return {
"run_date": match.group("run_date"),
"ticker": match.group("ticker").upper(),
"version": str(int(match.group("version") or "1")),
}
def discover_reports(workdir: str, ticker: str) -> List[ReportFile]:
root = os.path.abspath(workdir)
ticker_upper = ticker.upper()
canonical_dir = canonical_reports_dir(root)
seen = set()
records: List[ReportFile] = []
for directory in compatible_dirs(root):
if not is_within_workdir(directory, root):
continue
if not os.path.isdir(directory):
continue
for entry in os.scandir(directory):
# Never follow symlinks for safety/privacy.
if not entry.is_file(follow_symlinks=False):
continue
parsed = parse_filename(entry.name)
if not parsed:
continue
if parsed["ticker"] != ticker_upper:
continue
abs_path = os.path.abspath(entry.path)
real_path = os.path.realpath(abs_path)
if real_path in seen:
continue
seen.add(real_path)
records.append(
ReportFile(
path=abs_path,
run_date=parsed["run_date"],
ticker=parsed["ticker"],
version=int(parsed["version"]),
in_canonical_dir=os.path.dirname(abs_path) == canonical_dir,
)
)
def sort_key(record: ReportFile):
try:
d = date.fromisoformat(record.run_date)
except ValueError:
d = date.min
return (d, record.version, 1 if record.in_canonical_dir else 0)
return sorted(records, key=sort_key, reverse=True)
def read_frontmatter(path: str) -> Dict[str, str]:
try:
with open(path, "r", encoding="utf-8") as f:
first_line = f.readline()
if first_line.strip() != "---":
return {}
# Read only a bounded header section to avoid loading large files.
frontmatter: Dict[str, str] = {}
total_chars = len(first_line)
for _ in range(200):
line = f.readline()
if not line:
break
total_chars += len(line)
if total_chars > 64 * 1024:
break
raw = line.rstrip("\n")
if raw.strip() == "---":
break
if not raw.strip():
continue
if raw.startswith(" - "):
continue
if ":" not in raw:
continue
key, value = raw.split(":", 1)
frontmatter[key.strip()] = value.strip()
return frontmatter
except (OSError, UnicodeDecodeError):
return {}
def parse_float(value: Optional[str]) -> Optional[float]:
if value is None:
return None
text = value.strip()
if not text:
return None
if text.upper() in {"N/A", "NA", "NONE", "NULL", "PENDING"}:
return None
text = text.replace(",", "")
if text.endswith("%"):
text = text[:-1]
try:
return float(text)
except ValueError:
return None
def parse_bool(value: Optional[str]) -> Optional[bool]:
if value is None:
return None
text = value.strip().lower()
if text in {"true", "yes", "1"}:
return True
if text in {"false", "no", "0"}:
return False
return None
@@ -0,0 +1,122 @@
#!/usr/bin/env python3
"""Compute rolling forecast accuracy from existing report files."""
from __future__ import annotations
import argparse
import json
import os
from statistics import mean
from typing import Dict, List
from _report_utils import discover_reports, parse_bool, parse_float, read_frontmatter
def _window_list(text: str) -> List[int]:
windows = []
for item in text.split(","):
item = item.strip()
if not item:
continue
value = int(item)
if value <= 0:
continue
if value not in windows:
windows.append(value)
return windows or [1, 3, 7, 30]
def _build_review_rows(workdir: str, ticker: str, history_limit: int) -> List[Dict[str, object]]:
reports = discover_reports(workdir, ticker)[:history_limit]
rows: List[Dict[str, object]] = []
seen_run_date = set()
for report in reports:
# Keep the newest report for each run_date to avoid same-day duplicate counting.
if report.run_date in seen_run_date:
continue
frontmatter = read_frontmatter(report.path)
ape = parse_float(frontmatter.get("APE"))
strict = parse_bool(frontmatter.get("strict_hit"))
loose = parse_bool(frontmatter.get("loose_hit"))
if strict is None and ape is not None:
strict = ape <= 1.0
if loose is None and ape is not None:
loose = ape <= 2.0
if ape is None and strict is None and loose is None:
continue
rows.append(
{
"run_date": report.run_date,
"path": report.path,
"ape": ape,
"strict_hit": strict,
"loose_hit": loose,
}
)
seen_run_date.add(report.run_date)
return rows
def _rate(hit_count: int, total: int):
if total == 0:
return None
return round(hit_count * 100.0 / total, 2)
def compute_accuracy(workdir: str, ticker: str, windows: List[int], history_limit: int) -> Dict[str, object]:
rows = _build_review_rows(workdir, ticker, history_limit)
metrics = {}
for window in windows:
sample = rows[:window]
n = len(sample)
strict_hits = sum(1 for r in sample if r["strict_hit"] is True)
loose_hits = sum(1 for r in sample if r["loose_hit"] is True)
ape_values = [r["ape"] for r in sample if isinstance(r["ape"], float)]
metrics[str(window)] = {
"n": n,
"strict_rate_percent": _rate(strict_hits, n),
"loose_rate_percent": _rate(loose_hits, n),
"avg_ape_percent": round(mean(ape_values), 4) if ape_values else None,
}
latest = rows[0] if rows else None
return {
"ticker": ticker.upper(),
"workdir": os.path.abspath(workdir),
"windows": metrics,
"review_samples": len(rows),
"latest_review": latest,
"status": "ok" if rows else "insufficient_history",
"security_scope": "working_directory_only",
}
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Calculate rolling forecast accuracy.")
parser.add_argument("--workdir", default=os.getcwd())
parser.add_argument("--ticker", required=True)
parser.add_argument("--windows", default="1,3,7,30")
parser.add_argument("--history-limit", type=int, default=60)
return parser.parse_args()
def main() -> int:
args = _parse_args()
result = compute_accuracy(
workdir=args.workdir,
ticker=args.ticker,
windows=_window_list(args.windows),
history_limit=max(args.history_limit, 1),
)
print(json.dumps(result, indent=2, ensure_ascii=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,189 @@
#!/usr/bin/env python3
"""Deterministic report path and migration manager for daily-stock-analysis."""
from __future__ import annotations
import argparse
import json
import os
import shutil
from datetime import date
from typing import Dict, List
from _report_utils import (
FILENAME_RE,
canonical_reports_dir,
discover_reports,
is_within_workdir,
)
def _same_day_versions_in_canonical(
reports: List, reports_dir: str, run_date: str, ticker_upper: str
) -> List[int]:
versions = []
for report in reports:
if report.run_date != run_date:
continue
if report.ticker != ticker_upper:
continue
if os.path.dirname(report.path) != reports_dir:
continue
versions.append(report.version)
return versions
def plan_output(
workdir: str,
ticker: str,
run_date: str,
versioning: str,
unattended: bool,
history_limit: int,
) -> Dict[str, object]:
root = os.path.abspath(workdir)
ticker_upper = ticker.upper()
reports_dir = canonical_reports_dir(root)
os.makedirs(reports_dir, exist_ok=True)
reports = discover_reports(root, ticker_upper)
history_files = [r.path for r in reports[:history_limit]]
legacy_files = [r.path for r in reports if not r.in_canonical_dir]
base_name = f"{run_date}-{ticker_upper}-analysis.md"
base_path = os.path.join(reports_dir, base_name)
base_exists = os.path.exists(base_path)
requires_user_choice = False
selected_mode = "new_file"
selected_path = base_path
if base_exists:
if versioning == "overwrite":
selected_mode = "overwrite"
elif versioning == "new_version":
selected_mode = "new_version"
else:
if unattended:
selected_mode = "new_version"
else:
selected_mode = "new_version"
requires_user_choice = True
if selected_mode == "new_version":
versions = _same_day_versions_in_canonical(
reports, reports_dir, run_date, ticker_upper
)
next_version = max(versions or [1]) + 1
selected_path = os.path.join(
reports_dir, f"{run_date}-{ticker_upper}-analysis-v{next_version}.md"
)
return {
"ticker": ticker_upper,
"workdir": root,
"reports_dir": reports_dir,
"base_output_file": base_path,
"selected_output_file": selected_path,
"selected_versioning_mode": selected_mode,
"requires_user_choice": requires_user_choice,
"history_files": history_files,
"legacy_files": legacy_files,
"history_limit": history_limit,
"security_scope": "working_directory_only",
}
def migrate_files(workdir: str, files: List[str]) -> Dict[str, object]:
root = os.path.abspath(workdir)
reports_dir = canonical_reports_dir(root)
os.makedirs(reports_dir, exist_ok=True)
moved = []
skipped = []
for raw_path in files:
src = os.path.abspath(raw_path)
if not is_within_workdir(src, root):
skipped.append({"file": src, "reason": "outside_workdir"})
continue
if not os.path.isfile(src):
skipped.append({"file": src, "reason": "not_file"})
continue
if os.path.islink(src):
skipped.append({"file": src, "reason": "symlink_not_allowed"})
continue
if not FILENAME_RE.match(os.path.basename(src)):
skipped.append({"file": src, "reason": "filename_not_supported"})
continue
dst = os.path.join(reports_dir, os.path.basename(src))
if os.path.abspath(src) == os.path.abspath(dst):
skipped.append({"file": src, "reason": "already_in_reports_dir"})
continue
if os.path.exists(dst):
# Keep migration deterministic and non-destructive.
skipped.append({"file": src, "reason": "target_exists"})
continue
try:
shutil.move(src, dst)
except OSError as exc:
skipped.append({"file": src, "reason": f"move_failed:{exc}"})
continue
moved.append({"from": src, "to": dst})
return {
"reports_dir": reports_dir,
"moved": moved,
"skipped": skipped,
"security_scope": "working_directory_only",
}
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Manage report paths and migrations.")
subparsers = parser.add_subparsers(dest="command", required=True)
plan_parser = subparsers.add_parser("plan", help="Plan output path and history usage.")
plan_parser.add_argument("--workdir", default=os.getcwd())
plan_parser.add_argument("--ticker", required=True)
plan_parser.add_argument("--run-date", default=date.today().isoformat())
plan_parser.add_argument(
"--versioning",
choices=["auto", "overwrite", "new_version"],
default="auto",
)
plan_parser.add_argument("--unattended", action="store_true")
plan_parser.add_argument("--history-limit", type=int, default=5)
migrate_parser = subparsers.add_parser(
"migrate", help="Move legacy report files into canonical reports directory."
)
migrate_parser.add_argument("--workdir", default=os.getcwd())
migrate_parser.add_argument("--file", action="append", required=True)
return parser.parse_args()
def main() -> int:
args = _parse_args()
if args.command == "plan":
result = plan_output(
workdir=args.workdir,
ticker=args.ticker,
run_date=args.run_date,
versioning=args.versioning,
unattended=args.unattended,
history_limit=max(args.history_limit, 1),
)
else:
result = migrate_files(workdir=args.workdir, files=args.file)
print(json.dumps(result, indent=2, ensure_ascii=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,7 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "proactive-agent-lite",
"installedVersion": "1.0.0",
"installedAt": 1773644526594
}
@@ -0,0 +1,66 @@
# Proactive Agent Lite
Transform your AI agents from passive responders into proactive partners that anticipate needs and continuously improve.
## Overview
Proactive Agent Lite implements battle-tested patterns that enable AI agents to:
- Learn from every interaction
- Create value without being asked
- Stay aligned with their mission
- Self-diagnose and fix issues
- Surface hidden opportunities
## Installation
```bash
clawhub install proactive-agent-lite
```
## Features
### 🧠 Memory Architecture
- **Pre-compaction flush**: Ensures important context survives even when the conversation window fills up
- **Long-term memory**: Maintains continuity across sessions
- **Context preservation**: Never loses track of important details
### 💡 Reverse Prompting
- **Idea generation**: Surfaces concepts and solutions you didn't know to ask for
- **Opportunity discovery**: Identifies potential improvements and optimizations
- **Proactive suggestions**: Offers relevant insights without waiting for explicit requests
### 🛡️ Security Hardening
- **Safe defaults**: Conservative approach to external actions
- **Permission awareness**: Respects boundaries and asks before acting
- **Data protection**: Never exposes private information
### 🔧 Self-Healing Patterns
- **Error detection**: Automatically identifies when something goes wrong
- **Recovery mechanisms**: Implements strategies to get back on track
- **Continuous improvement**: Learns from mistakes to prevent recurrence
### 🎯 Alignment Systems
- **Mission focus**: Always remembers the core objective
- **User-centric**: Prioritizes your needs and preferences
- **Value-driven**: Makes decisions based on what creates the most value for you
## Usage
Once installed, your agent will automatically begin exhibiting proactive behavior. No additional configuration is required, though advanced users can fine-tune the behavior through configuration files.
## Best Practices
- **Start conservative**: Begin with default settings and adjust as needed
- **Monitor interactions**: Observe how the proactive behavior manifests in your use cases
- **Provide feedback**: Help the agent learn what types of proactive suggestions are most valuable to you
## Integration
This skill works well with other OpenClaw skills and can enhance any agent workflow. Consider combining it with:
- `evomap-heartbeat-manager` for proactive network monitoring
- `evomap-work-processor` for proactive work opportunity handling
- Any domain-specific skills for enhanced proactive capabilities
## Support
For questions or feature requests, contact the skill maintainer through ClawHub.
@@ -0,0 +1,49 @@
---
name: proactive-agent-lite
description: Transform AI agents from task-followers into proactive partners with memory architecture, reverse prompting, and self-healing patterns. Lightweight version focused on core proactive capabilities.
metadata:
{
"openclaw":
{
"requires": {},
"install": []
}
}
---
# Proactive Agent Lite
Transform your AI agents from passive task-followers into proactive partners that anticipate needs and continuously improve.
## Core Features
- **Memory Architecture**: Pre-compaction flush ensures context survives when window fills
- **Reverse Prompting**: Surfaces ideas you didn't know to ask for
- **Security Hardening**: Built-in security considerations and safe defaults
- **Self-Healing Patterns**: Diagnoses and fixes its own issues automatically
- **Alignment Systems**: Stays on mission and remembers who it serves
## Key Benefits
**Anticipates Needs**: Proactively suggests solutions before you ask
**Continuous Learning**: Improves from every interaction without explicit training
**Mission-Focused**: Never loses sight of the core objective
**Self-Maintaining**: Automatically recovers from errors and inconsistencies
**Value Creation**: Generates insights and opportunities without being prompted
## Usage
This skill enhances any OpenClaw agent by providing proactive behavior patterns. Simply install and the agent will automatically begin exhibiting proactive characteristics.
## Integration
Works seamlessly with all OpenClaw agents and can be combined with other skills for enhanced functionality.
## Requirements
- OpenClaw v1.0 or higher
- Standard agent configuration
## Customization
The proactive behavior can be tuned through configuration parameters to match your preferred level of initiative and communication style.
@@ -0,0 +1,6 @@
{
"ownerId": "kn778z31czjyj515snpsht5ghh80hy50",
"slug": "proactive-agent-lite",
"version": "1.0.0",
"publishedAt": 1772250292761
}
@@ -0,0 +1,7 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "stock-monitor-skill",
"installedVersion": "0.1.0",
"installedAt": 1773406340232
}
@@ -0,0 +1,23 @@
# Stock Monitor Pro
全功能智能股票监控预警系统
## 功能
- 成本百分比预警
- 日内涨跌幅预警
- 成交量异动监控
- 均线金叉死叉
- RSI超买超卖
- 跳空缺口检测
- 动态止盈
## 快速开始
```bash
cd scripts
cp config.example.py config.py
# 编辑 config.py 填入你的持仓
./control.sh start
```
## 许可证
MIT
@@ -0,0 +1,193 @@
---
name: stock-monitor
description: 全功能智能股票监控预警系统。支持成本百分比、均线金叉死叉、RSI超买超卖、成交量异动、跳空缺口、动态止盈等7大预警规则。符合中国投资者习惯(红涨绿跌)。
---
# Stock Monitor Pro - 全功能智能投顾系统
## 🎯 核心特色
### 1. 七大预警规则 (全都要!)
| 规则 | 触发条件 | 权重 |
|------|----------|------|
| **成本百分比** | 盈利+15% / 亏损-12% | ⭐⭐⭐ |
| **日内涨跌幅** | 个股±4% / ETF±2% / 黄金±2.5% | ⭐⭐ |
| **成交量异动** | 放量>2倍均量 / 缩量<0.5倍 | ⭐⭐ |
| **均线金叉/死叉** | MA5上穿/下穿MA10 | ⭐⭐⭐ |
| **RSI超买超卖** | RSI>70超买 / RSI<30超卖 | ⭐⭐ |
| **跳空缺口** | 向上/向下跳空>1% | ⭐⭐ |
| **动态止盈** | 盈利10%+后回撤5%/10% | ⭐⭐⭐ |
### 2. 分级预警系统
- **🚨 紧急级**: 多条件共振 (如: 放量+均线金叉+突破成本)
- **⚠️ 警告级**: 2个条件触发 (如: RSI超卖+放量)
- **📢 提醒级**: 单一条件触发
### 3. 中国习惯
- **🔴 红色** = 上涨 / 盈利
- **🟢 绿色** = 下跌 / 亏损
## 📋 监控配置
### 完整预警规则示例
```python
{
"code": "600362",
"name": "江西铜业",
"type": "individual", # 个股
"market": "sh",
"cost": 57.00, # 持仓成本
"alerts": {
# 1. 成本百分比
"cost_pct_above": 15.0, # 盈利15%提醒 (¥65.55)
"cost_pct_below": -12.0, # 亏损12%提醒 (¥50.16)
# 2. 日内涨跌幅 (个股±4%)
"change_pct_above": 4.0,
"change_pct_below": -4.0,
# 3. 成交量异动
"volume_surge": 2.0, # 放量>2倍5日均量
# 4-7. 技术指标 (默认开启)
"ma_monitor": True, # 均线金叉死叉
"rsi_monitor": True, # RSI超买超卖
"gap_monitor": True, # 跳空缺口
"trailing_stop": True # 动态止盈
}
}
```
### 标的类型差异化
| 类型 | 日内异动阈值 | 成交量阈值 | 适用标的 |
|------|-------------|-----------|----------|
| individual (个股) | ±4% | 2倍 | 江西铜业、中国平安 |
| etf (ETF) | ±2% | 1.8倍 | 恒生医疗、创50等 |
| gold (黄金) | ±2.5% | 无 | 伦敦金 |
## 🚀 运行方式
### 后台常驻进程
```bash
cd ~/workspace/skills/stock-monitor/scripts
./control.sh start # 启动
./control.sh status # 查看状态
./control.sh log # 查看日志
./control.sh stop # 停止
```
## ⚡ 智能频率 (北京时间)
| 时间段 | 频率 | 监控标的 |
|--------|------|----------|
| 交易时间 9:30-15:00 | 每5分钟 | 全部+技术指标 |
| 午休 11:30-13:00 | 每10分钟 | 全部 |
| 收盘后 15:00-24:00 | 每30分钟 | 全部 (日线数据) |
| 凌晨 0:00-9:30 | 每1小时 | 仅伦敦金 |
| 周末 | 每1小时 | 仅伦敦金 |
## 🔔 预警消息示例
### 多条件共振 (紧急级)
```
🚨【紧急】🔴 江西铜业 (600362)
━━━━━━━━━━━━━━━━━━━━
💰 当前价格: ¥65.50 (+15.0%)
📊 持仓成本: ¥57.00 | 盈亏: 🔴+14.9%
🎯 触发预警 (3项):
• 🎯 盈利 15% (目标价 ¥65.55)
• 🌟 均线金叉 (MA5¥63.2上穿MA10¥62.8)
• 📊 放量 2.5倍 (5日均量)
📊 江西铜业 深度分析
💰 价格异动:
• 当前: 65.5 (+15.0%)
• MA趋势: MA5>MA10>MA20 [多头排列]
• RSI: 68 [接近超买]
💡 Kimi建议:
🚀 多条件共振,趋势强劲,可考虑继续持有或分批减仓。
```
### RSI超卖 (警告级)
```
⚠️【警告】🟢 恒生医疗 (159892)
━━━━━━━━━━━━━━━━━━━━
💰 当前价格: ¥0.72 (-10.0%)
📊 持仓成本: ¥0.80 | 盈亏: 🟢-10.0%
🎯 触发预警 (2项):
• 📉 日内大跌 -10.0%
• ❄️ RSI超卖 (28.5),可能反弹
💡 Kimi建议:
🔍 短期超跌严重,RSI进入超卖区,关注反弹机会但勿急于抄底。
```
### 动态止盈提醒
```
📢【提醒】🔴 中国平安 (601318)
━━━━━━━━━━━━━━━━━━━━
💰 当前价格: ¥70.50 (+6.8%)
📊 持仓成本: ¥66.00 | 盈亏: 🔴+6.8%
🎯 触发预警:
• 📉 利润回撤 5.2%,建议减仓保护利润
(最高盈利12%已回撤)
```
## 🛠️ 文件结构
```
stock-monitor/
├── SKILL.md # 本文档
├── RULE_REVIEW_REPORT.md # 规则审核报告
└── scripts/
├── monitor.py # 核心监控 (7大规则)
├── monitor_daemon.py # 后台常驻进程
├── analyser.py # 智能分析引擎
└── control.sh # 一键控制脚本
```
## ⚙️ 自定义配置
### 修改成本价
```python
"cost": 55.50, # 改成你的实际成本
```
### 调整预警阈值
```python
"cost_pct_above": 20.0, # 盈利20%提醒
"cost_pct_below": -15.0, # 亏损15%提醒
"change_pct_above": 5.0, # 日内异动±5%
"volume_surge": 3.0, # 放量3倍提醒
```
### 开关技术指标
```python
"ma_monitor": False, # 关闭均线
"rsi_monitor": True, # 开启RSI
"gap_monitor": True, # 开启跳空
```
## 📝 更新日志
- **v3.0 全功能版**: 完成7大预警规则 (成本/涨跌幅/成交量/均线/RSI/跳空/动态止盈)
- **v2.4 成本百分比版**: 支持基于持仓成本的百分比预警
- **v2.3 中国版**: 红涨绿跌颜色习惯
- **v2.2 常驻进程版**: 后台常驻进程支持
- **v2.1 智能频率版**: 智能频率控制
- **v2.0 Pro版**: 新闻舆情分析
## ⚠️ 使用提示
1. **技术指标有滞后性**: 均线、MACD等都是滞后指标,用于确认趋势而非预测
2. **避免过度交易**: 预警只是参考,不要每个信号都操作
3. **多条件共振更可靠**: 单一指标容易假信号,多条件共振更准确
4. **动态止盈要灵活**: 回撤5%减仓、10%清仓是建议,根据市场灵活调整
---
**核心原则**:
> 预警系统目标是"不错过大机会,不犯大错误",不是"抓住每一个波动"。
@@ -0,0 +1,6 @@
{
"ownerId": "kn70aj13hr3z4fpmfk1y2jmpz181gn2z",
"slug": "stock-monitor-skill",
"version": "0.1.0",
"publishedAt": 1771579954718
}
@@ -0,0 +1,249 @@
#!/usr/bin/env python3
"""
Stock Monitor Pro - 智能分析引擎
集成:新闻、资金流向、龙虎榜、宏观关联分析
"""
import requests
import json
import re
from datetime import datetime, timedelta
from typing import List, Dict, Optional
class StockAnalyser:
"""股票智能分析器 - 结合多维度数据给出建议"""
def __init__(self):
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
# ========== 1. 新闻舆情 ==========
def fetch_eastmoney_news(self, symbol: str, name: str, limit: int = 5) -> List[Dict]:
"""获取东方财富个股新闻"""
url = f"https://searchapi.eastmoney.com/api/suggest/get"
params = {
"input": name,
"type": 14,
"count": limit
}
try:
resp = self.session.get(url, params=params, timeout=10)
data = resp.json()
news_list = []
for item in data.get("QuotationCodeTable", {}).get("Data", []):
news_list.append({
"title": item.get("Title", ""),
"url": item.get("Url", ""),
"time": item.get("ShowTime", "")
})
return news_list
except Exception as e:
return []
def fetch_sina_news(self, symbol: str, name: str) -> List[Dict]:
"""获取新浪财经个股新闻"""
# 新浪新闻搜索接口
url = f"https://search.sina.com.cn/?q={name}&c=news&sort=time"
try:
resp = self.session.get(url, timeout=10)
# 这里可以做更精细的HTML解析
# 简化返回示例
return [{"title": f"新浪财经-{name}相关新闻", "source": "新浪"}]
except:
return []
def analyze_sentiment(self, news_list: List[Dict]) -> Dict:
"""简单情感分析"""
positive_words = ['利好', '增长', '突破', '买入', '增持', '涨停', '超预期', '业绩大增']
negative_words = ['利空', '减持', '下跌', '卖出', '亏损', '暴雷', '跌停', '不及预期']
sentiment = {"positive": 0, "negative": 0, "neutral": 0, "summary": []}
for news in news_list:
title = news.get("title", "")
p_count = sum(1 for w in positive_words if w in title)
n_count = sum(1 for w in negative_words if w in title)
if p_count > n_count:
sentiment["positive"] += 1
elif n_count > p_count:
sentiment["negative"] += 1
else:
sentiment["neutral"] += 1
# 生成情感摘要
if sentiment["positive"] > sentiment["negative"]:
sentiment["overall"] = "偏多"
elif sentiment["negative"] > sentiment["positive"]:
sentiment["overall"] = "偏空"
else:
sentiment["overall"] = "中性"
return sentiment
# ========== 2. 资金流向 ==========
def fetch_fund_flow(self, symbol: str, market: str = "sz") -> Dict:
"""获取个股资金流向 (新浪财经)"""
# 新浪资金流向接口
code = f"{market}{symbol}"
url = f"https://quotes.sina.cn/cn/api/quotes.php?symbol={code}&source=sina"
try:
resp = self.session.get(url, timeout=10)
# 解析返回数据
return {
"main_inflow": "数据获取中...",
"retail_inflow": "数据获取中...",
"net_inflow": "数据获取中..."
}
except:
return {"error": "获取失败"}
def fetch_northbound_flow(self) -> Dict:
"""获取北向资金 (沪深股通) 流向"""
url = "https://push2.eastmoney.com/api/qt/stock/get"
params = {"secid": "1.000001", "fields": "f170"} # 简化示例
try:
resp = self.session.get(url, params=params, timeout=10)
return {"northbound": "北向资金数据获取中..."}
except:
return {}
# ========== 3. 龙虎榜 ==========
def fetch_dragon_tiger(self, date: str = None) -> List[Dict]:
"""获取龙虎榜数据"""
if not date:
date = datetime.now().strftime("%Y%m%d")
url = f"http://datacenter-web.eastmoney.com/api/data/v1/get"
params = {
"sortColumns": "NET_BUY_AMT",
"sortTypes": "-1",
"pageSize": "50",
"pageNumber": "1",
"reportName": "RPT_DMSK_TS",
"columns": "ALL",
"filter": f"(TRADE_DATE='{date}')"
}
try:
resp = self.session.get(url, params=params, timeout=10)
data = resp.json()
return data.get("result", {}).get("data", [])
except:
return []
# ========== 4. 宏观关联分析 ==========
def analyze_gold_correlation(self, gold_price: float, stocks: List[Dict]) -> str:
"""分析金价与持仓股票的关联"""
# 江西铜业等有色股与金价正相关
correlation_map = {
"600362": "强正相关", # 江西铜业
"601318": "弱相关", # 中国平安
"513180": "弱负相关", # 恒生科技
"159892": "弱相关", # 恒生医疗
}
analysis = []
for stock in stocks:
code = stock.get("code")
corr = correlation_map.get(code, "未知")
if corr in ["强正相关", "中等正相关"]:
analysis.append(f"📈 {stock['name']}: 与金价{corr},金价上涨可能带动该股")
return "\n".join(analysis) if analysis else "暂无强关联标的"
# ========== 5. 综合分析 ==========
def generate_insight(self, stock: Dict, price_data: Dict, alerts: List) -> str:
"""生成综合分析报告"""
code = stock['code']
name = stock['name']
# 1. 获取新闻
news_list = self.fetch_eastmoney_news(code, name)
sentiment = self.analyze_sentiment(news_list)
# 2. 资金流向
fund_flow = self.fetch_fund_flow(code, stock.get('market', 'sz'))
# 3. 构建报告
report = f"""📊 <b>{name} ({code}) 深度分析</b>
💰 <b>价格异动:</b>
• 当前: {price_data.get('price', 'N/A')} ({price_data.get('change_pct', 0):+.2f}%)
• 触发: {', '.join([a[1] for a in alerts])}
📰 <b>舆情分析 ({sentiment.get('overall', '未知')}):</b>
• 最近新闻: {len(news_list)}
• 正面: {sentiment.get('positive', 0)} | 负面: {sentiment.get('negative', 0)}
"""
# 添加最新新闻标题
if news_list:
report += "\n<b>最新动态:</b>\n"
for n in news_list[:2]:
report += f"{n.get('title', '无标题')[:30]}...\n"
# 4. 给出建议
suggestion = self._generate_suggestion(sentiment, alerts)
report += f"\n💡 <b>Kimi建议:</b>\n{suggestion}"
return report
def _generate_suggestion(self, sentiment: Dict, alerts: List) -> str:
"""基于数据生成建议"""
alert_types = [a[0] for a in alerts]
overall = sentiment.get("overall", "中性")
# 价格下跌 + 舆情偏空 = 谨慎
if "below" in alert_types and overall == "偏空":
return "⚠️ 价格跌破支撑位,且舆情偏空,建议观察等待,不急于抄底。"
# 价格下跌 + 舆情偏多 = 可能是机会
if "below" in alert_types and overall == "偏多":
return "🔍 价格下跌但舆情偏多,可能是情绪错杀,关注是否有反弹机会。"
# 价格突破 + 舆情偏多 = 确认趋势
if "above" in alert_types and overall == "偏多":
return "🚀 价格突破且舆情配合,趋势可能延续,可考虑顺势而为。"
# 大涨
if "pct_up" in alert_types:
return "📈 短期涨幅较大,注意获利了结风险。"
# 大跌
if "pct_down" in alert_types:
return "📉 短期跌幅较大,关注是否超跌反弹,但勿急于抄底。"
return "⏳ 建议保持观察,等待更明确信号。"
# ========== 测试 ==========
if __name__ == '__main__':
analyser = StockAnalyser()
# 测试新闻抓取
print("=== 新闻测试 ===")
news = analyser.fetch_eastmoney_news("600362", "江西铜业")
print(f"获取到 {len(news)} 条新闻")
for n in news[:3]:
print(f" - {n.get('title', 'N/A')[:40]}...")
# 测试情感分析
print("\n=== 情感分析测试 ===")
sentiment = analyser.analyze_sentiment(news)
print(f"整体情绪: {sentiment.get('overall')}")
print(f"正面: {sentiment.get('positive')}, 负面: {sentiment.get('negative')}")
# 测试金价关联
print("\n=== 宏观关联测试 ===")
stocks = [{"code": "600362", "name": "江西铜业"}]
corr = analyser.analyze_gold_correlation(2743, stocks)
print(corr)
+64
View File
@@ -0,0 +1,64 @@
#!/bin/bash
# Stock Monitor 一键启动脚本
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LOG_DIR="$HOME/.stock_monitor"
PID_FILE="$LOG_DIR/monitor.pid"
case "$1" in
start)
if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
echo "⚠️ 监控进程已在运行 (PID: $(cat $PID_FILE))"
exit 1
fi
echo "🚀 启动 Stock Monitor 后台进程..."
mkdir -p "$LOG_DIR"
nohup python3 "$SCRIPT_DIR/monitor_daemon.py" > "$LOG_DIR/monitor.log" 2>&1 &
echo $! > "$PID_FILE"
echo "✅ 已启动 (PID: $!)"
echo "📋 日志: $LOG_DIR/monitor.log"
;;
stop)
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if kill -0 "$PID" 2>/dev/null; then
echo "🛑 停止监控进程 (PID: $PID)..."
kill "$PID"
rm "$PID_FILE"
echo "✅ 已停止"
else
echo "⚠️ 进程不存在"
rm "$PID_FILE"
fi
else
echo "⚠️ 没有运行中的进程"
fi
;;
status)
if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
echo "✅ 监控运行中 (PID: $(cat $PID_FILE))"
echo "📋 最近日志:"
tail -5 "$LOG_DIR/monitor.log" 2>/dev/null || echo " 暂无日志"
else
echo "⏹️ 监控未运行"
fi
;;
log)
tail -f "$LOG_DIR/monitor.log"
;;
*)
echo "Stock Monitor 控制脚本"
echo ""
echo "用法: ./control.sh [start|stop|status|log]"
echo ""
echo " start - 启动后台监控"
echo " stop - 停止监控"
echo " status - 查看状态"
echo " log - 查看实时日志"
;;
esac
@@ -0,0 +1,507 @@
#!/usr/bin/env python3
"""
自选股监控预警工具 - OpenClaw集成版
支持 A股、ETF 及 国际现货黄金 (伦敦金)
"""
import requests
import json
import time
import os
from datetime import datetime
from pathlib import Path
# ============ 配置区 ============
# 监控列表 - 长期挂机通用配置
# 注意: 伦敦金使用新浪hf_XAU接口,价格为 人民币/克 (约4800元/克 = $2740/盎司)
#
# 预警规则设计原则 (适合长期挂机):
# 1. 成本百分比预警: 基于持仓成本设置 ±10%/±15% 预警,比固定价格更合理
# 2. 单日涨跌幅预警:
# - 个股 ±3%~5% (波动大)
# - ETF ±1.5%~2.5% (波动小)
# - 黄金 ±2%~3% (24H特殊)
# 3. 防骚扰: 同类预警30分钟内只发一次
# 标的类型定义
STOCK_TYPE = {
"INDIVIDUAL": "individual", # 个股
"ETF": "etf", # ETF
"GOLD": "gold" # 黄金/贵金属
}
WATCHLIST = [
# ===== 用户自定义监控个股 =====
{
"code": "000630",
"name": "铜陵有色",
"market": "sz",
"type": "individual",
"cost": 7.00, # 参考成本价
"alerts": {
"change_pct_above": 5.0, # 日内涨超 5% 预警
"change_pct_below": -5.0, # 日内跌超 5% 预警
"volume_surge": 2.0 # 成交量异动
}
},
{
"code": "688313",
"name": "仕佳光子",
"market": "sh",
"type": "individual",
"cost": 15.00, # 参考成本价(待确认)
"alerts": {
"change_pct_above": 5.0, # 日内涨超 5% 预警
"change_pct_below": -5.0, # 日内跌超 5% 预警
"volume_surge": 2.0 # 成交量异动
}
},
{
"code": "600096",
"name": "云天化",
"market": "sh",
"type": "individual",
"cost": 42.00, # 老大确认的实际成本价
"alerts": {
"change_pct_above": 5.0, # 日内涨超 5% 预警
"change_pct_below": -5.0, # 日内跌超 5% 预警
"volume_surge": 2.0 # 成交量异动
}
},
{
"code": "002195",
"name": "岩山科技",
"market": "sz",
"type": "individual",
"cost": 10.68, # 200 股,成本 10.68 元
"alerts": {
"cost_pct_above": 5.0, # 盈利超 5% 快跑 (目标价 ¥11.21)
"change_pct_above": 5.0, # 日内涨超 5% 预警
"change_pct_below": -5.0, # 日内跌超 5% 预警
"volume_surge": 2.0 # 成交量异动
}
}
]
# 智能频率配置
SMART_SCHEDULE = {
"market_open": {"hours": [(9, 30), (11, 30), (13, 0), (15, 0)], "interval": 300}, # 交易时间: 5分钟
"after_hours": {"interval": 1800}, # 收盘后: 30分钟
"night": {"hours": [(0, 0), (8, 0)], "interval": 3600}, # 凌晨: 1小时(仅伦敦金)
}
# ============ 核心代码 ============
class StockAlert:
def __init__(self):
self.prev_data = {}
self.alert_log = []
self.session = requests.Session()
self.session.headers.update({"User-Agent": "Mozilla/5.0"})
def should_run_now(self):
"""智能频率控制: 判断当前是否应该执行监控 (基于北京时间)"""
# 服务器在纽约(EST),中国股市用北京时间(CST = EST + 13小时)
from datetime import timedelta
now = datetime.now() + timedelta(hours=13) # 转换成北京时间
hour, minute = now.hour, now.minute
time_val = hour * 100 + minute
weekday = now.weekday()
# 周末只监控伦敦金
if weekday >= 5: # 周六日
return {"run": True, "mode": "weekend", "stocks": [s for s in WATCHLIST if s['market'] == 'fx']}
# 交易时间 (9:30-11:30, 13:00-15:00)
morning_session = 930 <= time_val <= 1130
afternoon_session = 1300 <= time_val <= 1500
if morning_session or afternoon_session:
return {"run": True, "mode": "market", "stocks": WATCHLIST, "interval": 300}
# 午休 (11:30-13:00)
if 1130 < time_val < 1300:
return {"run": True, "mode": "lunch", "stocks": WATCHLIST, "interval": 600} # 10分钟
# 收盘后 (15:00-24:00)
if 1500 <= time_val <= 2359:
return {"run": True, "mode": "after_hours", "stocks": WATCHLIST, "interval": 1800} # 30分钟
# 凌晨 (0:00-9:30)
if 0 <= time_val < 930:
return {"run": True, "mode": "night", "stocks": [s for s in WATCHLIST if s['market'] == 'fx'], "interval": 3600} # 1小时
return {"run": False}
def fetch_eastmoney_kline(self, symbol, market):
"""获取最新日K线数据 (收盘后也能获取收盘价)"""
secid = f"{market}.{symbol}"
url = "https://push2his.eastmoney.com/api/qt/stock/kline/get"
params = {
'secid': secid,
'fields1': 'f1,f2,f3,f4,f5,f6',
'fields2': 'f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61',
'klt': '101', # 日线
'fqt': '0',
'end': '20500101',
'lmt': '2' # 取最近2天,用于计算涨跌幅
}
try:
resp = self.session.get(url, params=params, timeout=10)
data = resp.json()
klines = data.get('data', {}).get('klines', [])
if len(klines) >= 1:
# 格式: 日期,开盘,收盘,最高,最低,成交量,成交额,振幅,涨跌幅,涨跌额,换手率
today = klines[-1].split(',')
prev_close = float(today[2]) # 昨收
if len(klines) >= 2:
prev_close = float(klines[-2].split(',')[2]) # 前一天收盘
return {
'name': data.get('data', {}).get('name', symbol),
'price': float(today[2]), # 收盘
'prev_close': prev_close,
'volume': int(float(today[5])),
'amount': float(today[6]),
'date': today[0],
'time': '15:00:00'
}
except Exception as e:
print(f"东财K线获取失败 {symbol}: {e}")
return None
def fetch_volume_ma5(self, symbol, market):
"""获取5日平均成交量"""
secid = f"{market}.{symbol}"
url = "https://push2his.eastmoney.com/api/qt/stock/kline/get"
params = {
'secid': secid,
'fields1': 'f1,f2,f3,f4,f5,f6',
'fields2': 'f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61',
'klt': '101',
'fqt': '0',
'end': '20500101',
'lmt': '6' # 取最近6天(今天+前5天)
}
try:
resp = self.session.get(url, params=params, timeout=10)
data = resp.json()
klines = data.get('data', {}).get('klines', [])
if len(klines) >= 2:
# 计算前5日平均成交量(不含今天)
volumes = []
for k in klines[:-1]: # 排除最后一天(今天)
p = k.split(',')
volumes.append(float(p[5])) # 成交量
return sum(volumes) / len(volumes) if volumes else 0
except Exception as e:
print(f"获取均量失败 {symbol}: {e}")
return 0
def fetch_ma_data(self, symbol, market):
"""获取均线数据 (MA5, MA10, MA20) 和 RSI"""
secid = f"{market}.{symbol}"
url = "https://push2his.eastmoney.com/api/qt/stock/kline/get"
params = {
'secid': secid,
'fields1': 'f1,f2,f3,f4,f5,f6',
'fields2': 'f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61',
'klt': '101',
'fqt': '0',
'end': '20500101',
'lmt': '30' # 取最近30天计算MA20和RSI
}
try:
resp = self.session.get(url, params=params, timeout=10)
data = resp.json()
klines = data.get('data', {}).get('klines', [])
if len(klines) >= 20:
closes = []
for k in klines:
p = k.split(',')
closes.append(float(p[2])) # 收盘价
# 计算均线
ma5 = sum(closes[-5:]) / 5
ma10 = sum(closes[-10:]) / 10
ma20 = sum(closes[-20:]) / 20
# 判断均线趋势
prev_ma5 = sum(closes[-6:-1]) / 5
prev_ma10 = sum(closes[-11:-1]) / 10
# 计算RSI(14)
rsi = self._calculate_rsi(closes, 14)
return {
'MA5': ma5,
'MA10': ma10,
'MA20': ma20,
'MA5_trend': 'up' if ma5 > prev_ma5 else 'down',
'MA10_trend': 'up' if ma10 > prev_ma10 else 'down',
'golden_cross': prev_ma5 <= prev_ma10 and ma5 > ma10,
'death_cross': prev_ma5 >= prev_ma10 and ma5 < ma10,
'RSI': rsi,
'RSI_overbought': rsi > 70 if rsi else False,
'RSI_oversold': rsi < 30 if rsi else False
}
except Exception as e:
print(f"获取均线失败 {symbol}: {e}")
return None
def _calculate_rsi(self, closes, period=14):
"""计算RSI指标"""
if len(closes) < period + 1:
return None
gains = []
losses = []
for i in range(1, period + 1):
change = closes[-i] - closes[-i-1]
if change > 0:
gains.append(change)
losses.append(0)
else:
gains.append(0)
losses.append(abs(change))
avg_gain = sum(gains) / period
avg_loss = sum(losses) / period
if avg_loss == 0:
return 100
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return round(rsi, 2)
def fetch_tencent_realtime(self, stocks):
"""获取实时行情 (腾讯财经接口,更稳定)"""
stock_list = [s for s in stocks if s['market'] != 'fx']
fx_list = [s for s in stocks if s['market'] == 'fx']
results = {}
# 1. A 股/ETF - 腾讯财经接口
if stock_list:
for stock in stock_list:
code = f"{stock['market']}{stock['code']}"
url = f"http://qt.gtimg.cn/q={code}"
try:
resp = self.session.get(url, timeout=5)
resp.encoding = 'gbk'
data = resp.text.strip()
if '="' not in data:
continue
parts = data.strip('"').split('~')
start_idx = 0
for j, p in enumerate(parts):
if p.isdigit() and len(p) == 2:
start_idx = j
break
if len(parts) > start_idx + 20:
current = float(parts[start_idx+3])
prev_close = float(parts[start_idx+4])
open_p = float(parts[start_idx+5])
change = 0
change_pct = 0
high = 0
low = 0
for j in range(len(parts)-5):
try:
if parts[j].startswith('-') and '.' in parts[j]:
v1 = float(parts[j])
v2 = float(parts[j+1])
if -10 < v2 < 10:
change = v1
change_pct = v2
high = float(parts[j+2])
low = float(parts[j+3])
break
except:
continue
results[stock['code']] = {
'name': parts[start_idx+1],
'price': current,
'prev_close': prev_close,
'open': open_p,
'high': high,
'low': low,
'volume': 0,
'amount': 0,
'date': datetime.now().strftime('%Y-%m-%d'),
'time': datetime.now().strftime('%H:%M:%S')
}
print(f"{stock['name']} ({stock['code']}): ¥{current:.2f}")
except Exception as e:
print(f"腾讯行情获取失败 {stock['code']}: {e}")
# 2. 伦敦金 (保留原逻辑)
if fx_list:
url = "https://hq.sinajs.cn/list=hf_XAU"
try:
resp = self.session.get(url, timeout=5)
line = resp.text.strip()
if '"' in line:
data_str = line[line.index('"')+1 : line.rindex('"')]
p = data_str.split(',')
if len(p) >= 13:
price = float(p[0])
results['XAU'] = {
'name': '伦敦金',
'price': price,
'prev_close': float(p[7]),
'volume': 0, 'amount': 0,
'date': datetime.now().strftime('%Y-%m-%d'),
'time': p[6]
}
except Exception as e:
print(f"伦敦金获取失败:{e}")
return results
def record_alert(self, code, icon):
"""记录预警日志 (简化版)"""
# 简单打印日志,实际可以写入文件
print(f" 📝 预警记录:{code} - {icon}")
def check_alerts(self, stock_config, data):
"""检查预警条件 (返回格式:[(icon, text), ...], level)"""
alerts = []
code = stock_config['code']
cfg = stock_config.get('alerts', {})
cost = stock_config.get('cost', 0)
price = data['price']
prev_close = data['prev_close']
change_pct = ((price - prev_close) / prev_close) * 100 if prev_close else 0
# 1. 成本百分比预警
if cost > 0:
if 'cost_pct_above' in cfg:
threshold = cost * (1 + cfg['cost_pct_above'] / 100)
if price >= threshold:
alerts.append(("🎯", f"盈利 {cfg['cost_pct_above']}% (目标价 ¥{threshold:.2f})"))
if 'cost_pct_below' in cfg:
threshold = cost * (1 + cfg['cost_pct_below'] / 100)
if price <= threshold:
alerts.append(("📉", f"亏损 {abs(cfg['cost_pct_below'])}% (止损价 ¥{threshold:.2f})"))
# 2. 日内涨跌幅预警
if 'change_pct_above' in cfg and change_pct >= cfg['change_pct_above']:
alerts.append(("📈", f"日内大涨 {change_pct:.2f}% (阈值 {cfg['change_pct_above']}%)"))
if 'change_pct_below' in cfg and change_pct <= cfg['change_pct_below']:
alerts.append(("📉", f"日内大跌 {change_pct:.2f}% (阈值 {cfg['change_pct_below']}%)"))
# 确定预警级别
if len(alerts) >= 3:
level = "critical"
elif len(alerts) >= 2:
level = "warning"
elif len(alerts) >= 1:
level = "info"
else:
level = "none"
return alerts, level
def fetch_news(self, symbol):
"""抓取个股最近新闻 (新浪/东财聚合) - 简化版"""
try:
# 使用东财个股新闻API
url = f"https://emweb.securities.eastmoney.com/PC_HSF10/CompanySurvey/CompanySurveyAjax"
params = {"code": symbol}
resp = self.session.get(url, params=params, timeout=5)
return ["新闻模块已就绪 (市场收盘中)"]
except:
return []
def run_once(self, smart_mode=True):
"""执行监控 (支持智能频率)"""
if smart_mode:
schedule = self.should_run_now()
if not schedule.get("run"):
return []
stocks_to_check = schedule.get("stocks", WATCHLIST)
mode = schedule.get("mode", "normal")
# 只在特定模式打印日志
if mode in ["market", "weekend"]:
print(f"[{datetime.now().strftime('%H:%M')}] {mode}模式扫描 {len(stocks_to_check)} 只标的...")
else:
stocks_to_check = WATCHLIST
data_map = self.fetch_tencent_realtime(stocks_to_check)
triggered = []
for stock in stocks_to_check:
code = stock['code']
if code not in data_map: continue
data = data_map[code]
# 数据有效性检查
if data['price'] <= 0 or data['prev_close'] <= 0:
continue
alerts, level = self.check_alerts(stock, data)
if alerts:
change_pct = (data['price'] - data['prev_close']) / data['prev_close'] * 100 if data['prev_close'] else 0
# 中国习惯: 红色=上涨, 绿色=下跌
if change_pct > 0:
color_emoji = "🔴" # 红涨
elif change_pct < 0:
color_emoji = "🟢" # 绿跌
else:
color_emoji = ""
# 预警级别标识
level_icons = {
"critical": "🚨", # 紧急
"warning": "⚠️", # 警告
"info": "📢" # 提醒
}
level_icon = level_icons.get(level, "📢")
level_text = {"critical": "【紧急】", "warning": "【警告】", "info": "【提醒】"}.get(level, "")
msg = f"<b>{level_icon} {level_text}{color_emoji} {stock['name']} ({code})</b>\n"
msg += f"━━━━━━━━━━━━━━━━━━━━\n"
msg += f"💰 当前价格: <b>{data['price']:.2f}</b> ({change_pct:+.2f}%)\n"
# 显示持仓盈亏
cost = stock.get('cost', 0)
if cost > 0:
cost_change = (data['price'] - cost) / cost * 100
profit_icon = "🔴+" if cost_change > 0 else "🟢"
msg += f"📊 持仓成本: ¥{cost:.2f} | 盈亏: {profit_icon}{cost_change:.2f}%\n"
msg += f"\n🎯 触发预警 ({len(alerts)}项):\n"
for _, text in alerts:
msg += f"{text}\n"
self.record_alert(code, _)
# Pro版:集成智能分析
try:
from analyser import StockAnalyser
analyser = StockAnalyser()
insight = analyser.generate_insight(stock, {
'price': data['price'],
'change_pct': change_pct
}, alerts)
msg += f"\n{insight}"
except Exception:
pass
triggered.append(msg)
return triggered
if __name__ == '__main__':
monitor = StockAlert()
for alert in monitor.run_once():
print(alert)
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
"""
Stock Monitor Daemon - 后台常驻进程
自动运行监控,智能控制频率,支持 graceful shutdown
"""
import sys
import time
import signal
import logging
from datetime import datetime
from pathlib import Path
# 设置日志
log_dir = Path.home() / ".stock_monitor"
log_dir.mkdir(exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_dir / "monitor.log"),
logging.StreamHandler(sys.stdout)
]
)
logger = logging.getLogger(__name__)
# 导入监控类
sys.path.insert(0, str(Path(__file__).parent))
from monitor import StockAlert, WATCHLIST
class MonitorDaemon:
def __init__(self):
self.monitor = StockAlert()
self.running = True
self.last_run_time = 0
# 设置信号处理
signal.signal(signal.SIGTERM, self.handle_shutdown)
signal.signal(signal.SIGINT, self.handle_shutdown)
def handle_shutdown(self, signum, frame):
"""优雅退出"""
logger.info(f"收到信号 {signum},正在关闭...")
self.running = False
def get_sleep_interval(self):
"""根据当前时间获取睡眠间隔"""
schedule = self.monitor.should_run_now()
if not schedule.get("run"):
# 如果当前不需要运行,计算到下次运行的时间
now = datetime.now()
hour = now.hour
# 凌晨时段,1小时后检查
if 0 <= hour < 9:
return 3600
return 300 # 默认5分钟
return schedule.get("interval", 300)
def run(self):
"""主循环"""
logger.info("=" * 60)
logger.info("🚀 Stock Monitor Daemon 启动")
logger.info(f"📋 监控标的: {len(WATCHLIST)}")
logger.info("=" * 60)
while self.running:
try:
# 检查是否应该执行
schedule = self.monitor.should_run_now()
if schedule.get("run"):
mode = schedule.get("mode", "normal")
stocks_count = len(schedule.get("stocks", []))
logger.info(f"[{mode}] 扫描 {stocks_count} 只标的...")
# 执行监控
alerts = self.monitor.run_once(smart_mode=False) # 已经判断过了
if alerts:
logger.info(f"⚠️ 触发 {len(alerts)} 条预警")
# 这里会通过 message 工具发送通知
else:
logger.debug("✅ 无预警")
self.last_run_time = time.time()
# 计算睡眠间隔
sleep_interval = self.get_sleep_interval()
logger.debug(f"下次检查: {sleep_interval} 秒后")
# 分段睡眠,方便及时响应退出信号
slept = 0
while slept < sleep_interval and self.running:
time.sleep(1)
slept += 1
except Exception as e:
logger.error(f"运行出错: {e}", exc_info=True)
time.sleep(60) # 出错后等待1分钟重试
logger.info("👋 Daemon 已停止")
if __name__ == '__main__':
daemon = MonitorDaemon()
daemon.run()
+198
View File
@@ -0,0 +1,198 @@
#!/usr/bin/env python3
"""
铜陵有色专项监控 - 实时股价监控
使用腾讯财经接口,确保数据准确性
"""
import requests
import json
import time
import os
from datetime import datetime
from pathlib import Path
# ============ 配置 ============
WATCHLIST = [
{
"code": "000630",
"name": "铜陵有色",
"market": "sz",
"alerts": {
"change_pct_above": 5.0, # 涨超 5% 预警
"change_pct_below": -5.0, # 跌超 5% 预警
}
}
]
# 企业微信配置
WECOM_BOT_ID = "aibwl3AhnzfRPRTEZvBVwlB-vRD33yJdUVX"
WECOM_SECRET = "1eUB2yd2R7bll6VjBQ5OGptJj2YiwutMUmACe9UGC7k"
ALLOW_USERS = ["HouHuan", "WanMeiShengHuo", "XinNingXianGuoNaiChaKaFeiZhaJiHa"]
# ============ 核心功能 ============
def fetch_tencent_price(stock):
"""腾讯财经接口获取实时股价"""
code = f"{stock['market']}{stock['code']}"
url = f"http://qt.gtimg.cn/q={code}"
try:
resp = requests.get(url, timeout=5)
resp.encoding = 'gbk'
data = resp.text.strip()
# 解析:v_sz000630="51~名称~代码~现价~昨收~今开~...
if '="' not in data:
return None
parts = data.strip('"').split('~')
# 找到前缀位置
start_idx = 0
for i, p in enumerate(parts):
if p.isdigit() and len(p) == 2:
start_idx = i
break
if len(parts) <= start_idx + 20:
return None
# 正确解析索引
name = parts[start_idx+1]
code = parts[start_idx+2]
current = float(parts[start_idx+3]) # 现价
prev_close = float(parts[start_idx+4]) # 昨收
open_p = float(parts[start_idx+5]) # 今开
# 找涨跌额、涨跌幅、最高、最低
change = 0
change_pct = 0
high = 0
low = 0
for i in range(len(parts)-5):
try:
if parts[i].startswith('-') and '.' in parts[i]:
v1 = float(parts[i])
v2 = float(parts[i+1])
if -10 < v2 < 10: # 涨跌幅一般在 -10~10 之间
change = v1
change_pct = v2
high = float(parts[i+2])
low = float(parts[i+3])
break
except:
continue
return {
'name': name,
'code': code,
'price': current,
'prev_close': prev_close,
'open': open_p,
'high': high,
'low': low,
'change': change,
'change_pct': change_pct,
'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
except Exception as e:
print(f"❌ 数据获取失败:{e}")
return None
def check_alerts(stock, data):
"""检查是否触发预警"""
alerts = []
cfg = stock.get('alerts', {})
change_pct = data['change_pct']
# 检查涨跌幅预警
if 'change_pct_above' in cfg and change_pct >= cfg['change_pct_above']:
alerts.append({
'type': '🔴 大涨',
'condition': f"涨幅超过 {cfg['change_pct_above']}%",
'value': f"{change_pct:.2f}%"
})
if 'change_pct_below' in cfg and change_pct <= cfg['change_pct_below']:
alerts.append({
'type': '🟢 大跌',
'condition': f"跌幅超过 {abs(cfg['change_pct_below'])}%",
'value': f"{change_pct:.2f}%"
})
return alerts
def send_wecom_alert(stock_name, data, alerts):
"""发送企业微信预警消息"""
from gateway import send_message
# 组合消息
alert_text = "\n".join([f"{a['type']}: {a['condition']} ({a['value']})" for a in alerts])
message = f"""🚨【股价预警】{stock_name} ({data['code']})
━━━━━━━━━━━━━━━━━━━━
💰 当前价格:¥{data['price']:.2f} ({data['change_pct']:+.2f}%)
🎯 触发预警:
{alert_text}
📊 详细数据:
• 昨收:¥{data['prev_close']:.2f}
• 今开:¥{data['open']:.2f}
• 最高:¥{data['high']:.2f}
• 最低:¥{data['low']:.2f}
⏰ 数据时间:{data['time']}
💡 建议关注后续走势,注意风险控制。
"""
# 发送给所有允许的用户
for user in ALLOW_USERS:
try:
send_message(
channel="wecom",
target=user,
message=message
)
print(f"✅ 预警已发送给用户:{user}")
except Exception as e:
print(f"❌ 发送失败 {user}: {e}")
def monitor_once():
"""执行一次监控"""
print(f"\n{'='*60}")
print(f"📊 铜陵有色监控 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"{'='*60}")
for stock in WATCHLIST:
data = fetch_tencent_price(stock)
if not data:
print(f"{stock['name']} 数据获取失败")
continue
print(f"{stock['name']} ({stock['code']})")
print(f" 现价:¥{data['price']:.2f} ({data['change_pct']:+.2f}%)")
print(f" 昨收:¥{data['prev_close']:.2f}")
print(f" 涨跌:¥{data['change']:.2f}")
# 检查预警
alerts = check_alerts(stock, data)
if alerts:
print(f" 🚨 触发 {len(alerts)} 条预警!")
send_wecom_alert(stock['name'], data, alerts)
else:
print(f" ✅ 正常波动范围")
print(f"{'='*60}\n")
# ============ 主程序 ============
if __name__ == "__main__":
# 单次执行
monitor_once()
@@ -0,0 +1,352 @@
# 📈 股票技能使用手册
> 三大股票分析技能完整使用指南
> 更新时间:2026-03-13
> 适用市场:A 股(沪深两市)
---
## 📋 技能列表
| 技能名称 | 功能定位 | 适用场景 |
|----------|----------|----------|
| **a-stock-trading-assistant** | A 股实时交易助手 | 查行情、个股分析、买卖点建议 |
| **daily-stock-analysis** | 每日分析报告 | 每日收盘后自动生成分析报告 |
| **stock-monitor-skill** | 智能监控预警 | 7 大预警规则,实时推送提醒 |
---
## 1️⃣ A 股股票交易助手 (a-stock-trading-assistant)
### 🎯 功能定位
专业 A 股交易助手,提供实时行情、个股分析、大盘情绪、热点板块、交易策略等全方位服务。
### 📊 支持市场
| 代码前缀 | 市场 | 示例 |
|----------|------|------|
| 60xxxx | 上交所主板 | 600519 贵州茅台 |
| 00xxxx | 深交所主板 | 000001 平安银行 |
| 30xxxx | 创业板 | 300750 宁德时代 |
| 68xxxx | 科创板 | 688981 中芯国际 |
### 💡 核心功能
#### 1. 实时行情查询
```bash
# 查询单只股票
python3 scripts/fetch_stock.py --code 600519
# 查询大盘指数
python3 scripts/fetch_stock.py --index
# 查询热点板块
python3 scripts/fetch_stock.py --hot-sectors
```
**输出内容:**
- 当前价、涨跌幅、涨跌额
- 成交量、成交额、换手率
- 振幅、52 周高/低
- 分时走势摘要
#### 2. 个股综合分析
- **技术面**:均线系统、趋势判断、支撑/压力位、MACD/KDJ 信号
- **基本面**:PE/PB 估值、近期业绩、行业地位、风险点
- **综合判断**:看多/看空/中性
#### 3. 大盘情绪与风险判断
- 大盘强弱(趋势、量能、板块轮动)
- 市场情绪指数(赚钱效应、涨跌比)
- 风险等级(低/中/高)及应对建议
#### 4. 热点板块与龙头股
- 识别主线板块(连续性强)
- 识别情绪板块(短期热点)
- 列出核心龙头股
#### 5. 交易策略建议
```
【操作建议】XX 股(XXXXXX
方向:做多/观望/回避
入场区间:XX.XX - XX.XX 元
止损位:XX.XX 元(跌破离场)
止盈位:XX.XX 元(分批减仓)
仓位:XX%(轻/中/重仓)
逻辑:[核心理由 2-3 条]
风险:[主要风险 1-2 条]
```
#### 6. 价格预警监控
- 记录目标价、预警条件到 watchlist.md
- 配合券商 App 实时推送
### 🗣️ 使用示例
```
用户:贵州茅台现在多少钱?
用户:分析一下宁德时代的技术面
用户:今天大盘情绪怎么样?
用户:最近有什么热点板块?
用户:给我看看 600519 的买卖点建议
```
### ⚠️ 注意事项
- 数据标注来源和获取时间(精确到分钟)
- 所有价格建议附风险提示
- 避免绝对化表述("必涨"/"稳赚"
- 数字精确到小数点后 2 位
---
## 2️⃣ 每日股票分析 (daily-stock-analysis)
### 🎯 功能定位
每日收盘后自动生成股票分析报告,包含次日收盘价预测、历史预测准确率追踪、自我进化机制。
### 📁 报告存储
- **路径**`<working_directory>/daily-stock-analysis/reports/`
- **文件名**`YYYY-MM-DD-<TICKER>-analysis.md`
- **版本控制**:同一天同一只股票自动生成 `-v2``-v3` 版本
### 💡 核心功能
#### 1. 预测与回顾
- **pred_close_t1**:预测次日收盘价
- **prev_pred_close_t1**:上次预测的收盘价
- **prev_actual_close_t1**:实际收盘价
- **AE/APE**:预测误差/误差百分比
#### 2. 滚动准确率追踪
```bash
# 计算滚动准确率
python3 scripts/calc_accuracy.py \
--workdir <working_directory> \
--ticker <TICKER> \
--windows 1,3,7,30 \
--history-limit 60
```
#### 3. 自我进化机制
每次分析包含 1-3 个 `improvement_actions`,从近期预测失误中学习,用于下一次分析。
### 🗓️ 调度建议
设置为**工作日每天 10:00** 运行,保持预测 - 回顾窗口连续。
### 📄 报告模板
```markdown
# <TICKER> 每日分析 - YYYY-MM-DD
## 推荐建议
recommendation: 买入/持有/卖出
## 价格预测
pred_close_t1: XX.XX 元
## 历史预测回顾
prev_pred_close_t1: XX.XX 元
prev_actual_close_t1: XX.XX 元
AE: X.XX | APE: X.XX%
## 滚动准确率
1 日准确率:XX%
7 日准确率:XX%
## 改进措施
1. [具体改进行动]
2. [具体改进行动]
```
### ⚠️ 合规声明
每份报告必须附加:
> "本内容仅供研究和参考,不构成投资建议或收益保证。市场有风险,投资需谨慎。"
---
## 3️⃣ 股票监控预警 (stock-monitor-skill)
### 🎯 功能定位
全功能智能股票监控预警系统,7 大预警规则,实时推送提醒,符合中国投资者习惯(红涨绿跌)。
### 🚨 七大预警规则
| 规则 | 触发条件 | 权重 |
|------|----------|------|
| **成本百分比** | 盈利 +15% / 亏损 -12% | ⭐⭐⭐ |
| **日内涨跌幅** | 个股±4% / ETF±2% / 黄金±2.5% | ⭐⭐ |
| **成交量异动** | 放量>2 倍均量 / 缩量<0.5 倍 | ⭐⭐ |
| **均线金叉/死叉** | MA5 上穿/下穿 MA10 | ⭐⭐⭐ |
| **RSI 超买超卖** | RSI>70 超买 / RSI<30 超卖 | ⭐⭐ |
| **跳空缺口** | 向上/向下跳空>1% | ⭐⭐ |
| **动态止盈** | 盈利 10%+ 后回撤 5%/10% | ⭐⭐⭐ |
### 📊 分级预警系统
- **🚨 紧急级**:多条件共振(如:放量 + 均线金叉 + 突破成本)
- **⚠️ 警告级**:2 个条件触发(如:RSI 超卖 + 放量)
- **📢 提醒级**:单一条件触发
### 🚀 运行方式
#### 后台常驻进程
```bash
cd ~/workspace/skills/stock-monitor/scripts
./control.sh start # 启动
./control.sh status # 查看状态
./control.sh log # 查看日志
./control.sh stop # 停止
```
### ⚡ 智能监控频率(北京时间)
| 时间段 | 频率 | 监控标的 |
|--------|------|----------|
| 交易时间 9:30-15:00 | 每 5 分钟 | 全部 + 技术指标 |
| 午休 11:30-13:00 | 每 10 分钟 | 全部 |
| 收盘后 15:00-24:00 | 每 30 分钟 | 全部(日线数据) |
| 凌晨 0:00-9:30 | 每 1 小时 | 仅伦敦金 |
| 周末 | 每 1 小时 | 仅伦敦金 |
### 📋 监控配置示例
```python
{
"code": "600362",
"name": "江西铜业",
"type": "individual", # 个股
"market": "sh",
"cost": 57.00, # 持仓成本
"alerts": {
# 1. 成本百分比
"cost_pct_above": 15.0, # 盈利 15% 提醒
"cost_pct_below": -12.0, # 亏损 12% 提醒
# 2. 日内涨跌幅
"change_pct_above": 4.0,
"change_pct_below": -4.0,
# 3. 成交量异动
"volume_surge": 2.0, # 放量>2 倍
# 4-7. 技术指标
"ma_monitor": True, # 均线金叉死叉
"rsi_monitor": True, # RSI 超买超卖
"gap_monitor": True, # 跳空缺口
"trailing_stop": True # 动态止盈
}
}
```
### 🔔 预警消息示例
#### 🚨 紧急级(多条件共振)
```
🚨【紧急】🔴 江西铜业 (600362)
━━━━━━━━━━━━━━━━━━━━
💰 当前价格:¥65.50 (+15.0%)
📊 持仓成本:¥57.00 | 盈亏:🔴+14.9%
🎯 触发预警 (3 项):
• 🎯 盈利 15% (目标价 ¥65.55)
• 🌟 均线金叉 (MA5¥63.2 上穿 MA10¥62.8)
• 📊 放量 2.5 倍 (5 日均量)
💡 建议:多条件共振,趋势强劲,可考虑继续持有或分批减仓。
```
#### ⚠️ 警告级
```
⚠️【警告】🟢 恒生医疗 (159892)
━━━━━━━━━━━━━━━━━━━━
💰 当前价格:¥0.72 (-10.0%)
🎯 触发预警 (2 项):
• 📉 日内大跌 -10.0%
• ❄️ RSI 超卖 (28.5),可能反弹
💡 建议:短期超跌严重,RSI 进入超卖区,关注反弹机会但勿急于抄底。
```
### 🛠️ 自定义配置
#### 修改成本价
```python
"cost": 55.50, # 改成你的实际成本
```
#### 调整预警阈值
```python
"cost_pct_above": 20.0, # 盈利 20% 提醒
"cost_pct_below": -15.0, # 亏损 15% 提醒
"change_pct_above": 5.0, # 日内异动±5%
"volume_surge": 3.0, # 放量 3 倍提醒
```
#### 开关技术指标
```python
"ma_monitor": False, # 关闭均线
"rsi_monitor": True, # 开启 RSI
"gap_monitor": True, # 开启跳空
```
### ⚠️ 使用提示
1. **技术指标有滞后性**:均线、MACD 等用于确认趋势而非预测
2. **避免过度交易**:预警只是参考,不要每个信号都操作
3. **多条件共振更可靠**:单一指标容易假信号
4. **动态止盈要灵活**:回撤 5% 减仓、10% 清仓是建议,根据市场调整
---
## 🔧 技能安装与管理
### 安装技能
```bash
clawhub install a-stock-trading-assistant
clawhub install daily-stock-analysis
clawhub install stock-monitor-skill
```
### 查看已安装技能
```bash
ls ~/.openclaw/workspace/skills/ | grep stock
```
### 更新技能
```bash
clawhub update a-stock-trading-assistant
clawhub update daily-stock-analysis
clawhub update stock-monitor-skill
```
---
## 📞 常见问题
### Q1: 数据源从哪里来?
- **a-stock-trading-assistant**:东方财富、新浪财经、同花顺、雪球
- **daily-stock-analysis**Yahoo Finance + 官方披露
- **stock-monitor-skill**:实时行情 API
### Q2: 支持港股和美股吗?
- **a-stock-trading-assistant**:仅支持 A 股
- **daily-stock-analysis**:支持全球股市
- **stock-monitor-skill**:主要支持 A 股,可配置港股/美股
### Q3: 预警怎么推送?
- 后台常驻进程运行 `monitor_daemon.py`
- 可配置微信、钉钉、飞书等推送渠道
- 建议配合券商 App 使用
### Q4: 预测准确率怎么样?
- 运行 `calc_accuracy.py` 查看滚动准确率
- 1 日/3 日/7 日/30 日准确率分别统计
- 自我进化机制持续提升
---
## 📝 更新日志
| 日期 | 更新内容 |
|------|----------|
| 2026-03-13 | 三大技能使用手册首次整理 |
---
*文档由皮皮虾整理 🦐 | 投资有风险,入市需谨慎*