Skill: shenghoo123-png/kay-stock-filter (version 0.1.0)

--- name: stock-filter-skills description: 股票多条件筛选、热门因子管理、Jiuyan 数据查询和抖音热点分析。提供 17 个 CLI 工具覆盖四大模块。 metadata: {"openclaw": {"requires": {"bins": ["node"], "env": ["STOCK_API_BASE_URL"]}, "primaryEnv": "STOCK_API_KEY", "emoji": "📈"}} --- # Stock Filter Skills 股票多条件筛选、热门因子管理、Jiuyan 数据查询和抖音热点分析。 当用户提到股票筛选、股票搜索、股票详情、股票对比、批量查询、股票分析、热门因子、因子预设、jiuyan/韭研、抖音热点等话题时，使用本技能。 ## Setup 安装依赖： ```bash cd {baseDir} && npm install ``` 需要配置以下环境变量（在 OpenClaw config 中设置 `skills.entries.stock-filter-skills.env`）： - `STOCK_API_BASE_URL`: API 服务地址（必填） - `STOCK_API_KEY`: API 认证密钥（必填，联系后端管理员申请） - `STOCK_API_TIMEOUT`: 请求超时秒数（可选，默认 30） ## 使用方式 所有工具通过 CLI 调用，格式为： ```bash node src/main.js <tool_name> '<JSON 参数>' ``` 无参数的工具直接调用： ```bash node src/main.js <tool_name> ``` 工作目录必须为 `{baseDir}`。 ## 工具列表 ### 股票筛选模块 **stock_filter** — 多条件筛选股票，支持分页排序 ```bash node src/main.js stock_filter '{"filters": {"market": ["sh"]}, "page": 1, "page_size": 20, "sort_field": "pe", "sort_order": "asc"}' ``` 参数: filters(object, 筛选条件), page(int, 页码), page_size(int, 每页数量), sort_field(string, 排序字段), sort_order(string, asc/desc) **stock_filter_options** — 获取所有可用筛选维度和选项（无参数） ```bash node src/main.js stock_filter_options ``` 使用 stock_filter 前应先调用本工具了解有哪些维度可用。 **stock_search** — 按关键词搜索股票 ```bash node src/main.js stock_search '{"keyword": "贵州茅台", "limit": 10}' ``` 参数: keyword(string, 必填, 搜索关键词), limit(int, 可选, 返回上限默认10) **stock_detail** — 获取股票详细指标（PE/PB/ROE/毛利率/负债率/营收/净利润/大单/概念板块） ```bash node src/main.js stock_detail '{"code": "600519"}' ``` 参数: code(string, 必填, 股票代码) **stock_detail_batch** — 批量获取多只股票详情，并发请求提高效率 ```bash node src/main.js stock_detail_batch '{"codes": ["600519", "000858", "000333"]}' ``` 参数: codes(array, 必填, 股票代码列表) 需要同时查看多只股票时使用本工具，比逐个调用 stock_detail 更高效。单只股票失败不影响其他结果。 **stock_compare** — 对比多只股票的指标，可指定对比字段 ```bash node src/main.js stock_compare '{"codes": ["600519", "000858"], "fields": ["pe", "pb", "roe", "gross_margin"]}' ``` 参数: codes(array, 必填, 股票代码列表), fields(array, 可选, 对比字段列表) 不传 fields 则返回所有指标。传入 fields 时仅返回指定字段的对比数据，结果更精简易读。 ### 热门因子模块 **hot_factor_list** — 获取因子预设列表（无参数） ```bash node src/main.js hot_factor_list ``` **hot_factor_create** — 创建因子预设 ```bash node src/main.js hot_factor_create '{"name": "成长股筛选", "factors": ["roe_high", "revenue_growth"]}' ``` 参数: name(string, 必填, 预设名称), factors(array, 必填, 因子ID列表) **hot_factor_update** — 更新因子预设 ```bash node src/main.js hot_factor_update '{"preset_id": "xxx", "name": "新名称"}' ``` 参数: preset_id(string, 必填), name(string, 可选), factors(array, 可选) **hot_factor_delete** — 删除因子预设（不可恢复） ```bash node src/main.js hot_factor_delete '{"preset_id": "xxx"}' ``` 参数: preset_id(string, 必填) **hot_factor_use** — 使用预设并增加计数 ```bash node src/main.js hot_factor_use '{"preset_id": "xxx"}' ``` 参数: preset_id(string, 必填) **hot_factor_sort** — 调整预设排序 ```bash node src/main.js hot_factor_sort '{"preset_ids": ["id1", "id2", "id3"]}' ``` 参数: preset_ids(array, 必填, 按期望顺序排列的ID列表) ### Jiuyan 数据模块 **jiuyan_stock_analysis** — 获取股票综合分析 ```bash node src/main.js jiuyan_stock_analysis '{"stock_code": "300236"}' ``` 参数: stock_code(string, 必填, 股票代码) **jiuyan_stock_theme** — 获取股票主题/题材数据 ```bash node src/main.js jiuyan_stock_theme '{"stock_code": "300236"}' ``` 参数: stock_code(string, 必填, 股票代码) **jiuyan_articles** — 批量获取文章详情 ```bash node src/main.js jiuyan_articles '{"article_ids": "id1,id2,id3"}' ``` 参数: article_ids(string, 必填, 逗号分隔的文章ID) ### 抖音热点模块 **douyin_hotspot_list** — 获取抖音热点列表 ```bash node src/main.js douyin_hotspot_list '{"page": 1, "page_size": 20}' ``` 参数: page(int, 可选, 默认1), page_size(int, 可选, 默认20) **douyin_hotspot_detail** — 获取热点详情 ```bash node src/main.js douyin_hotspot_detail '{"aweme_id": "xxx"}' ``` 参数: aweme_id(string, 必填, 热点ID) ## 错误处理 所有工具返回 JSON。错误时格式为 `{"error": "描述"}`。常见错误：认证失败（检查 API Key）、连接失败（检查服务是否启动）、参数错误。 ## 典型使用流程 1. 筛选股票：先 stock_filter_options 获取维度 → 再 stock_filter 执行筛选 2. 查股票：stock_search 搜索 → stock_detail 查详情 3. 批量查询：stock_detail_batch 一次获取多只股票详情 4. 对比股票：stock_compare 对比多只股票的关键指标 5. 分析股票：jiuyan_stock_analysis 获取分析 → jiuyan_stock_theme 查看主题 6. 管理预设：hot_factor_list 查看 → hot_factor_create/update/delete 操作 7. 看热点：douyin_hotspot_list 浏览 → douyin_hotspot_detail 查详情

path: README.md
content: # Stock Filter Skills for OpenClaw

股票多条件筛选、热门因子管理、Jiuyan 数据查询和抖音热点分析技能。

## 功能概览

| 模块 | 工具数 | 说明 |
|------|--------|------|
| 股票筛选 | 6 | 多条件筛选、搜索、详情、批量查询、对比 |
| 热门因子 | 6 | 预设的增删改查、排序和使用 |
| Jiuyan 数据 | 3 | 股票分析、主题、文章查询 |
| 抖音热点 | 2 | 热点列表和详情 |

共计 **17 个工具**。

## 安装

```bash
cd stock-filter-skills
npm install
```

要求 Node.js >= 18.0.0。

## 配置

### 方式一：环境变量（推荐）

```bash
export STOCK_API_BASE_URL="http://localhost:8000"
export STOCK_API_KEY="your-api-key-here"
export STOCK_API_TIMEOUT="30"
```

### 方式二：.env 文件

```bash
cp .env.example .env
# 编辑 .env 填写实际配置
```

### 方式三：OpenClaw config

参见 `config.json.example`。

## 使用方式

### 作为 OpenClaw Skill

将本目录安装为 OpenClaw Skill：

```bash
# 本地加载
openclaw skill load stock-filter-skills

# 或发布到 ClawHub
openclaw skill publish stock-filter-skills
```

安装后，在对话中提到"股票筛选"、"热门因子"、"股票分析"、"抖音热点"等触发词时，Agent 会自动调用对应工具。

### 命令行直接调用

```bash
# 查看所有工具
node src/main.js --help

# 搜索股票
node src/main.js stock_search '{"keyword": "贵州茅台"}'

# 筛选股票
node src/main.js stock_filter '{"filters": {"market": ["sh"]}, "page": 1}'

# 获取股票详情
node src/main.js stock_detail '{"code": "600519"}'

# 批量获取多只股票
node src/main.js stock_detail_batch '{"codes": ["600519", "000858", "000333"]}'

# 对比股票指标
node src/main.js stock_compare '{"codes": ["600519", "000858"], "fields": ["pe", "pb", "roe"]}'

# 获取热门因子
node src/main.js hot_factor_list

# Jiuyan 分析
node src/main.js jiuyan_stock_analysis '{"stock_code": "300236"}'

# 抖音热点
node src/main.js douyin_hotspot_list '{"page": 1}'
```

## API Key 认证

### 工作原理

所有请求通过 `X-API-Key` HTTP Header 传递密钥，后端验证通过后放行。

### 后端需要配合的事项

1. **创建 API Key 管理功能**：生成、存储（哈希）和验证 Key
2. **添加认证中间件**：检查 `X-API-Key` Header
3. **权限控制**：不同 Key 绑定不同权限

### 安全建议

- API Key 至少 32 字符，使用 UUID 或随机字符串
- 数据库存储 Key 的哈希值（SHA-256），不存明文
- 支持 Key 的吊销和过期机制
- 记录调用日志用于审计

## 项目结构

```
stock-filter-skills/
├── SKILL.md               # OpenClaw 技能定义（核心）
├── README.md              # 说明文档
├── config.json.example    # OpenClaw 配置模板
├── package.json           # Node.js 依赖与脚本
├── .env.example           # 环境变量模板
├── .gitignore
└── src/
    ├── main.js            # CLI 入口与工具调度
    ├── tools.js           # 15 个工具实现
    ├── config.js          # 配置与环境变量
    └── api-client.js      # 共享 HTTP 客户端
```

## 发布到 ClawHub

### 1. 安装 ClawHub CLI

```bash
npm i -g clawhub
```

### 2. 登录

```bash
clawhub login
```

浏览器会打开 ClawHub 登录页面（需要 GitHub 账号，且注册满 1 周）。

### 3. 发布

```bash
clawhub publish ./stock-filter-skills \
  --slug stock-filter-skills \
  --name "Stock Filter Skills" \
  --version 1.0.0 \
  --tags latest
```

### 4. 后续更新

```bash
clawhub publish ./stock-filter-skills \
  --slug stock-filter-skills \
  --version 1.1.0 \
  --changelog "新增抖音热点功能" \
  --tags latest
```

或使用批量同步：

```bash
clawhub sync --all
```

## License

MIT


path: _meta.json
content: ownerId: kn76fgzjjwj84mm60mdxzy8k8h831trg
slug: stock-filter-skills
version: 1.3.0
publishedAt: 1773805986664

path: package-lock.json
content: {
  "name": "stock-filter-skills",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "stock-filter-skills",
      "version": "1.0.0",
      "dependencies": {
        "dotenv": "^16.4.0"
      },
      "bin": {
        "stock-filter": "src/main.js"
      },
      "engines": {
        "node": ">=18.0.0"
      }
    },
    "node_modules/dotenv": {
      "version": "16.6.1",
      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "BSD-2-Clause",
      "engines": {
        "node": ">=12"
      },
      "funding": {
        "url": "https://dotenvx.com"
      }
    }
  }
}


path: package.json
content: {
  "name": "stock-filter-skills",
  "version": "1.3.0",
  "description": "股票筛选、热门因子管理、Jiuyan 数据查询和抖音热点分析 OpenClaw Skill",
  "type": "module",
  "main": "src/main.js",
  "bin": {
    "stock-filter": "src/main.js"
  },
  "scripts": {
    "start": "node src/main.js",
    "test": "node src/main.js --help"
  },
  "dependencies": {
    "dotenv": "^16.4.0"
  },
  "engines": {
    "node": ">=18.0.0"
  }
}


path: src/api-client.js
content: import { getApiUrl, getAuthHeaders, API_TIMEOUT } from "./config.js";

export async function apiRequest(path, { method = "GET", params, body } = {}) {
  let url = getApiUrl(path);
  if (params) {
    const qs = new URLSearchParams(
      Object.entries(params).filter(([, v]) => v != null)
    ).toString();
    if (qs) url += `?${qs}`;
  }

  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), API_TIMEOUT);

  try {
    const res = await fetch(url, {
      method,
      headers: getAuthHeaders(),
      body: body ? JSON.stringify(body) : undefined,
      signal: controller.signal,
    });

    const data = await res.json().catch(() => ({}));
    if (!res.ok) {
      const detail = data.detail || data.message || "";
      const detailStr = Array.isArray(detail) ? detail.join("; ") : String(detail);
      const statusMap = {
        400: "请求参数有误",
        401: "认证失败，请检查 API Key",
        403: "没有权限访问该资源",
        404: "资源不存在",
        422: "请求参数验证失败",
        429: "请求频率超限，请稍后重试",
        500: "服务器内部错误",
      };
      const msg = statusMap[res.status] || `API 请求失败 (${res.status})`;
      throw new Error(detailStr ? `${msg}。详情: ${detailStr}` : msg);
    }
    return data;
  } catch (e) {
    if (e.name === "AbortError") throw new Error("请求超时，请检查网络或稍后重试");
    if (e.cause?.code === "ECONNREFUSED") throw new Error("无法连接到 API 服务器，请检查服务是否已启动");
    throw e;
  } finally {
    clearTimeout(timer);
  }
}

export function formatJson(data) {
  return JSON.stringify(data, null, 2);
}


path: src/config.js
content: import { config as loadDotenv } from "dotenv";
import { resolve, dirname } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
loadDotenv({ path: resolve(__dirname, "..", ".env") });

export const API_BASE_URL = (process.env.STOCK_API_BASE_URL || "").replace(/\/+$/, "");
export const [REDACTED_SECRET] || "";
export const API_TIMEOUT = Number(process.env.STOCK_API_TIMEOUT || "30") * 1000;
export const API_PREFIX = "/api/v1";

export function getApiUrl(path) {
  if (!API_BASE_URL) {
    throw new Error("未设置 STOCK_API_BASE_URL 环境变量");
  }
  return `${API_BASE_URL}${API_PREFIX}${path}`;
}

export function getAuthHeaders() {
  const headers = { "Content-Type": "application/json", Accept: "application/json" };
  if (API_KEY) headers["X-API-Key"] = API_KEY;
  return headers;
}


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)