tickflow-assist 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +7 -39
  2. package/dist/analysis/types/composite-analysis.d.ts +27 -0
  3. package/dist/bootstrap.js +15 -4
  4. package/dist/config/tickflow-access.d.ts +2 -1
  5. package/dist/config/tickflow-access.js +10 -3
  6. package/dist/dev/tickflow-assist-cli.js +4 -3
  7. package/dist/dev/validate-mx-search.js +10 -2
  8. package/dist/plugin.js +4 -6
  9. package/dist/prompts/analysis/kline-analysis-user-prompt.js +2 -1
  10. package/dist/prompts/analysis/post-close-review-user-prompt.js +40 -1
  11. package/dist/prompts/analysis/pre-market-brief-prompt.d.ts +3 -1
  12. package/dist/prompts/analysis/pre-market-brief-prompt.js +5 -1
  13. package/dist/services/industry-peer-service.d.ts +9 -0
  14. package/dist/services/industry-peer-service.js +152 -0
  15. package/dist/services/jin10-flash-monitor-service.js +2 -1
  16. package/dist/services/monitor-service.js +3 -17
  17. package/dist/services/post-close-review-service.d.ts +11 -4
  18. package/dist/services/post-close-review-service.js +113 -10
  19. package/dist/services/pre-market-brief-service.js +165 -11
  20. package/dist/services/tickflow-client.d.ts +4 -1
  21. package/dist/services/tickflow-client.js +32 -0
  22. package/dist/services/tickflow-universe-service.d.ts +26 -0
  23. package/dist/services/tickflow-universe-service.js +213 -0
  24. package/dist/services/watchlist-profile-service.d.ts +4 -1
  25. package/dist/services/watchlist-profile-service.js +58 -29
  26. package/dist/services/watchlist-service.js +1 -1
  27. package/dist/storage/repositories/universe-membership-repo.d.ts +11 -0
  28. package/dist/storage/repositories/universe-membership-repo.js +38 -0
  29. package/dist/storage/repositories/universe-repo.d.ts +17 -0
  30. package/dist/storage/repositories/universe-repo.js +62 -0
  31. package/dist/storage/schemas.d.ts +2 -0
  32. package/dist/storage/schemas.js +13 -0
  33. package/dist/tools/add-stock.tool.d.ts +2 -1
  34. package/dist/tools/add-stock.tool.js +10 -1
  35. package/dist/tools/query-database.tool.js +6 -0
  36. package/dist/tools/refresh-watchlist-profiles.tool.d.ts +2 -1
  37. package/dist/tools/refresh-watchlist-profiles.tool.js +11 -1
  38. package/dist/tools/test-alert.tool.js +56 -19
  39. package/dist/types/tickflow.d.ts +12 -0
  40. package/dist/utils/tickflow-quote.d.ts +5 -0
  41. package/dist/utils/tickflow-quote.js +31 -0
  42. package/openclaw.plugin.json +79 -2
  43. package/package.json +5 -5
  44. package/skills/stock-analysis/SKILL.md +8 -18
@@ -1,3 +1,4 @@
1
+ import { performance } from "node:perf_hooks";
1
2
  import { formatChinaDateTime } from "../utils/china-time.js";
2
3
  export function testAlertTool(alertService, alertMediaService, configSource = "openclaw_plugin") {
3
4
  return {
@@ -5,38 +6,52 @@ export function testAlertTool(alertService, alertMediaService, configSource = "o
5
6
  description: "Send a test alert through the configured OpenClaw alert delivery path. Plugin mode includes PNG; local mode sends text only.",
6
7
  optional: true,
7
8
  async run() {
9
+ const totalStart = performance.now();
8
10
  const now = formatChinaDateTime();
9
11
  const message = alertService.formatSystemNotification("🧪 TickFlow 测试告警", [
10
12
  `时间: ${now}`,
11
13
  "说明: 这是一条手动触发的测试消息,用于验证文本与 PNG 告警卡投递链路正常。",
12
14
  ]);
13
15
  if (configSource === "local_config") {
16
+ const sendStart = performance.now();
14
17
  const result = await alertService.sendWithResult({ message });
18
+ const sendMs = performance.now() - sendStart;
19
+ const totalMs = performance.now() - totalStart;
15
20
  if (result.ok) {
16
21
  return [
17
22
  "✅ 测试告警文本已发送(本地命令模式)",
23
+ formatTimingLine({ sendMs, totalMs }),
18
24
  "说明: `npm run tool -- test_alert` 仅验证文本链路;请通过 `/ta_testalert` 验证 PNG 图片链路。",
19
25
  ].join("\n");
20
26
  }
21
27
  const detail = result.error ?? alertService.getLastError();
22
- return detail
23
- ? `❌ 测试告警发送失败\n原因: ${detail}`
24
- : "❌ 测试告警发送失败";
28
+ return [
29
+ "❌ 测试告警发送失败",
30
+ detail ? `原因: ${detail}` : null,
31
+ formatTimingLine({ sendMs, totalMs }),
32
+ ].filter(Boolean).join("\n");
25
33
  }
26
34
  let mediaFile = null;
35
+ let renderMs;
27
36
  try {
37
+ const renderStart = performance.now();
28
38
  mediaFile = await alertMediaService.writeAlertCard({
29
39
  symbol: "000001.SZ",
30
40
  ruleName: "test_alert",
31
41
  image: buildTestAlertImage(now),
32
42
  });
43
+ renderMs = performance.now() - renderStart;
33
44
  }
34
45
  catch (error) {
46
+ const sendStart = performance.now();
35
47
  const textOnlyResult = await alertService.sendWithResult({ message });
48
+ const sendMs = performance.now() - sendStart;
49
+ const totalMs = performance.now() - totalStart;
36
50
  if (textOnlyResult.ok) {
37
51
  return [
38
52
  "⚠️ 测试告警文本已发送,但 PNG 生成失败",
39
53
  `原因: ${formatErrorMessage(error)}`,
54
+ formatTimingLine({ sendMs, totalMs }),
40
55
  ].join("\n");
41
56
  }
42
57
  const detail = textOnlyResult.error ?? alertService.getLastError() ?? "未知错误";
@@ -44,38 +59,46 @@ export function testAlertTool(alertService, alertMediaService, configSource = "o
44
59
  "❌ 测试告警发送失败",
45
60
  `PNG 生成失败: ${formatErrorMessage(error)}`,
46
61
  `文本发送失败: ${detail}`,
62
+ formatTimingLine({ sendMs, totalMs }),
47
63
  ].join("\n");
48
64
  }
49
65
  try {
66
+ const sendStart = performance.now();
50
67
  const result = await alertService.sendWithResult({
51
68
  message,
52
69
  mediaPath: mediaFile.filePath,
53
70
  mediaLocalRoots: mediaFile.mediaLocalRoots,
54
71
  filename: mediaFile.filename,
55
72
  });
73
+ const sendMs = performance.now() - sendStart;
74
+ const totalMs = performance.now() - totalStart;
56
75
  if (result.deliveryUncertain) {
57
- return result.error
58
- ? `⚠️ PNG 告警疑似已送达,但通道返回异常;为避免重复未执行拆分补发\n原因: ${result.error}`
59
- : "⚠️ PNG 告警疑似已送达,但通道返回异常;为避免重复未执行拆分补发";
76
+ return [
77
+ "⚠️ PNG 告警疑似已送达,但通道返回异常;为避免重复未执行拆分补发",
78
+ result.error ? `原因: ${result.error}` : null,
79
+ formatTimingLine({ renderMs, sendMs, totalMs }),
80
+ ].filter(Boolean).join("\n");
60
81
  }
61
82
  if (result.ok && result.mediaDelivered) {
62
- if (result.error) {
63
- return [
64
- "⚠️ PNG 告警卡已发送,但文本补发失败",
65
- `原因: ${result.error}`,
66
- ].join("\n");
67
- }
68
- return "✅ 测试告警发送成功(文本 + PNG)";
83
+ return [
84
+ result.error ? "⚠️ PNG 告警卡已发送,但文本补发失败" : "✅ 测试告警发送成功(文本 + PNG)",
85
+ result.error ? `原因: ${result.error}` : null,
86
+ formatTimingLine({ renderMs, sendMs, totalMs }),
87
+ ].filter(Boolean).join("\n");
69
88
  }
70
89
  if (result.ok) {
71
- return result.error
72
- ? `⚠️ 测试告警文本已发送,但 PNG 未送达,已回退为纯文本\n原因: ${result.error}`
73
- : "⚠️ 测试告警文本已发送,但 PNG 未送达,已回退为纯文本";
90
+ return [
91
+ "⚠️ 测试告警文本已发送,但 PNG 未送达,已回退为纯文本",
92
+ result.error ? `原因: ${result.error}` : null,
93
+ formatTimingLine({ renderMs, sendMs, totalMs }),
94
+ ].filter(Boolean).join("\n");
74
95
  }
75
96
  const detail = result.error ?? alertService.getLastError();
76
- return detail
77
- ? `❌ 测试告警发送失败\n原因: ${detail}`
78
- : "❌ 测试告警发送失败";
97
+ return [
98
+ "❌ 测试告警发送失败",
99
+ detail ? `原因: ${detail}` : null,
100
+ formatTimingLine({ renderMs, sendMs, totalMs }),
101
+ ].filter(Boolean).join("\n");
79
102
  }
80
103
  finally {
81
104
  if (mediaFile) {
@@ -127,3 +150,17 @@ function formatErrorMessage(error) {
127
150
  }
128
151
  return String(error);
129
152
  }
153
+ function formatTimingLine(params) {
154
+ const parts = [
155
+ params.renderMs == null ? null : `PNG 生成 ${formatDuration(params.renderMs)}`,
156
+ `发送 ${formatDuration(params.sendMs)}`,
157
+ `总计 ${formatDuration(params.totalMs)}`,
158
+ ].filter((value) => Boolean(value));
159
+ return `耗时: ${parts.join(" | ")}`;
160
+ }
161
+ function formatDuration(valueMs) {
162
+ if (valueMs >= 1000) {
163
+ return `${(valueMs / 1000).toFixed(2)}s`;
164
+ }
165
+ return `${Math.round(valueMs)}ms`;
166
+ }
@@ -8,6 +8,7 @@ export interface TickFlowInstrument {
8
8
  }
9
9
  export interface TickFlowQuote {
10
10
  symbol: string;
11
+ name?: string | null;
11
12
  last_price: number;
12
13
  prev_close: number;
13
14
  timestamp: number;
@@ -17,6 +18,17 @@ export interface TickFlowQuote {
17
18
  change_pct?: number;
18
19
  };
19
20
  }
21
+ export interface TickFlowUniverseSummary {
22
+ id: string;
23
+ name: string;
24
+ description?: string | null;
25
+ region: string;
26
+ category: string;
27
+ symbol_count: number;
28
+ }
29
+ export interface TickFlowUniverseDetail extends TickFlowUniverseSummary {
30
+ symbols: string[];
31
+ }
20
32
  export interface TickFlowCompactKline {
21
33
  timestamp: number[];
22
34
  open: number[];
@@ -0,0 +1,5 @@
1
+ import type { TickFlowKlineRow, TickFlowQuote } from "../types/tickflow.js";
2
+ export declare function normalizeTickFlowChangePct(value: number | null | undefined): number | null;
3
+ export declare function deriveChangePctFromPrices(close: number | null | undefined, prevClose: number | null | undefined): number | null;
4
+ export declare function resolveTickFlowQuoteChangePct(quote: TickFlowQuote | null | undefined): number | null;
5
+ export declare function resolveTickFlowKlineChangePct(kline: Pick<TickFlowKlineRow, "close" | "prev_close"> | null | undefined): number | null;
@@ -0,0 +1,31 @@
1
+ export function normalizeTickFlowChangePct(value) {
2
+ if (value == null) {
3
+ return null;
4
+ }
5
+ const numericValue = Number(value);
6
+ if (!Number.isFinite(numericValue)) {
7
+ return null;
8
+ }
9
+ return numericValue * 100;
10
+ }
11
+ export function deriveChangePctFromPrices(close, prevClose) {
12
+ const closeValue = Number(close);
13
+ const prevCloseValue = Number(prevClose);
14
+ if (!Number.isFinite(closeValue) || !Number.isFinite(prevCloseValue) || prevCloseValue <= 0) {
15
+ return null;
16
+ }
17
+ return ((closeValue - prevCloseValue) / prevCloseValue) * 100;
18
+ }
19
+ export function resolveTickFlowQuoteChangePct(quote) {
20
+ if (!quote) {
21
+ return null;
22
+ }
23
+ return normalizeTickFlowChangePct(quote.ext?.change_pct)
24
+ ?? deriveChangePctFromPrices(quote.last_price, quote.prev_close);
25
+ }
26
+ export function resolveTickFlowKlineChangePct(kline) {
27
+ if (!kline) {
28
+ return null;
29
+ }
30
+ return deriveChangePctFromPrices(kline.close, kline.prev_close);
31
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "tickflow-assist",
3
3
  "name": "TickFlow Assist",
4
- "version": "0.3.6",
4
+ "version": "0.3.7",
5
5
  "description": "A-share watchlist analysis, monitoring, and alert delivery powered by TickFlow and OpenClaw.",
6
6
  "skills": [
7
7
  "skills"
@@ -9,6 +9,83 @@
9
9
  "activation": {
10
10
  "onCapabilities": ["tool", "hook"]
11
11
  },
12
+ "providerAuthEnvVars": {
13
+ "tickflow": [
14
+ "TICKFLOW_ASSIST_TICKFLOW_API_KEY",
15
+ "TICKFLOW_API_KEY"
16
+ ],
17
+ "llm": [
18
+ "TICKFLOW_ASSIST_LLM_API_KEY",
19
+ "LLM_API_KEY"
20
+ ],
21
+ "mx-search": [
22
+ "TICKFLOW_ASSIST_MX_SEARCH_API_KEY",
23
+ "MX_SEARCH_API_KEY",
24
+ "MX_APIKEY"
25
+ ],
26
+ "jin10": [
27
+ "TICKFLOW_ASSIST_JIN10_API_TOKEN",
28
+ "JIN10_API_TOKEN"
29
+ ]
30
+ },
31
+ "providerAuthChoices": [
32
+ {
33
+ "provider": "tickflow",
34
+ "method": "api-key",
35
+ "choiceId": "tickflow-api-key",
36
+ "choiceLabel": "TickFlow API key",
37
+ "groupId": "tickflow-assist",
38
+ "groupLabel": "TickFlow Assist",
39
+ "groupHint": "TickFlow, analysis LLM, and optional data-service credentials.",
40
+ "optionKey": "tickflowApiKey",
41
+ "cliFlag": "--tickflow-api-key",
42
+ "cliOption": "--tickflow-api-key <key>",
43
+ "cliDescription": "TickFlow API key",
44
+ "assistantVisibility": "manual-only"
45
+ },
46
+ {
47
+ "provider": "llm",
48
+ "method": "api-key",
49
+ "choiceId": "llm-api-key",
50
+ "choiceLabel": "LLM API key",
51
+ "groupId": "tickflow-assist",
52
+ "groupLabel": "TickFlow Assist",
53
+ "groupHint": "TickFlow, analysis LLM, and optional data-service credentials.",
54
+ "optionKey": "llmApiKey",
55
+ "cliFlag": "--llm-api-key",
56
+ "cliOption": "--llm-api-key <key>",
57
+ "cliDescription": "Analysis LLM API key",
58
+ "assistantVisibility": "manual-only"
59
+ },
60
+ {
61
+ "provider": "mx-search",
62
+ "method": "api-key",
63
+ "choiceId": "mx-search-api-key",
64
+ "choiceLabel": "MX Search API key",
65
+ "groupId": "tickflow-assist",
66
+ "groupLabel": "TickFlow Assist",
67
+ "groupHint": "TickFlow, analysis LLM, and optional data-service credentials.",
68
+ "optionKey": "mxSearchApiKey",
69
+ "cliFlag": "--mx-search-api-key",
70
+ "cliOption": "--mx-search-api-key <key>",
71
+ "cliDescription": "MX Search API key",
72
+ "assistantVisibility": "manual-only"
73
+ },
74
+ {
75
+ "provider": "jin10",
76
+ "method": "api-key",
77
+ "choiceId": "jin10-api-token",
78
+ "choiceLabel": "Jin10 API token",
79
+ "groupId": "tickflow-assist",
80
+ "groupLabel": "TickFlow Assist",
81
+ "groupHint": "TickFlow, analysis LLM, and optional data-service credentials.",
82
+ "optionKey": "jin10ApiToken",
83
+ "cliFlag": "--jin10-api-token",
84
+ "cliOption": "--jin10-api-token <token>",
85
+ "cliDescription": "Jin10 API token",
86
+ "assistantVisibility": "manual-only"
87
+ }
88
+ ],
12
89
  "setup": {
13
90
  "providers": [
14
91
  {
@@ -79,7 +156,7 @@
79
156
  },
80
157
  "tickflowApiKeyLevel": {
81
158
  "type": "string",
82
- "enum": ["Free", "Start", "Pro", "Expert"],
159
+ "enum": ["Free", "Starter", "Pro", "Expert"],
83
160
  "default": "Free",
84
161
  "description": "TickFlow subscription level. Pro and Expert enable intraday K-line fetching. Can also come from TICKFLOW_ASSIST_TICKFLOW_API_KEY_LEVEL or TICKFLOW_API_KEY_LEVEL."
85
162
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tickflow-assist",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "面向 A 股投资与盯盘场景的 OpenClaw 智能股票插件,基于 TickFlow API 提供实时监控、收盘后复盘、多维综合分析、关键价位跟踪与告警能力。OpenClaw smart stock plugin for A-share investing and watchlist workflows, powered by TickFlow API for realtime monitoring, post-close review, multi-dimensional analysis, key level tracking, and alerts.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -46,12 +46,12 @@
46
46
  },
47
47
  "openclaw": {
48
48
  "build": {
49
- "openclawVersion": "2026.4.11"
49
+ "openclawVersion": "2026.4.14"
50
50
  },
51
51
  "compat": {
52
52
  "pluginApi": ">=2026.3.31",
53
53
  "minGatewayVersion": "2026.3.31",
54
- "builtWithOpenClawVersion": "2026.4.11"
54
+ "builtWithOpenClawVersion": "2026.4.14"
55
55
  },
56
56
  "install": {
57
57
  "npmSpec": "tickflow-assist",
@@ -74,9 +74,9 @@
74
74
  },
75
75
  "devDependencies": {
76
76
  "@types/node": "^22.13.11",
77
- "openclaw": "^2026.4.11",
77
+ "openclaw": "^2026.4.14",
78
78
  "typescript": "^5.8.2"
79
79
  },
80
- "readme": "# TickFlow Assist\n\n基于 [OpenClaw](https://openclaw.ai) 的 A 股监控与分析插件。它使用 [TickFlow](https://tickflow.org/auth/register?ref=BUJ54JEDGE) 获取行情与财务数据,并可选接入 [金十数据 MCP](https://mcp.jin10.com/app/) 快讯流,结合 LLM 生成技术面、基本面、资讯面的综合判断,并把结果持久化到本地 LanceDB。\n\n最近更新:`v0.3.5` 对齐 OpenClaw `2026.4.11` metadata 与社区安装提示,修复源码升级时本地链接扫描 `node_modules` 失败的问题,并支持通过环境变量回退 TickFlow / LLM / MX / Jin10 配置。完整发布记录见 <https://github.com/robinspt/tickflow-assist/blob/main/CHANGELOG.md>。\n\n当前主线按 OpenClaw `v2026.3.31+` 对齐,并已验证社区安装在 `v2026.4.11` 上兼容。\n\n## 安装前准备\n\n在执行社区安装前,建议先确认你已经准备好以下配置:\n\n- 核心必需:`tickflowApiKey`、`llmApiKey`、`llmBaseUrl`、`llmModel`\n- 告警投递:`alertChannel`、`alertTarget`、`alertAccount`\n- 可选增强:`mxSearchApiKey`、`jin10ApiToken`\n\n其中,`configure-openclaw` 会把上述配置写入 `~/.openclaw/openclaw.json` 的 `plugins.entries[\"tickflow-assist\"].config`,插件启用后会在本地 `databasePath` 下持久化 LanceDB 数据,并运行监控 / 日更等后台服务。\n如果你不想把密钥写进配置文件,运行时也支持环境变量回退,优先级是 `openclaw.json / local.config.json` > 环境变量 > 默认值。\n常用环境变量:`TICKFLOW_ASSIST_TICKFLOW_API_KEY` / `TICKFLOW_API_KEY`、`TICKFLOW_ASSIST_LLM_API_KEY` / `LLM_API_KEY`、`TICKFLOW_ASSIST_LLM_BASE_URL` / `LLM_BASE_URL`、`TICKFLOW_ASSIST_LLM_MODEL` / `LLM_MODEL`、`TICKFLOW_ASSIST_MX_SEARCH_API_KEY` / `MX_SEARCH_API_KEY` / `MX_APIKEY`、`TICKFLOW_ASSIST_JIN10_API_TOKEN` / `JIN10_API_TOKEN`。\n如果你希望尽量避免把密钥落盘,推荐先把这些变量写进 `~/.openclaw/.env`,再运行配置向导补齐非密钥项。\n\n## 安装\n\n社区安装:\n\n```bash\nopenclaw plugins install tickflow-assist\nnode ~/.openclaw/extensions/tickflow-assist/dist/dev/tickflow-assist-cli.js configure-openclaw\ncd ~/.openclaw/extensions/tickflow-assist/python && uv sync\nopenclaw plugins enable tickflow-assist\nopenclaw config validate\nopenclaw gateway restart\n```\n\n安装阶段允许先落插件,再通过第二条命令写入 `tickflowApiKey`、`llmApiKey`、`llmBaseUrl`、`llmModel` 等正式配置。\n`configure-openclaw` 会写入 `~/.openclaw/openclaw.json` 中的 `plugins.entries[\"tickflow-assist\"].config`,并打印后续建议执行的命令;它不再自动执行 `openclaw`、`uv` 或系统包安装命令,也不会重新执行插件安装;如果你已经设置了环境变量,密钥项可留空,输入 `-` 可主动清空已有配置并切回环境变量。\n如果检测到 `plugins.installs[\"tickflow-assist\"]` 来自 `clawhub`,向导还会把被旧版本钉死的 `spec` 归一化为 `clawhub:tickflow-assist`,避免后续升级继续锁在旧版本。\n\n如果你希望先审阅配置,再只打印最少的后续步骤,可使用:\n\n```bash\nnode ~/.openclaw/extensions/tickflow-assist/dist/dev/tickflow-assist-cli.js configure-openclaw --no-enable --no-restart\n```\n\n如果你在 Linux 或 macOS 上需要 PNG 告警卡正常显示中文,请额外手动安装 `fontconfig` 与 Noto CJK 一类中文字体,例如:\n\n```bash\n# Debian / Ubuntu\nsudo apt-get update\nsudo apt-get install -y fontconfig fonts-noto-cjk\nfc-cache -fv\n\n# RHEL / Fedora / Rocky / AlmaLinux\nsudo dnf install -y fontconfig google-noto-sans-cjk-ttc-fonts\nfc-cache -fv\n\n# Arch / Manjaro\nsudo pacman -Sy --noconfirm fontconfig noto-fonts-cjk\nfc-cache -fv\n\n# Alpine\nsudo apk add fontconfig font-noto-cjk\nfc-cache -fv\n\n# macOS (Homebrew)\nbrew install fontconfig\nbrew install --cask font-noto-sans-cjk\nfc-cache -fv\n```\n\n社区安装后的升级方式:\n\n```bash\nopenclaw plugins update tickflow-assist\nopenclaw gateway restart\n```\n\n## 配置\n\n插件正式运行读取:\n\n```text\n~/.openclaw/openclaw.json\n```\n\n配置路径:\n\n```text\nplugins.entries[\"tickflow-assist\"].config\n```\n\n建议按完整功能显式填写以下字段,不要只填 API Key:\n\n- 核心运行:`tickflowApiKey`、`llmApiKey`、`llmBaseUrl`、`llmModel`\n- 本地数据:`databasePath`、`calendarFile`\n- 告警投递:`alertChannel`、`alertTarget`、`alertAccount`\n- 能力补充:`mxSearchApiKey`、`jin10ApiToken`\n\n其中,`mxSearchApiKey` 用于 `mx_search`、`mx_select_stock` 以及非 `Expert` 财务链路的 lite 补充;`jin10ApiToken` 用于 24 小时金十数据快讯监控;`jin10FlashNightAlert` 默认 `false`(开启夜间静默),设为 `true` 可恢复 24 小时快讯告警;`alertTarget`、`alertAccount` 建议在准备启用 `test_alert`、实时监控告警、金十数据快讯告警和定时通知前一并配好,避免配置不完整导致功能缺失。\n如果你使用环境变量,运行时支持以下回退:\n\n- `tickflowApiUrl`:`TICKFLOW_ASSIST_TICKFLOW_API_URL` / `TICKFLOW_API_URL`\n- `tickflowApiKey`:`TICKFLOW_ASSIST_TICKFLOW_API_KEY` / `TICKFLOW_API_KEY`\n- `tickflowApiKeyLevel`:`TICKFLOW_ASSIST_TICKFLOW_API_KEY_LEVEL` / `TICKFLOW_API_KEY_LEVEL`\n- `llmBaseUrl`:`TICKFLOW_ASSIST_LLM_BASE_URL` / `LLM_BASE_URL`\n- `llmApiKey`:`TICKFLOW_ASSIST_LLM_API_KEY` / `LLM_API_KEY`\n- `llmModel`:`TICKFLOW_ASSIST_LLM_MODEL` / `LLM_MODEL`\n- `mxSearchApiUrl`:`TICKFLOW_ASSIST_MX_SEARCH_API_URL` / `MX_SEARCH_API_URL`\n- `mxSearchApiKey`:`TICKFLOW_ASSIST_MX_SEARCH_API_KEY` / `MX_SEARCH_API_KEY` / `MX_APIKEY`\n- `jin10McpUrl`:`TICKFLOW_ASSIST_JIN10_MCP_URL` / `JIN10_MCP_URL`\n- `jin10ApiToken`:`TICKFLOW_ASSIST_JIN10_API_TOKEN` / `JIN10_API_TOKEN`\n\n## 功能\n\n- 自选股管理、日 K / 分钟 K 抓取与指标计算\n- 技术面、财务面、资讯面的综合分析\n- 实时监控、定时日更、收盘后复盘\n- 金十数据 24 小时快讯监控与自选关联提醒\n- 本地 LanceDB 数据留痕与分析结果查看\n\n## 运行说明\n\n- 插件会在本地 `databasePath` 下持久化 LanceDB 数据。\n- 后台服务会按配置执行定时日更、实时监控与金十数据快讯监控。\n- Python 子模块仅用于技术指标计算,不承担主业务流程。\n\n## 依赖与可选能力\n\n- [TickFlow](https://tickflow.org/auth/register?ref=BUJ54JEDGE):提供日线、分钟线、实时行情与财务数据接口。\n- [金十数据 MCP](https://mcp.jin10.com/app/):可选,用于 24 小时快讯流接入、自选关联筛选与事件驱动告警。\n- [东方财富妙想 Skills](https://marketing.dfcfs.com/views/finskillshub/):可选,用于 `mx_search`、`mx_select_stock` 与非 `Expert` 财务链路的 lite 补充。\n\n## 仓库\n\n- GitHub: [robinspt/tickflow-assist](https://github.com/robinspt/tickflow-assist)\n",
80
+ "readme": "# TickFlow Assist\n\n基于 [OpenClaw](https://openclaw.ai) 的 A 股监控与分析插件。它使用 [TickFlow](https://tickflow.org/auth/register?ref=BUJ54JEDGE) 获取行情与财务数据,并可选接入 [金十数据 MCP](https://mcp.jin10.com/app/) 快讯流,结合 LLM 生成技术面、基本面、资讯面的综合判断,并把结果持久化到本地 LanceDB。\n\n最近更新:`v0.3.7` 接入 TickFlow 标的池行业映射与申万三级同业上下文,优化盘前简报提炼与收盘复盘市场信息展示,并对齐 OpenClaw `v2026.4.14` 兼容与社区元数据。完整发布记录见 <https://github.com/robinspt/tickflow-assist/blob/main/CHANGELOG.md>。\n\n当前主线按 OpenClaw `v2026.3.31+` 对齐,并已验证社区安装在 `v2026.4.14` 上兼容。\n\n## 安装前准备\n\n在执行社区安装前,建议先确认你已经准备好以下配置:\n\n- 核心必需:`tickflowApiKey`、`llmApiKey`、`llmBaseUrl`、`llmModel`\n- 告警投递:`alertChannel`、`alertTarget`、`alertAccount`\n- 可选增强:`mxSearchApiKey`、`jin10ApiToken`\n\n其中,`configure-openclaw` 会把上述配置写入 `~/.openclaw/openclaw.json` 的 `plugins.entries[\"tickflow-assist\"].config`,插件启用后会在本地 `databasePath` 下持久化 LanceDB 数据,并运行监控 / 日更等后台服务。\n如果你不想把密钥写进配置文件,运行时也支持环境变量回退,优先级是 `openclaw.json / local.config.json` > 环境变量 > 默认值。\n常用环境变量:`TICKFLOW_ASSIST_TICKFLOW_API_KEY` / `TICKFLOW_API_KEY`、`TICKFLOW_ASSIST_LLM_API_KEY` / `LLM_API_KEY`、`TICKFLOW_ASSIST_LLM_BASE_URL` / `LLM_BASE_URL`、`TICKFLOW_ASSIST_LLM_MODEL` / `LLM_MODEL`、`TICKFLOW_ASSIST_MX_SEARCH_API_KEY` / `MX_SEARCH_API_KEY` / `MX_APIKEY`、`TICKFLOW_ASSIST_JIN10_API_TOKEN` / `JIN10_API_TOKEN`。\n如果你希望尽量避免把密钥落盘,推荐先把这些变量写进 `~/.openclaw/.env`,再运行配置向导补齐非密钥项。\n\n## 安装\n\n社区安装:\n\n```bash\nopenclaw plugins install tickflow-assist\nnode ~/.openclaw/extensions/tickflow-assist/dist/dev/tickflow-assist-cli.js configure-openclaw\ncd ~/.openclaw/extensions/tickflow-assist/python && uv sync\nopenclaw plugins enable tickflow-assist\nopenclaw config validate\nopenclaw gateway restart\n```\n\n- `configure-openclaw` 会把配置写入 `~/.openclaw/openclaw.json` `plugins.entries[\"tickflow-assist\"].config`。\n- 核心必填建议先准备:`tickflowApiKey`、`tickflowApiKeyLevel`、`llmApiKey`、`llmBaseUrl`、`llmModel`;告警场景再补 `alertChannel`、`alertTarget`、`alertAccount`。\n- 如果你不想把密钥落盘,优先把环境变量写进 `~/.openclaw/.env`,再运行配置向导补齐非密钥项;如需 PNG 告警卡正常显示中文,请自行安装 `fontconfig` 与 Noto CJK 字体。\n\n社区安装后的升级方式:\n\n```bash\nopenclaw plugins update tickflow-assist\nopenclaw gateway restart\n```\n\n## 配置\n\n插件正式运行读取:\n\n```text\n~/.openclaw/openclaw.json\n```\n\n配置路径:\n\n```text\nplugins.entries[\"tickflow-assist\"].config\n```\n\n建议按完整功能显式填写以下字段,不要只填 API Key:\n\n- 核心运行:`tickflowApiKey`、`llmApiKey`、`llmBaseUrl`、`llmModel`\n- 本地数据:`databasePath`、`calendarFile`\n- 告警投递:`alertChannel`、`alertTarget`、`alertAccount`\n- 能力补充:`mxSearchApiKey`、`jin10ApiToken`\n\n其中,`mxSearchApiKey` 用于 `mx_search`、`mx_select_stock` 以及非 `Expert` 财务链路的 lite 补充;`jin10ApiToken` 用于 24 小时金十数据快讯监控;`jin10FlashNightAlert` 默认 `false`(开启夜间静默),设为 `true` 可恢复 24 小时快讯告警;`alertTarget`、`alertAccount` 建议在准备启用 `test_alert`、实时监控告警、金十数据快讯告警和定时通知前一并配好,避免配置不完整导致功能缺失。\n如果你使用环境变量,运行时支持以下回退:\n\n- `tickflowApiUrl`:`TICKFLOW_ASSIST_TICKFLOW_API_URL` / `TICKFLOW_API_URL`\n- `tickflowApiKey`:`TICKFLOW_ASSIST_TICKFLOW_API_KEY` / `TICKFLOW_API_KEY`\n- `tickflowApiKeyLevel`:`TICKFLOW_ASSIST_TICKFLOW_API_KEY_LEVEL` / `TICKFLOW_API_KEY_LEVEL`\n- `llmBaseUrl`:`TICKFLOW_ASSIST_LLM_BASE_URL` / `LLM_BASE_URL`\n- `llmApiKey`:`TICKFLOW_ASSIST_LLM_API_KEY` / `LLM_API_KEY`\n- `llmModel`:`TICKFLOW_ASSIST_LLM_MODEL` / `LLM_MODEL`\n- `mxSearchApiUrl`:`TICKFLOW_ASSIST_MX_SEARCH_API_URL` / `MX_SEARCH_API_URL`\n- `mxSearchApiKey`:`TICKFLOW_ASSIST_MX_SEARCH_API_KEY` / `MX_SEARCH_API_KEY` / `MX_APIKEY`\n- `jin10McpUrl`:`TICKFLOW_ASSIST_JIN10_MCP_URL` / `JIN10_MCP_URL`\n- `jin10ApiToken`:`TICKFLOW_ASSIST_JIN10_API_TOKEN` / `JIN10_API_TOKEN`\n\n## 功能\n\n- 自选股管理、日 K / 分钟 K 抓取与指标计算\n- 技术面、财务面、资讯面的综合分析\n- 实时监控、定时日更、收盘后复盘\n- 金十数据 24 小时快讯监控与自选关联提醒\n- 本地 LanceDB 数据留痕与分析结果查看\n\n## 运行说明\n\n- 插件会在本地 `databasePath` 下持久化 LanceDB 数据。\n- 后台服务会按配置执行定时日更、实时监控与金十数据快讯监控。\n- Python 子模块仅用于技术指标计算,不承担主业务流程。\n\n## 依赖与可选能力\n\n- [TickFlow](https://tickflow.org/auth/register?ref=BUJ54JEDGE):`Free` 可用日线与实时行情;`Starter` 起可用标的池,插件会用来做申万行业映射与申万 3 级同业表现;`Pro` 起可用分钟K;`Expert` 才走 TickFlow 财务数据,非 `Expert` 默认回退妙想 lite。\n- [金十数据 MCP](https://mcp.jin10.com/app/):可选,用于 24 小时快讯流接入、自选关联筛选与事件驱动告警。独立的金十数据 Skill 详见 [OpenClaw Skill](https://clawhub.ai/robinspt/jin10) / [Hermes Skill](https://github.com/robinspt/hermes-skills)。\n- [东方财富妙想 Skills](https://marketing.dfcfs.com/views/finskillshub/):可选,用于 `mx_search`、`mx_select_stock` 与非 `Expert` 财务链路的 lite 补充。\n\n## 仓库\n\n- GitHub: [robinspt/tickflow-assist](https://github.com/robinspt/tickflow-assist)\n",
81
81
  "readmeFilename": "README.md"
82
82
  }
@@ -1,12 +1,7 @@
1
1
  ---
2
2
  name: stock_analysis
3
3
  description: Analyze A-share watchlist symbols, update daily K-line data, run monitor, and report status through the TickFlow Assist plugin.
4
- metadata:
5
- openclaw:
6
- skillKey: stock_analysis
7
- requires:
8
- config:
9
- - plugins.entries.tickflow-assist.enabled
4
+ metadata: {"openclaw":{"skillKey":"stock_analysis","always":true,"primaryEnv":"TICKFLOW_ASSIST_TICKFLOW_API_KEY","requires":{"config":["plugins.entries.tickflow-assist.enabled"],"env":["TICKFLOW_ASSIST_TICKFLOW_API_KEY","TICKFLOW_ASSIST_LLM_API_KEY","TICKFLOW_ASSIST_LLM_BASE_URL","TICKFLOW_ASSIST_LLM_MODEL"]}}}
10
5
  ---
11
6
  # 股票分析与监控
12
7
 
@@ -15,7 +10,7 @@ metadata:
15
10
 
16
11
  此技能随插件加载,不需要手动复制到 workspace。
17
12
 
18
- 优先使用 TickFlow Assist 插件工具,不要改用 `exec`、shell 命令、`node -e`、`python -c`、直接文件读写或数据库脚本,也不要在没有工具结果的前提下自行推断分析结论、监控状态或数据更新结果。
13
+ 优先通过 TickFlow Assist 插件工具完成相关任务,并以工具返回结果为准。
19
14
 
20
15
  适用场景:
21
16
  - 添加或删除自选股
@@ -57,15 +52,15 @@ metadata:
57
52
  - “测试告警” -> `test_alert`
58
53
 
59
54
  参数理解规则:
60
- - 对“添加自选 / 删除自选 / 查看自选 / 监控状态 / 日更状态”这类一跳即可完成的意图,首个动作必须直接调用对应插件工具;禁止先调用 `read`、`write`、`edit`、`query_database`、子代理、会话生成、环境探测工具,禁止先说“我先找一下方法”“我先确认工具”之类的话。
61
- - 当用户消息中已经包含足够参数时,必须直接执行,不得额外探索:
55
+ - 对“添加自选 / 删除自选 / 查看自选 / 监控状态 / 日更状态”这类直接型意图,如果用户已经给出足够参数,应直接调用对应插件工具。
56
+ - 当用户消息中已经包含足够参数时,不要额外补充假设:
62
57
  - “添加自选 601872” -> 直接调用 `add_stock`
63
58
  - “添加自选 601872 成本 5.32” -> 直接调用 `add_stock`
64
59
  - “删除自选 601872” -> 直接调用 `remove_stock`
65
60
  - “自选列表” -> 直接调用 `list_watchlist`
66
61
  - “查立讯精密最新研报” -> 直接调用 `mx_search`
67
62
  - “找今天涨幅 2% 的股票” -> 直接调用 `mx_select_stock`
68
- - 如果工具必需参数缺失,只能补充缺失项本身;不要以“我需要摸索当前环境里的工具”“我需要确认执行方式”为由拒绝或拖延。
63
+ - 如果工具必需参数缺失,只补充缺失项本身。
69
64
  - 股票代码按用户原始输入提取,例如 `002261`。
70
65
  - 成本价对应 `costPrice`;若用户未提供成本价,可以省略该字段。
71
66
  - `add_stock` 默认会在添加成功后自动拉取日K并计算指标。
@@ -74,18 +69,13 @@ metadata:
74
69
  - `update_all` 除了更新日K和日线指标,也会同步更新当日分钟K;本地分钟K默认仅保留近 30 个交易日。
75
70
  - `update_all` 是立即执行一次日更;`start_daily_update` / `stop_daily_update` 控制的是后台定时日更进程,两者不要混淆。
76
71
  - `backtest_key_levels` 默认回测全部关注股的活动价位;如果用户提到股票代码,应传入 `symbol`;如果用户提到“最近 N 次”,应传入 `recentLimit=N`。
77
- - 若配置中的 `tickflowApiKeyLevel` 为 `Free` 或 `Start`,则应自动跳过分钟K获取;若分钟K接口失败,也不要让 `analyze` 或 `update_all` 因此整体失败。
72
+ - 若配置中的 `tickflowApiKeyLevel` 为 `Free` 或 `Starter`,则应自动跳过分钟K获取;若分钟K接口失败,也不要让 `analyze` 或 `update_all` 因此整体失败。
78
73
  - 对新闻、公告、研报、政策、交易规则、具体事件、时效性影响分析等外部检索类问题,优先使用 `mx_search`,不要直接凭常识回答,也不要先读仓库文件再决定是否搜索。
79
74
  - 对自然语言选股、板块成分股、条件筛选、候选池推荐等任务,优先使用 `mx_select_stock`;若问题本质是“找哪些标的符合条件”,不要误用 `mx_search`。
80
75
  - 用户在“添加自选”意图中提到的“`N`天”对应 `add_stock.count`(或 `klineCount`),例如“添加 002261 成本 34.15 并获取 120 天日K”应调用 `add_stock`,其中 `count=120`。若用户未提供成本价但明确要求拉取 `N` 天日K,可只传 `symbol` 与 `count`。
81
76
  - 用户询问 TickFlow / 自选股 的日更状态时,必须调用 `daily_update_status`,不要把它解释成其他 crontab、系统任务或无关插件的定时更新。
82
- - 对 `daily_update_status`、`monitor_status`、`list_watchlist` 这类轻量状态查询,禁止使用 `sessions_spawn`、子代理、并行子任务、`query_database`、文件读取或任何“先分析再回答”的编排;必须在当前回合直接调用对应插件工具并返回结果。
83
- - `add_stock`、`remove_stock` 同样适用上述限制:不得先读文件、读目录、读 skill、自查工具列表或推测执行方法,必须直接调用工具。
84
- - `daily_update_status` 不依赖数据库查询工具;如果模型想改用 `query_database`、读取状态文件、读取交易日历文件,或拆成多个子任务,应视为错误策略并立即改回直接调用 `daily_update_status`。
85
- - 如果模型想调用 `read` 查看仓库、配置、数据库目录、SKILL 内容或工具脚本来决定是否添加/删除自选,应视为错误策略并立即改回 `add_stock` / `remove_stock`。
86
- - 禁止使用 `exec`、shell、`node -e`、`python -c`、SQL/LanceDB 脚本、直接编辑数据文件或任何“自己写脚本完成工具能力”的替代路径。
87
- - 如果当前环境拿不到对应插件工具,应直接说明无法通过该技能执行,不要回退到 `exec` 或命令行绕过。
88
- - 当用户追问“这个任务怎么执行的”“刚才是怎么做的”时,如果任务是通过插件工具完成的,应只说明实际调用的工具名与关键参数;不要默认展开成 `npm run tool -- ...`、shell 命令、`cd ~/projects/...`,也不要把本地调试命令当成对话里的真实执行链路。只有用户明确要求“给我命令行等价命令”时,才允许给出 CLI 示例。
77
+ - 如果当前会话没有暴露对应插件工具,应直接说明当前技能暂不可用。
78
+ - 当用户追问“这个任务怎么执行的”“刚才是怎么做的”时,如果任务是通过插件工具完成的,应只说明实际调用的工具名与关键参数;只有用户明确要求命令行等价命令时,再给出 CLI 示例。
89
79
  - 对 `daily_update_status` 的返回结果,不要额外追加“当前是 local_config”“插件配置问题”“已重试但结果相同”这类解释性警告。先完整原样输出工具结果;只有用户继续追问原因时,才再解释调用链路差异。
90
80
  - 仅在工具必需参数缺失时,才简短指出缺少的字段。
91
81
  - 不要臆造股票代码、成本价、日期、阈值、分析结果或监控状态。