tickflow-assist 0.2.0

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 (246) hide show
  1. package/README.md +151 -0
  2. package/day_future.txt +8797 -0
  3. package/dist/analysis/orchestrators/composite-analysis.orchestrator.d.ts +35 -0
  4. package/dist/analysis/orchestrators/composite-analysis.orchestrator.js +232 -0
  5. package/dist/analysis/parsers/json-block.parser.d.ts +1 -0
  6. package/dist/analysis/parsers/json-block.parser.js +13 -0
  7. package/dist/analysis/parsers/key-levels.parser.d.ts +5 -0
  8. package/dist/analysis/parsers/key-levels.parser.js +61 -0
  9. package/dist/analysis/parsers/post-close-review.parser.d.ts +2 -0
  10. package/dist/analysis/parsers/post-close-review.parser.js +79 -0
  11. package/dist/analysis/parsers/watchlist-profile.parser.d.ts +6 -0
  12. package/dist/analysis/parsers/watchlist-profile.parser.js +93 -0
  13. package/dist/analysis/providers/financial-analysis.provider.d.ts +12 -0
  14. package/dist/analysis/providers/financial-analysis.provider.js +85 -0
  15. package/dist/analysis/providers/market-analysis.provider.d.ts +27 -0
  16. package/dist/analysis/providers/market-analysis.provider.js +187 -0
  17. package/dist/analysis/providers/news-analysis.provider.d.ts +9 -0
  18. package/dist/analysis/providers/news-analysis.provider.js +60 -0
  19. package/dist/analysis/tasks/analysis-step-task.d.ts +11 -0
  20. package/dist/analysis/tasks/analysis-step-task.js +1 -0
  21. package/dist/analysis/tasks/analysis-task.d.ts +13 -0
  22. package/dist/analysis/tasks/analysis-task.js +1 -0
  23. package/dist/analysis/tasks/composite-stock-analysis.task.d.ts +17 -0
  24. package/dist/analysis/tasks/composite-stock-analysis.task.js +47 -0
  25. package/dist/analysis/tasks/financial-fundamental-lite.task.d.ts +10 -0
  26. package/dist/analysis/tasks/financial-fundamental-lite.task.js +21 -0
  27. package/dist/analysis/tasks/financial-fundamental.task.d.ts +12 -0
  28. package/dist/analysis/tasks/financial-fundamental.task.js +64 -0
  29. package/dist/analysis/tasks/kline-technical-signal.task.d.ts +10 -0
  30. package/dist/analysis/tasks/kline-technical-signal.task.js +41 -0
  31. package/dist/analysis/tasks/kline-technical.task.d.ts +32 -0
  32. package/dist/analysis/tasks/kline-technical.task.js +61 -0
  33. package/dist/analysis/tasks/news-catalyst.task.d.ts +11 -0
  34. package/dist/analysis/tasks/news-catalyst.task.js +62 -0
  35. package/dist/analysis/tasks/post-close-review.task.d.ts +12 -0
  36. package/dist/analysis/tasks/post-close-review.task.js +35 -0
  37. package/dist/analysis/types/composite-analysis.d.ts +123 -0
  38. package/dist/analysis/types/composite-analysis.js +1 -0
  39. package/dist/background/daily-update.worker.d.ts +50 -0
  40. package/dist/background/daily-update.worker.js +546 -0
  41. package/dist/background/realtime-monitor.worker.d.ts +8 -0
  42. package/dist/background/realtime-monitor.worker.js +28 -0
  43. package/dist/bootstrap.d.ts +45 -0
  44. package/dist/bootstrap.js +214 -0
  45. package/dist/config/normalize.d.ts +4 -0
  46. package/dist/config/normalize.js +99 -0
  47. package/dist/config/schema.d.ts +23 -0
  48. package/dist/config/schema.js +18 -0
  49. package/dist/config/tickflow-access.d.ts +4 -0
  50. package/dist/config/tickflow-access.js +28 -0
  51. package/dist/constants/market-indexes.d.ts +5 -0
  52. package/dist/constants/market-indexes.js +4 -0
  53. package/dist/dev/run-daily-update-loop.d.ts +1 -0
  54. package/dist/dev/run-daily-update-loop.js +48 -0
  55. package/dist/dev/run-monitor-loop.d.ts +1 -0
  56. package/dist/dev/run-monitor-loop.js +60 -0
  57. package/dist/dev/run-tool.d.ts +1 -0
  58. package/dist/dev/run-tool.js +49 -0
  59. package/dist/dev/tickflow-assist-cli.d.ts +2 -0
  60. package/dist/dev/tickflow-assist-cli.js +525 -0
  61. package/dist/dev/validate-mx-search.d.ts +1 -0
  62. package/dist/dev/validate-mx-search.js +212 -0
  63. package/dist/plugin-commands.d.ts +3 -0
  64. package/dist/plugin-commands.js +229 -0
  65. package/dist/plugin.d.ts +8 -0
  66. package/dist/plugin.js +151 -0
  67. package/dist/prompts/analysis/common-system-prompt.d.ts +1 -0
  68. package/dist/prompts/analysis/common-system-prompt.js +44 -0
  69. package/dist/prompts/analysis/composite-analysis-user-prompt.d.ts +11 -0
  70. package/dist/prompts/analysis/composite-analysis-user-prompt.js +102 -0
  71. package/dist/prompts/analysis/financial-analysis-user-prompt.d.ts +7 -0
  72. package/dist/prompts/analysis/financial-analysis-user-prompt.js +106 -0
  73. package/dist/prompts/analysis/financial-lite-analysis-user-prompt.d.ts +7 -0
  74. package/dist/prompts/analysis/financial-lite-analysis-user-prompt.js +59 -0
  75. package/dist/prompts/analysis/index.d.ts +8 -0
  76. package/dist/prompts/analysis/index.js +8 -0
  77. package/dist/prompts/analysis/kline-analysis-user-prompt.d.ts +13 -0
  78. package/dist/prompts/analysis/kline-analysis-user-prompt.js +215 -0
  79. package/dist/prompts/analysis/news-analysis-user-prompt.d.ts +8 -0
  80. package/dist/prompts/analysis/news-analysis-user-prompt.js +57 -0
  81. package/dist/prompts/analysis/post-close-review-user-prompt.d.ts +3 -0
  82. package/dist/prompts/analysis/post-close-review-user-prompt.js +129 -0
  83. package/dist/prompts/analysis/watchlist-profile-extraction-prompt.d.ts +7 -0
  84. package/dist/prompts/analysis/watchlist-profile-extraction-prompt.js +52 -0
  85. package/dist/prompts/analysis-system-prompt.d.ts +1 -0
  86. package/dist/prompts/analysis-system-prompt.js +1 -0
  87. package/dist/prompts/analysis-user-prompt.d.ts +1 -0
  88. package/dist/prompts/analysis-user-prompt.js +1 -0
  89. package/dist/runtime/daily-update-process.d.ts +3 -0
  90. package/dist/runtime/daily-update-process.js +24 -0
  91. package/dist/runtime/monitor-process.d.ts +3 -0
  92. package/dist/runtime/monitor-process.js +24 -0
  93. package/dist/runtime/plugin-api.d.ts +22 -0
  94. package/dist/runtime/plugin-api.js +2 -0
  95. package/dist/runtime/process-config.d.ts +8 -0
  96. package/dist/runtime/process-config.js +35 -0
  97. package/dist/services/alert-service.d.ts +45 -0
  98. package/dist/services/alert-service.js +198 -0
  99. package/dist/services/analysis-service.d.ts +20 -0
  100. package/dist/services/analysis-service.js +73 -0
  101. package/dist/services/analysis-view-service.d.ts +23 -0
  102. package/dist/services/analysis-view-service.js +343 -0
  103. package/dist/services/financial-lite-service.d.ts +23 -0
  104. package/dist/services/financial-lite-service.js +201 -0
  105. package/dist/services/financial-service.d.ts +20 -0
  106. package/dist/services/financial-service.js +47 -0
  107. package/dist/services/indicator-service.d.ts +9 -0
  108. package/dist/services/indicator-service.js +67 -0
  109. package/dist/services/instrument-service.d.ts +6 -0
  110. package/dist/services/instrument-service.js +21 -0
  111. package/dist/services/key-level-service.d.ts +2 -0
  112. package/dist/services/key-level-service.js +2 -0
  113. package/dist/services/key-levels-backtest-service.d.ts +71 -0
  114. package/dist/services/key-levels-backtest-service.js +427 -0
  115. package/dist/services/kline-service.d.ts +19 -0
  116. package/dist/services/kline-service.js +91 -0
  117. package/dist/services/monitor-service.d.ts +44 -0
  118. package/dist/services/monitor-service.js +598 -0
  119. package/dist/services/mx-search-service.d.ts +22 -0
  120. package/dist/services/mx-search-service.js +286 -0
  121. package/dist/services/post-close-review-service.d.ts +31 -0
  122. package/dist/services/post-close-review-service.js +402 -0
  123. package/dist/services/quote-service.d.ts +7 -0
  124. package/dist/services/quote-service.js +9 -0
  125. package/dist/services/review-memory-service.d.ts +7 -0
  126. package/dist/services/review-memory-service.js +76 -0
  127. package/dist/services/tickflow-client.d.ts +43 -0
  128. package/dist/services/tickflow-client.js +126 -0
  129. package/dist/services/trading-calendar-service.d.ts +20 -0
  130. package/dist/services/trading-calendar-service.js +102 -0
  131. package/dist/services/update-service.d.ts +21 -0
  132. package/dist/services/update-service.js +130 -0
  133. package/dist/services/watchlist-profile-service.d.ts +20 -0
  134. package/dist/services/watchlist-profile-service.js +76 -0
  135. package/dist/services/watchlist-service.d.ts +43 -0
  136. package/dist/services/watchlist-service.js +204 -0
  137. package/dist/storage/db.d.ts +23 -0
  138. package/dist/storage/db.js +70 -0
  139. package/dist/storage/repositories/alert-log-repo.d.ts +15 -0
  140. package/dist/storage/repositories/alert-log-repo.js +54 -0
  141. package/dist/storage/repositories/analysis-log-repo.d.ts +8 -0
  142. package/dist/storage/repositories/analysis-log-repo.js +48 -0
  143. package/dist/storage/repositories/composite-analysis-repo.d.ts +9 -0
  144. package/dist/storage/repositories/composite-analysis-repo.js +116 -0
  145. package/dist/storage/repositories/financial-analysis-repo.d.ts +9 -0
  146. package/dist/storage/repositories/financial-analysis-repo.js +107 -0
  147. package/dist/storage/repositories/indicators-repo.d.ts +8 -0
  148. package/dist/storage/repositories/indicators-repo.js +98 -0
  149. package/dist/storage/repositories/intraday-klines-repo.d.ts +9 -0
  150. package/dist/storage/repositories/intraday-klines-repo.js +102 -0
  151. package/dist/storage/repositories/key-levels-history-repo.d.ts +9 -0
  152. package/dist/storage/repositories/key-levels-history-repo.js +91 -0
  153. package/dist/storage/repositories/key-levels-repo.d.ts +9 -0
  154. package/dist/storage/repositories/key-levels-repo.js +83 -0
  155. package/dist/storage/repositories/klines-repo.d.ts +8 -0
  156. package/dist/storage/repositories/klines-repo.js +60 -0
  157. package/dist/storage/repositories/news-analysis-repo.d.ts +9 -0
  158. package/dist/storage/repositories/news-analysis-repo.js +107 -0
  159. package/dist/storage/repositories/technical-analysis-repo.d.ts +9 -0
  160. package/dist/storage/repositories/technical-analysis-repo.js +80 -0
  161. package/dist/storage/repositories/watchlist-repo.d.ts +10 -0
  162. package/dist/storage/repositories/watchlist-repo.js +124 -0
  163. package/dist/storage/schemas.d.ts +13 -0
  164. package/dist/storage/schemas.js +177 -0
  165. package/dist/tools/add-stock.tool.d.ts +13 -0
  166. package/dist/tools/add-stock.tool.js +123 -0
  167. package/dist/tools/analyze.tool.d.ts +8 -0
  168. package/dist/tools/analyze.tool.js +24 -0
  169. package/dist/tools/backtest-key-levels.tool.d.ts +8 -0
  170. package/dist/tools/backtest-key-levels.tool.js +43 -0
  171. package/dist/tools/daily-update-status.tool.d.ts +6 -0
  172. package/dist/tools/daily-update-status.tool.js +9 -0
  173. package/dist/tools/fetch-financials.tool.d.ts +8 -0
  174. package/dist/tools/fetch-financials.tool.js +224 -0
  175. package/dist/tools/fetch-intraday-klines.tool.d.ts +11 -0
  176. package/dist/tools/fetch-intraday-klines.tool.js +58 -0
  177. package/dist/tools/fetch-klines.tool.d.ts +11 -0
  178. package/dist/tools/fetch-klines.tool.js +61 -0
  179. package/dist/tools/list-watchlist.tool.d.ts +6 -0
  180. package/dist/tools/list-watchlist.tool.js +22 -0
  181. package/dist/tools/monitor-status.tool.d.ts +6 -0
  182. package/dist/tools/monitor-status.tool.js +9 -0
  183. package/dist/tools/mx-search.tool.d.ts +8 -0
  184. package/dist/tools/mx-search.tool.js +77 -0
  185. package/dist/tools/mx-select-stock.tool.d.ts +8 -0
  186. package/dist/tools/mx-select-stock.tool.js +118 -0
  187. package/dist/tools/query-database.tool.d.ts +8 -0
  188. package/dist/tools/query-database.tool.js +283 -0
  189. package/dist/tools/refresh-watchlist-names.tool.d.ts +7 -0
  190. package/dist/tools/refresh-watchlist-names.tool.js +17 -0
  191. package/dist/tools/refresh-watchlist-profiles.tool.d.ts +9 -0
  192. package/dist/tools/refresh-watchlist-profiles.tool.js +67 -0
  193. package/dist/tools/remove-stock.tool.d.ts +9 -0
  194. package/dist/tools/remove-stock.tool.js +27 -0
  195. package/dist/tools/start-daily-update.tool.d.ts +10 -0
  196. package/dist/tools/start-daily-update.tool.js +23 -0
  197. package/dist/tools/start-monitor.tool.d.ts +9 -0
  198. package/dist/tools/start-monitor.tool.js +52 -0
  199. package/dist/tools/stop-daily-update.tool.d.ts +9 -0
  200. package/dist/tools/stop-daily-update.tool.js +20 -0
  201. package/dist/tools/stop-monitor.tool.d.ts +9 -0
  202. package/dist/tools/stop-monitor.tool.js +44 -0
  203. package/dist/tools/test-alert.tool.d.ts +7 -0
  204. package/dist/tools/test-alert.tool.js +21 -0
  205. package/dist/tools/update-all.tool.d.ts +9 -0
  206. package/dist/tools/update-all.tool.js +17 -0
  207. package/dist/tools/view-analysis.tool.d.ts +8 -0
  208. package/dist/tools/view-analysis.tool.js +95 -0
  209. package/dist/types/daily-update.d.ts +26 -0
  210. package/dist/types/daily-update.js +1 -0
  211. package/dist/types/domain.d.ts +140 -0
  212. package/dist/types/domain.js +1 -0
  213. package/dist/types/indicator.d.ts +43 -0
  214. package/dist/types/indicator.js +1 -0
  215. package/dist/types/monitor.d.ts +17 -0
  216. package/dist/types/monitor.js +1 -0
  217. package/dist/types/mx-search.d.ts +14 -0
  218. package/dist/types/mx-search.js +1 -0
  219. package/dist/types/mx-select-stock.d.ts +28 -0
  220. package/dist/types/mx-select-stock.js +1 -0
  221. package/dist/types/tickflow.d.ts +133 -0
  222. package/dist/types/tickflow.js +1 -0
  223. package/dist/utils/abortable-sleep.d.ts +1 -0
  224. package/dist/utils/abortable-sleep.js +20 -0
  225. package/dist/utils/china-time.d.ts +3 -0
  226. package/dist/utils/china-time.js +18 -0
  227. package/dist/utils/cost-price.d.ts +3 -0
  228. package/dist/utils/cost-price.js +18 -0
  229. package/dist/utils/format.d.ts +1 -0
  230. package/dist/utils/format.js +3 -0
  231. package/dist/utils/process.d.ts +5 -0
  232. package/dist/utils/process.js +1 -0
  233. package/dist/utils/symbol.d.ts +1 -0
  234. package/dist/utils/symbol.js +13 -0
  235. package/docs/installation.md +391 -0
  236. package/docs/usage.md +244 -0
  237. package/openclaw.plugin.json +116 -0
  238. package/package.json +57 -0
  239. package/python/indicator_runner.py +31 -0
  240. package/python/indicators.py +148 -0
  241. package/python/pyproject.toml +9 -0
  242. package/python/requirements.txt +3 -0
  243. package/python/uv.lock +366 -0
  244. package/skills/database-query/SKILL.md +58 -0
  245. package/skills/stock-analysis/SKILL.md +106 -0
  246. package/skills/usage-help/SKILL.md +102 -0
@@ -0,0 +1,283 @@
1
+ const DEFAULT_LIMIT = 10;
2
+ const MAX_LIMIT = 100;
3
+ const TABLE_ALIASES = {
4
+ watchlist: "watchlist",
5
+ watchlists: "watchlist",
6
+ 自选: "watchlist",
7
+ 自选股: "watchlist",
8
+ klines: "klines_daily",
9
+ kline: "klines_daily",
10
+ klines_daily: "klines_daily",
11
+ 日k: "klines_daily",
12
+ 日线: "klines_daily",
13
+ intraday: "klines_intraday",
14
+ minute: "klines_intraday",
15
+ minutes: "klines_intraday",
16
+ klines_intraday: "klines_intraday",
17
+ 分钟k: "klines_intraday",
18
+ 分钟线: "klines_intraday",
19
+ 分时: "klines_intraday",
20
+ indicators: "indicators",
21
+ indicator: "indicators",
22
+ 指标: "indicators",
23
+ key_levels: "key_levels",
24
+ keylevel: "key_levels",
25
+ 关键价位: "key_levels",
26
+ key_levels_history: "key_levels_history",
27
+ keylevel_history: "key_levels_history",
28
+ active_key_levels: "key_levels_history",
29
+ 活动价位历史: "key_levels_history",
30
+ 关键价位历史: "key_levels_history",
31
+ analysis_log: "analysis_log",
32
+ analysis: "analysis_log",
33
+ 分析日志: "analysis_log",
34
+ technical_analysis: "technical_analysis",
35
+ technical: "technical_analysis",
36
+ technicals: "technical_analysis",
37
+ 技术分析: "technical_analysis",
38
+ financial_analysis: "financial_analysis",
39
+ financial: "financial_analysis",
40
+ fundamentals: "financial_analysis",
41
+ 基本面分析: "financial_analysis",
42
+ 财务分析: "financial_analysis",
43
+ news_analysis: "news_analysis",
44
+ news: "news_analysis",
45
+ research: "news_analysis",
46
+ 资讯分析: "news_analysis",
47
+ composite_analysis: "composite_analysis",
48
+ composite: "composite_analysis",
49
+ 综合分析: "composite_analysis",
50
+ alert_log: "alert_log",
51
+ alert: "alert_log",
52
+ 告警日志: "alert_log",
53
+ };
54
+ function parseInput(rawInput) {
55
+ if (rawInput == null || rawInput === "") {
56
+ return { action: "tables" };
57
+ }
58
+ if (typeof rawInput === "object" && rawInput !== null) {
59
+ const input = rawInput;
60
+ const action = input.action == null ? undefined : String(input.action).trim().toLowerCase();
61
+ const table = input.table == null ? undefined : normalizeTableName(String(input.table));
62
+ const symbol = input.symbol == null ? undefined : String(input.symbol).trim();
63
+ const limit = input.limit == null ? undefined : Number(input.limit);
64
+ const sortBy = input.sortBy == null ? undefined : String(input.sortBy).trim();
65
+ const sortOrderRaw = input.sortOrder == null ? undefined : String(input.sortOrder).trim().toLowerCase();
66
+ const fields = Array.isArray(input.fields)
67
+ ? input.fields.map((value) => String(value).trim()).filter(Boolean)
68
+ : undefined;
69
+ const filters = typeof input.filters === "object" && input.filters !== null
70
+ ? input.filters
71
+ : undefined;
72
+ const contains = input.contains == null ? undefined : String(input.contains).trim();
73
+ return {
74
+ action: normalizeAction(action),
75
+ table,
76
+ symbol,
77
+ limit: normalizeLimit(limit),
78
+ sortBy,
79
+ sortOrder: normalizeSortOrder(sortOrderRaw),
80
+ fields,
81
+ filters,
82
+ contains,
83
+ };
84
+ }
85
+ if (typeof rawInput === "string") {
86
+ const text = rawInput.trim();
87
+ if (!text) {
88
+ return { action: "tables" };
89
+ }
90
+ const parts = text.split(/\s+/);
91
+ const command = parts[0]?.toLowerCase();
92
+ if (command === "tables") {
93
+ return { action: "tables" };
94
+ }
95
+ if (command === "schema") {
96
+ return { action: "schema", table: normalizeTableName(parts[1] ?? "") };
97
+ }
98
+ return {
99
+ action: "query",
100
+ table: normalizeTableName(parts[0] ?? ""),
101
+ symbol: parts[1],
102
+ limit: normalizeLimit(parts[2] ? Number(parts[2]) : undefined),
103
+ };
104
+ }
105
+ throw new Error("invalid query-database input");
106
+ }
107
+ function normalizeAction(value) {
108
+ if (value === "schema" || value === "query" || value === "tables") {
109
+ return value;
110
+ }
111
+ return "query";
112
+ }
113
+ function normalizeTableName(value) {
114
+ const raw = (value ?? "").trim();
115
+ if (!raw) {
116
+ return undefined;
117
+ }
118
+ return TABLE_ALIASES[raw.toLowerCase()] ?? raw;
119
+ }
120
+ function normalizeLimit(value) {
121
+ if (value == null) {
122
+ return undefined;
123
+ }
124
+ if (!Number.isFinite(value) || value <= 0) {
125
+ throw new Error("query-database limit must be > 0");
126
+ }
127
+ return Math.min(Math.floor(value), MAX_LIMIT);
128
+ }
129
+ function normalizeSortOrder(value) {
130
+ if (value === "asc" || value === "desc") {
131
+ return value;
132
+ }
133
+ return undefined;
134
+ }
135
+ export function queryDatabaseTool(database) {
136
+ return {
137
+ name: "query_database",
138
+ description: "Inspect LanceDB tables, schemas, and rows for the plugin data store.",
139
+ async run({ rawInput }) {
140
+ const input = parseInput(rawInput);
141
+ if (input.action === "tables") {
142
+ return renderTableList(await database.listTables());
143
+ }
144
+ if (input.action === "schema") {
145
+ if (!input.table) {
146
+ throw new Error("query_database schema requires table");
147
+ }
148
+ return renderTableSchema(input.table, await database.describeTable(input.table));
149
+ }
150
+ if (!input.table) {
151
+ throw new Error("query_database query requires table");
152
+ }
153
+ const tables = await database.listTables();
154
+ if (!tables.includes(input.table)) {
155
+ return `⚠️ 数据表不存在: ${input.table}\n可用表: ${tables.join(", ") || "无"}`;
156
+ }
157
+ const rows = await database.tableToArray(input.table);
158
+ const filtered = filterRows(rows, input);
159
+ const sorted = sortRows(filtered, input.sortBy, input.sortOrder);
160
+ const limit = input.limit ?? DEFAULT_LIMIT;
161
+ const selected = sorted.slice(0, limit);
162
+ return renderQueryResult(input.table, rows.length, filtered.length, selected, input.fields);
163
+ },
164
+ };
165
+ }
166
+ function filterRows(rows, input) {
167
+ return rows.filter((row) => {
168
+ if (input.symbol != null && String(row.symbol ?? "") !== input.symbol) {
169
+ return false;
170
+ }
171
+ if (input.contains) {
172
+ const haystack = JSON.stringify(normalizeForDisplay(row)).toLowerCase();
173
+ if (!haystack.includes(input.contains.toLowerCase())) {
174
+ return false;
175
+ }
176
+ }
177
+ if (input.filters) {
178
+ for (const [key, expected] of Object.entries(input.filters)) {
179
+ if (!matchesFilter(row[key], expected)) {
180
+ return false;
181
+ }
182
+ }
183
+ }
184
+ return true;
185
+ });
186
+ }
187
+ function matchesFilter(actual, expected) {
188
+ if (Array.isArray(expected)) {
189
+ return expected.some((item) => matchesFilter(actual, item));
190
+ }
191
+ if (typeof expected === "string") {
192
+ return String(actual ?? "") === expected;
193
+ }
194
+ if (typeof expected === "number") {
195
+ return Number(actual) === expected;
196
+ }
197
+ if (typeof expected === "boolean") {
198
+ return Boolean(actual) === expected;
199
+ }
200
+ return String(actual ?? "") === String(expected ?? "");
201
+ }
202
+ function sortRows(rows, sortBy, sortOrder) {
203
+ if (!sortBy) {
204
+ return rows;
205
+ }
206
+ const direction = sortOrder === "asc" ? 1 : -1;
207
+ return [...rows].sort((left, right) => compareValues(left[sortBy], right[sortBy]) * direction);
208
+ }
209
+ function compareValues(left, right) {
210
+ if (left == null && right == null) {
211
+ return 0;
212
+ }
213
+ if (left == null) {
214
+ return -1;
215
+ }
216
+ if (right == null) {
217
+ return 1;
218
+ }
219
+ if (typeof left === "number" ||
220
+ typeof right === "number" ||
221
+ typeof left === "bigint" ||
222
+ typeof right === "bigint") {
223
+ return Number(left) - Number(right);
224
+ }
225
+ return String(left).localeCompare(String(right));
226
+ }
227
+ function renderTableList(tableNames) {
228
+ if (tableNames.length === 0) {
229
+ return "📚 当前 LanceDB 没有任何数据表";
230
+ }
231
+ return ["📚 当前 LanceDB 数据表:", ...tableNames.map((name) => `- ${name}`)].join("\n");
232
+ }
233
+ function renderTableSchema(table, fields) {
234
+ if (fields.length === 0) {
235
+ return `⚠️ 数据表不存在或没有结构信息: ${table}`;
236
+ }
237
+ const lines = [`🧱 表结构: ${table}`, `字段数: ${fields.length}`];
238
+ for (const field of fields) {
239
+ lines.push(`- ${field.name}: ${field.type} | nullable=${field.nullable ? "yes" : "no"}`);
240
+ }
241
+ return lines.join("\n");
242
+ }
243
+ function renderQueryResult(table, totalRows, filteredRows, rows, fields) {
244
+ const lines = [
245
+ `📦 数据表: ${table}`,
246
+ `总记录: ${totalRows}`,
247
+ `匹配记录: ${filteredRows}`,
248
+ `返回记录: ${rows.length}`,
249
+ ];
250
+ if (rows.length === 0) {
251
+ lines.push("⚠️ 没有匹配到数据");
252
+ return lines.join("\n");
253
+ }
254
+ rows.forEach((row, index) => {
255
+ lines.push(`${index + 1}. ${JSON.stringify(normalizeForDisplay(selectFields(row, fields)), null, 0)}`);
256
+ });
257
+ return lines.join("\n");
258
+ }
259
+ function selectFields(row, fields) {
260
+ if (!fields || fields.length === 0) {
261
+ return row;
262
+ }
263
+ const selected = {};
264
+ for (const field of fields) {
265
+ selected[field] = row[field];
266
+ }
267
+ return selected;
268
+ }
269
+ function normalizeForDisplay(value) {
270
+ if (typeof value === "bigint") {
271
+ return Number(value);
272
+ }
273
+ if (Array.isArray(value)) {
274
+ return value.map((item) => normalizeForDisplay(item));
275
+ }
276
+ if (value && typeof value === "object") {
277
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
278
+ key,
279
+ normalizeForDisplay(item),
280
+ ]));
281
+ }
282
+ return value;
283
+ }
@@ -0,0 +1,7 @@
1
+ import { WatchlistService } from "../services/watchlist-service.js";
2
+ export declare function refreshWatchlistNamesTool(watchlistService: WatchlistService): {
3
+ name: string;
4
+ description: string;
5
+ optional: boolean;
6
+ run(): Promise<string>;
7
+ };
@@ -0,0 +1,17 @@
1
+ export function refreshWatchlistNamesTool(watchlistService) {
2
+ return {
3
+ name: "refresh_watchlist_names",
4
+ description: "Refresh instrument names in watchlist from TickFlow metadata.",
5
+ optional: true,
6
+ async run() {
7
+ const result = await watchlistService.refreshNames();
8
+ const lines = [
9
+ `🔄 名称刷新完成: 更新 ${result.updated.length} 只, 保持不变 ${result.unchanged.length} 只`,
10
+ ];
11
+ for (const item of result.updated) {
12
+ lines.push(`• ${item.symbol} -> ${item.name}`);
13
+ }
14
+ return lines.join("\n");
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,9 @@
1
+ import { WatchlistService } from "../services/watchlist-service.js";
2
+ export declare function refreshWatchlistProfilesTool(watchlistService: WatchlistService): {
3
+ name: string;
4
+ description: string;
5
+ optional: boolean;
6
+ run({ rawInput }: {
7
+ rawInput?: unknown;
8
+ }): Promise<string>;
9
+ };
@@ -0,0 +1,67 @@
1
+ import { normalizeSymbol } from "../utils/symbol.js";
2
+ function parseInput(rawInput) {
3
+ if (typeof rawInput === "object" && rawInput !== null) {
4
+ const input = rawInput;
5
+ const symbol = String(input.symbol ?? "").trim();
6
+ const forceRaw = input.force;
7
+ return {
8
+ symbol: symbol || undefined,
9
+ force: typeof forceRaw === "boolean" ? forceRaw : undefined,
10
+ };
11
+ }
12
+ if (typeof rawInput === "string") {
13
+ const symbol = rawInput.trim();
14
+ return symbol ? { symbol } : {};
15
+ }
16
+ return {};
17
+ }
18
+ export function refreshWatchlistProfilesTool(watchlistService) {
19
+ return {
20
+ name: "refresh_watchlist_profiles",
21
+ description: "Refresh industry and concept metadata for the whole watchlist or one symbol.",
22
+ optional: true,
23
+ async run({ rawInput }) {
24
+ const input = parseInput(rawInput);
25
+ const result = await watchlistService.refreshProfiles({
26
+ symbol: input.symbol,
27
+ force: input.force ?? true,
28
+ });
29
+ if (result.targetCount === 0) {
30
+ return input.symbol
31
+ ? `⚠️ 自选列表中未找到该股票: ${normalizeSymbol(input.symbol)}`
32
+ : "⚠️ 当前自选列表为空";
33
+ }
34
+ const lines = [
35
+ `🔄 行业分类/概念板块刷新完成: 目标 ${result.targetCount} 只 | 资料更新 ${result.updated.length} | 已复核 ${result.rechecked.length} | 失败 ${result.failed.length}`,
36
+ ];
37
+ if (result.updated.length > 0) {
38
+ lines.push("", "已更新:");
39
+ for (const item of result.updated) {
40
+ lines.push(formatProfileLine(item));
41
+ }
42
+ }
43
+ if (input.symbol && result.rechecked.length > 0) {
44
+ lines.push("", "复核结果:");
45
+ for (const item of result.rechecked) {
46
+ lines.push(formatProfileLine(item));
47
+ }
48
+ }
49
+ if (!input.symbol && result.rechecked.length > 0) {
50
+ lines.push("", `• 已复核 ${result.rechecked.length} 只,资料未变化`);
51
+ }
52
+ if (result.failed.length > 0) {
53
+ lines.push("", "失败:");
54
+ for (const item of result.failed) {
55
+ lines.push(`• ${item.name || item.symbol}(${item.symbol}): ${item.error}`);
56
+ }
57
+ }
58
+ return lines.join("\n");
59
+ },
60
+ };
61
+ }
62
+ function formatProfileLine(item) {
63
+ const sector = item.sector ?? "未识别";
64
+ const themes = item.themes.length > 0 ? item.themes.join("、") : "未识别";
65
+ const updatedAt = item.themeUpdatedAt ?? "未记录";
66
+ return `• ${item.name}(${item.symbol}) | 行业分类: ${sector} | 概念板块: ${themes} | 更新时间: ${updatedAt}`;
67
+ }
@@ -0,0 +1,9 @@
1
+ import { WatchlistService } from "../services/watchlist-service.js";
2
+ export declare function removeStockTool(watchlistService: WatchlistService): {
3
+ name: string;
4
+ description: string;
5
+ optional: boolean;
6
+ run({ rawInput }: {
7
+ rawInput?: unknown;
8
+ }): Promise<string>;
9
+ };
@@ -0,0 +1,27 @@
1
+ function parseSymbol(rawInput) {
2
+ if (typeof rawInput === "string" && rawInput.trim()) {
3
+ return rawInput.trim();
4
+ }
5
+ if (typeof rawInput === "object" && rawInput !== null) {
6
+ const symbol = String(rawInput.symbol ?? "").trim();
7
+ if (symbol) {
8
+ return symbol;
9
+ }
10
+ }
11
+ throw new Error("remove-stock requires a symbol");
12
+ }
13
+ export function removeStockTool(watchlistService) {
14
+ return {
15
+ name: "remove_stock",
16
+ description: "Remove a symbol from the watchlist.",
17
+ optional: true,
18
+ async run({ rawInput }) {
19
+ const symbol = parseSymbol(rawInput);
20
+ const removed = await watchlistService.remove(symbol);
21
+ if (!removed) {
22
+ return `⚠️ 未找到该股票: ${symbol}`;
23
+ }
24
+ return `✅ 已从关注列表移除: ${symbol}`;
25
+ },
26
+ };
27
+ }
@@ -0,0 +1,10 @@
1
+ import type { PluginConfig } from "../config/schema.js";
2
+ import { DailyUpdateWorker } from "../background/daily-update.worker.js";
3
+ export declare function startDailyUpdateTool(dailyUpdateWorker: DailyUpdateWorker, config: PluginConfig, configSource: "openclaw_plugin" | "local_config", runtime: {
4
+ pluginManagedServices: boolean;
5
+ }): {
6
+ name: string;
7
+ description: string;
8
+ optional: boolean;
9
+ run(): Promise<string>;
10
+ };
@@ -0,0 +1,23 @@
1
+ export function startDailyUpdateTool(dailyUpdateWorker, config, configSource, runtime) {
2
+ return {
3
+ name: "start_daily_update",
4
+ description: "Start the TickFlow daily-update scheduler.",
5
+ optional: true,
6
+ async run() {
7
+ const result = runtime.pluginManagedServices
8
+ ? await dailyUpdateWorker.enableManagedLoop(configSource)
9
+ : await dailyUpdateWorker.ensureLoopRunning(config, configSource);
10
+ if (!result.started) {
11
+ return await dailyUpdateWorker.getStatusReport();
12
+ }
13
+ const lines = [
14
+ "✅ TickFlow 定时日更已启动",
15
+ `PID: ${result.pid ?? "托管服务"}`,
16
+ `运行方式: ${runtime.pluginManagedServices ? "plugin_service" : "project_scheduler"}`,
17
+ `配置来源: ${configSource}`,
18
+ "说明: 后台按 15 分钟对齐轮询,交易日 15:25 后最多执行一次日更,20:00 后最多执行一次复盘",
19
+ ];
20
+ return lines.join("\n");
21
+ },
22
+ };
23
+ }
@@ -0,0 +1,9 @@
1
+ import { MonitorService } from "../services/monitor-service.js";
2
+ export declare function startMonitorTool(monitorService: MonitorService, runtime: {
3
+ pluginManagedServices: boolean;
4
+ }): {
5
+ name: string;
6
+ description: string;
7
+ optional: boolean;
8
+ run(): Promise<string>;
9
+ };
@@ -0,0 +1,52 @@
1
+ import { spawn } from "node:child_process";
2
+ import path from "node:path";
3
+ export function startMonitorTool(monitorService, runtime) {
4
+ return {
5
+ name: "start_monitor",
6
+ description: "Start logical monitor state and return startup summary.",
7
+ optional: true,
8
+ async run() {
9
+ if (runtime.pluginManagedServices) {
10
+ const result = await monitorService.enableManagedLoop();
11
+ if (!result.started) {
12
+ return await monitorService.getStatusReport();
13
+ }
14
+ return [
15
+ "✅ TickFlow 实时监控已启动",
16
+ "运行方式: plugin_service",
17
+ `轮询间隔: 由后台服务管理`,
18
+ "说明: 后台服务按配置间隔轮询,交易时段自动执行监控",
19
+ ].join("\n");
20
+ }
21
+ const currentState = await monitorService.getState();
22
+ if (currentState.running
23
+ && currentState.workerPid != null
24
+ && isPidAlive(currentState.workerPid)) {
25
+ return await monitorService.getStatusReport();
26
+ }
27
+ const summary = await monitorService.start();
28
+ const workerPid = spawnMonitorLoop();
29
+ await monitorService.setWorkerPid(workerPid);
30
+ return summary;
31
+ },
32
+ };
33
+ }
34
+ function spawnMonitorLoop() {
35
+ const scriptPath = path.resolve("dist/dev/run-monitor-loop.js");
36
+ const child = spawn(process.execPath, [scriptPath], {
37
+ cwd: process.cwd(),
38
+ detached: true,
39
+ stdio: "ignore",
40
+ });
41
+ child.unref();
42
+ return child.pid ?? null;
43
+ }
44
+ function isPidAlive(pid) {
45
+ try {
46
+ process.kill(pid, 0);
47
+ return true;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
@@ -0,0 +1,9 @@
1
+ import { DailyUpdateWorker } from "../background/daily-update.worker.js";
2
+ export declare function stopDailyUpdateTool(dailyUpdateWorker: DailyUpdateWorker, runtime: {
3
+ pluginManagedServices: boolean;
4
+ }): {
5
+ name: string;
6
+ description: string;
7
+ optional: boolean;
8
+ run(): Promise<string>;
9
+ };
@@ -0,0 +1,20 @@
1
+ export function stopDailyUpdateTool(dailyUpdateWorker, runtime) {
2
+ return {
3
+ name: "stop_daily_update",
4
+ description: "Stop the TickFlow daily-update scheduler.",
5
+ optional: true,
6
+ async run() {
7
+ const result = runtime.pluginManagedServices
8
+ ? { stopped: (await dailyUpdateWorker.getState()).running, pid: null }
9
+ : await dailyUpdateWorker.stopLoop();
10
+ await dailyUpdateWorker.markSchedulerStopped();
11
+ if (!result.stopped) {
12
+ return "✅ TickFlow 定时日更已停止";
13
+ }
14
+ return [
15
+ "🛑 TickFlow 定时日更已停止",
16
+ `PID: ${result.pid ?? "托管服务"}`,
17
+ ].join("\n");
18
+ },
19
+ };
20
+ }
@@ -0,0 +1,9 @@
1
+ import { MonitorService } from "../services/monitor-service.js";
2
+ export declare function stopMonitorTool(monitorService: MonitorService, runtime: {
3
+ pluginManagedServices: boolean;
4
+ }): {
5
+ name: string;
6
+ description: string;
7
+ optional: boolean;
8
+ run(): Promise<string>;
9
+ };
@@ -0,0 +1,44 @@
1
+ export function stopMonitorTool(monitorService, runtime) {
2
+ return {
3
+ name: "stop_monitor",
4
+ description: "Stop logical monitor state and return stop summary.",
5
+ optional: true,
6
+ async run() {
7
+ if (runtime.pluginManagedServices) {
8
+ const wasRunning = (await monitorService.getState()).running;
9
+ await monitorService.markStopped();
10
+ if (!wasRunning) {
11
+ return "✅ TickFlow 实时监控已停止";
12
+ }
13
+ return [
14
+ "🛑 TickFlow 实时监控已停止",
15
+ "运行方式: plugin_service(后台服务循环仍在,但不再执行监控)",
16
+ ].join("\n");
17
+ }
18
+ const state = await monitorService.getState();
19
+ const shouldSignalWorker = state.workerPid != null && isPidAlive(state.workerPid);
20
+ if (shouldSignalWorker) {
21
+ await monitorService.setExpectedStop(true);
22
+ }
23
+ const summary = await monitorService.stop();
24
+ if (shouldSignalWorker && state.workerPid) {
25
+ try {
26
+ process.kill(state.workerPid, "SIGTERM");
27
+ }
28
+ catch {
29
+ // Best-effort stop for the dev/VPS worker.
30
+ }
31
+ }
32
+ return summary;
33
+ },
34
+ };
35
+ }
36
+ function isPidAlive(pid) {
37
+ try {
38
+ process.kill(pid, 0);
39
+ return true;
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ }
@@ -0,0 +1,7 @@
1
+ import { AlertService } from "../services/alert-service.js";
2
+ export declare function testAlertTool(alertService: AlertService): {
3
+ name: string;
4
+ description: string;
5
+ optional: boolean;
6
+ run(): Promise<string>;
7
+ };
@@ -0,0 +1,21 @@
1
+ import { formatChinaDateTime } from "../utils/china-time.js";
2
+ export function testAlertTool(alertService) {
3
+ return {
4
+ name: "test_alert",
5
+ description: "Send a test alert through the configured OpenClaw alert delivery path.",
6
+ optional: true,
7
+ async run() {
8
+ const ok = await alertService.send(alertService.formatSystemNotification("🧪 TickFlow 测试告警", [
9
+ `时间: ${formatChinaDateTime()}`,
10
+ "说明: 这是一条手动触发的测试消息,用于验证 channel 投递链路正常。",
11
+ ]));
12
+ if (ok) {
13
+ return "✅ 测试告警发送成功";
14
+ }
15
+ const detail = alertService.getLastError();
16
+ return detail
17
+ ? `❌ 测试告警发送失败\n原因: ${detail}`
18
+ : "❌ 测试告警发送失败";
19
+ },
20
+ };
21
+ }
@@ -0,0 +1,9 @@
1
+ import { DailyUpdateWorker } from "../background/daily-update.worker.js";
2
+ export declare function updateAllTool(dailyUpdateWorker: DailyUpdateWorker): {
3
+ name: string;
4
+ description: string;
5
+ optional: boolean;
6
+ run({ rawInput }: {
7
+ rawInput?: unknown;
8
+ }): Promise<string>;
9
+ };
@@ -0,0 +1,17 @@
1
+ export function updateAllTool(dailyUpdateWorker) {
2
+ return {
3
+ name: "update_all",
4
+ description: "Batch update daily/intraday market data for all watchlist symbols, then run post-close analysis and key-level backtest.",
5
+ optional: true,
6
+ async run({ rawInput }) {
7
+ let force = false;
8
+ if (typeof rawInput === "object" && rawInput !== null) {
9
+ force = Boolean(rawInput.force);
10
+ }
11
+ if (typeof rawInput === "string") {
12
+ force = rawInput.includes("--force");
13
+ }
14
+ return dailyUpdateWorker.run(force);
15
+ },
16
+ };
17
+ }