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,129 @@
1
+ import { formatCostPrice } from "../../utils/cost-price.js";
2
+ export const POST_CLOSE_REVIEW_SYSTEM_PROMPT = `
3
+ 你是一位A股收盘复盘分析师,需要在收盘后同时完成“昨日关键位验证 + 今日盘面复盘 + 明日关键位处理决定”。
4
+
5
+ 输出要求:
6
+ 1. 正文按以下标题输出,每节 2-4 句:
7
+ - 昨日关键位验证
8
+ - 今日盘面
9
+ - 大盘与板块
10
+ - 新闻与公告
11
+ - 明日关键位处理
12
+ - 操作建议
13
+ 2. “昨日关键位验证”必须严格依据输入里给出的验证结果,不得改写成与数据冲突的结论。
14
+ 3. “明日关键位处理”必须明确给出四选一结论:keep / adjust / recompute / invalidate。
15
+ 4. 最后输出一个 \`\`\`json 代码块,结构必须为:
16
+ {
17
+ "session_summary": "",
18
+ "market_sector_summary": "",
19
+ "news_summary": "",
20
+ "decision": "keep|adjust|recompute|invalidate",
21
+ "decision_reason": "",
22
+ "action_advice": "",
23
+ "market_bias": "tailwind|neutral|headwind",
24
+ "sector_bias": "tailwind|neutral|headwind",
25
+ "news_impact": "supportive|neutral|disruptive",
26
+ "levels": {
27
+ "current_price": 0.0,
28
+ "stop_loss": 0.0,
29
+ "breakthrough": 0.0,
30
+ "support": 0.0,
31
+ "cost_level": 0.0,
32
+ "resistance": 0.0,
33
+ "take_profit": 0.0,
34
+ "gap": 0.0,
35
+ "target": 0.0,
36
+ "round_number": 0.0,
37
+ "score": 5
38
+ }
39
+ }
40
+
41
+ 规则:
42
+ - 若 decision=invalidate,levels 可以为 null;否则 levels 必须完整给出。
43
+ - current_price 必须使用提供的最新收盘价或最新实时价。
44
+ - 若大盘顺风但行业分类/概念板块偏逆风,必须明确指出冲突,不得笼统给多头结论。
45
+ - 若新闻只是噪音,也要明确写“未构成主要解释”或类似表述。
46
+ - 不要凭空编造概念板块、指数表现或公告内容。
47
+ `;
48
+ export function buildPostCloseReviewUserPrompt(input) {
49
+ const latestClose = input.market.klines[input.market.klines.length - 1]?.close ?? 0;
50
+ const latestRealtimePrice = input.market.realtimeQuote?.last_price ?? latestClose;
51
+ const watchlistItem = input.market.watchlistItem;
52
+ return [
53
+ `请对 ${input.market.companyName}(${input.market.symbol})生成收盘复盘。`,
54
+ `用户成本价: ${formatCostPrice(watchlistItem?.costPrice ?? null)}`,
55
+ `最新收盘价: ${latestClose.toFixed(2)}`,
56
+ `最新实时价: ${latestRealtimePrice.toFixed(2)}`,
57
+ `申万行业分类: ${watchlistItem?.sector ?? "未记录"}`,
58
+ `概念板块: ${watchlistItem?.themes.length ? watchlistItem.themes.join(";") : "未记录"}`,
59
+ "",
60
+ "## 昨日关键位验证(必须严格依据)",
61
+ input.validation.summary,
62
+ ...input.validation.lines.map((line) => `- ${line}`),
63
+ "",
64
+ "## 当前综合分析基线",
65
+ extractNarrative(input.compositeResult.analysisText),
66
+ "",
67
+ "## 大盘环境",
68
+ input.market.marketOverview.summary,
69
+ "",
70
+ "## 个股资讯摘要",
71
+ formatDocuments(input.news.documents),
72
+ "",
73
+ "## 行业分类/概念板块资讯摘要",
74
+ input.news.boardDocuments.length > 0 ? formatDocuments(input.news.boardDocuments) : "未获取到有效行业分类/概念板块资讯。",
75
+ "",
76
+ "## 结构化参考",
77
+ `当前综合关键位: ${formatLevels(input.compositeResult.levels ?? input.technicalResult.levels)}`,
78
+ `基本面评分/倾向: ${input.financialResult.score ?? "-"} / ${input.financialResult.bias}`,
79
+ `资讯面评分/倾向: ${input.newsResult.score ?? "-"} / ${input.newsResult.bias}`,
80
+ `基本面优势: ${joinList(input.financialResult.strengths)}`,
81
+ `基本面风险: ${joinList(input.financialResult.risks)}`,
82
+ `资讯催化: ${joinList(input.newsResult.catalysts)}`,
83
+ `资讯风险: ${joinList(input.newsResult.risks)}`,
84
+ "",
85
+ "请按系统要求输出正文和最终 JSON。正文重点回答:昨天关键位到底是否有效;今天盘面是否得到大盘、行业分类/概念板块、新闻的解释;明天该沿用、微调、重算还是暂停关键位。",
86
+ ].join("\n");
87
+ }
88
+ function formatDocuments(documents) {
89
+ if (documents.length === 0) {
90
+ return "未获取到有效资讯。";
91
+ }
92
+ return documents.slice(0, 3).map((document) => {
93
+ const meta = [document.source, document.publishedAt].filter(Boolean).join(" | ");
94
+ const excerpt = truncate(document.trunk, 120);
95
+ return [`标题: ${document.title}`, meta ? `来源: ${meta}` : null, excerpt ? `摘要: ${excerpt}` : null]
96
+ .filter(Boolean)
97
+ .join("\n");
98
+ }).join("\n\n");
99
+ }
100
+ function formatLevels(levels) {
101
+ if (!levels) {
102
+ return "暂无结构化关键位";
103
+ }
104
+ return [
105
+ `current=${formatMaybePrice(levels.current_price)}`,
106
+ `support=${formatMaybePrice(levels.support)}`,
107
+ `resistance=${formatMaybePrice(levels.resistance)}`,
108
+ `breakthrough=${formatMaybePrice(levels.breakthrough)}`,
109
+ `stop_loss=${formatMaybePrice(levels.stop_loss)}`,
110
+ `take_profit=${formatMaybePrice(levels.take_profit)}`,
111
+ `score=${levels.score}`,
112
+ ].join(", ");
113
+ }
114
+ function formatMaybePrice(value) {
115
+ return value == null ? "-" : value.toFixed(2);
116
+ }
117
+ function truncate(value, maxLength) {
118
+ const text = value.trim();
119
+ if (!text) {
120
+ return "";
121
+ }
122
+ return text.length <= maxLength ? text : `${text.slice(0, maxLength)}...`;
123
+ }
124
+ function extractNarrative(text) {
125
+ return text.replace(/```json\s*[\s\S]*?\s*```/gi, "").trim();
126
+ }
127
+ function joinList(items) {
128
+ return items.length > 0 ? items.join(";") : "无";
129
+ }
@@ -0,0 +1,7 @@
1
+ import type { MxSearchDocument } from "../../types/mx-search.js";
2
+ export declare const WATCHLIST_PROFILE_EXTRACTION_SYSTEM_PROMPT: string;
3
+ export declare function buildWatchlistProfileExtractionUserPrompt(input: {
4
+ symbol: string;
5
+ companyName: string;
6
+ documents: MxSearchDocument[];
7
+ }): string;
@@ -0,0 +1,52 @@
1
+ const MAX_PROMPT_DOCUMENTS = 8;
2
+ const MAX_TRUNK_LENGTH = 600;
3
+ export const WATCHLIST_PROFILE_EXTRACTION_SYSTEM_PROMPT = [
4
+ "你是A股证券资料结构化抽取助手。",
5
+ "",
6
+ "你的唯一任务:根据给定的妙想搜索结果,提取该股票的行业分类与概念板块,并严格输出 JSON。",
7
+ "",
8
+ "硬性要求:",
9
+ "1. 只能依据提供的资料,不得编造。",
10
+ "2. 只输出 JSON 对象或 ```json 代码块,不要输出解释文字。",
11
+ "3. JSON 结构固定为:",
12
+ "{",
13
+ ' "sector": string | null,',
14
+ ' "themes": string[],',
15
+ ' "confidence": "low" | "medium" | "high"',
16
+ "}",
17
+ "4. sector 优先提取申万行业/行业分类,保留完整层级;没有可靠信息时填 null。",
18
+ "5. themes 尽量完整列出概念板块,去重后输出数组;优先保留明确的概念/题材/板块名称。",
19
+ "6. themes 中不要输出泛词,例如公司新闻、最新公告、市场快讯;也不要输出等。",
20
+ "7. 若资料中是组合表达,拆成独立概念更优,例如华为昇腾 / 华为昇思应拆成两个数组项。",
21
+ "8. 若资料仅出现业务描述而没有足够证据支持概念标签,不要强行扩写。",
22
+ "9. confidence 仅反映你对提取结果的把握,不要附加解释。",
23
+ ].join("\n");
24
+ export function buildWatchlistProfileExtractionUserPrompt(input) {
25
+ const documents = input.documents.slice(0, MAX_PROMPT_DOCUMENTS);
26
+ return [
27
+ `股票名称: ${input.companyName}`,
28
+ `股票代码: ${input.symbol}`,
29
+ "",
30
+ "请根据下面的妙想搜索结果,提取该股票的行业分类与概念板块,并严格按要求输出 JSON。",
31
+ "",
32
+ "## 妙想搜索结果",
33
+ documents.length > 0 ? formatDocuments(documents) : "未获取到任何搜索结果。",
34
+ "",
35
+ "再次提醒:不要输出解释,只输出 JSON。",
36
+ ].join("\n");
37
+ }
38
+ function formatDocuments(documents) {
39
+ return documents.map((document, index) => formatDocument(document, index + 1)).join("\n\n");
40
+ }
41
+ function formatDocument(document, index) {
42
+ const trunk = document.trunk.trim();
43
+ const safeTrunk = trunk.length > MAX_TRUNK_LENGTH
44
+ ? `${trunk.slice(0, MAX_TRUNK_LENGTH)}...`
45
+ : trunk;
46
+ return [
47
+ `${index}. 标题: ${document.title}`,
48
+ `来源: ${document.source ?? "未知"}`,
49
+ `时间: ${document.publishedAt ?? "未知"}`,
50
+ `正文: ${safeTrunk || "无"}`,
51
+ ].join("\n");
52
+ }
@@ -0,0 +1 @@
1
+ export { ANALYSIS_COMMON_SYSTEM_PROMPT as ANALYSIS_SYSTEM_PROMPT } from "./analysis/index.js";
@@ -0,0 +1 @@
1
+ export { ANALYSIS_COMMON_SYSTEM_PROMPT as ANALYSIS_SYSTEM_PROMPT } from "./analysis/index.js";
@@ -0,0 +1 @@
1
+ export { buildKlineAnalysisUserPrompt as buildAnalysisUserPrompt } from "./analysis/index.js";
@@ -0,0 +1 @@
1
+ export { buildKlineAnalysisUserPrompt as buildAnalysisUserPrompt } from "./analysis/index.js";
@@ -0,0 +1,3 @@
1
+ import type { PluginConfig } from "../config/schema.js";
2
+ export declare function spawnDailyUpdateLoop(config: PluginConfig, configSource: "openclaw_plugin" | "local_config"): number | null;
3
+ export declare function isPidAlive(pid: number): boolean;
@@ -0,0 +1,24 @@
1
+ import { spawn } from "node:child_process";
2
+ import { fileURLToPath } from "node:url";
3
+ import { buildProcessConfigEnv } from "./process-config.js";
4
+ const PROJECT_ROOT = fileURLToPath(new URL("../../", import.meta.url));
5
+ const DAILY_UPDATE_LOOP_SCRIPT = fileURLToPath(new URL("../dev/run-daily-update-loop.js", import.meta.url));
6
+ export function spawnDailyUpdateLoop(config, configSource) {
7
+ const child = spawn(process.execPath, [DAILY_UPDATE_LOOP_SCRIPT], {
8
+ cwd: PROJECT_ROOT,
9
+ detached: true,
10
+ stdio: "ignore",
11
+ env: buildProcessConfigEnv(config, configSource),
12
+ });
13
+ child.unref();
14
+ return child.pid ?? null;
15
+ }
16
+ export function isPidAlive(pid) {
17
+ try {
18
+ process.kill(pid, 0);
19
+ return true;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
@@ -0,0 +1,3 @@
1
+ import type { PluginConfig } from "../config/schema.js";
2
+ export declare function spawnMonitorLoop(config: PluginConfig, configSource: "openclaw_plugin" | "local_config"): number | null;
3
+ export declare function isPidAlive(pid: number): boolean;
@@ -0,0 +1,24 @@
1
+ import { spawn } from "node:child_process";
2
+ import { fileURLToPath } from "node:url";
3
+ import { buildProcessConfigEnv } from "./process-config.js";
4
+ const PROJECT_ROOT = fileURLToPath(new URL("../../", import.meta.url));
5
+ const MONITOR_LOOP_SCRIPT = fileURLToPath(new URL("../dev/run-monitor-loop.js", import.meta.url));
6
+ export function spawnMonitorLoop(config, configSource) {
7
+ const child = spawn(process.execPath, [MONITOR_LOOP_SCRIPT], {
8
+ cwd: PROJECT_ROOT,
9
+ detached: true,
10
+ stdio: "ignore",
11
+ env: buildProcessConfigEnv(config, configSource),
12
+ });
13
+ child.unref();
14
+ return child.pid ?? null;
15
+ }
16
+ export function isPidAlive(pid) {
17
+ try {
18
+ process.kill(pid, 0);
19
+ return true;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
@@ -0,0 +1,22 @@
1
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
2
+ import type { AnyAgentTool, OpenClawConfig, OpenClawPluginApi, OpenClawPluginCommandDefinition, OpenClawPluginService, OpenClawPluginServiceContext, PluginCommandContext, PluginLogger } from "openclaw/plugin-sdk/plugin-entry";
3
+ import type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store";
4
+ export { definePluginEntry };
5
+ export interface ToolContext {
6
+ rawInput?: unknown;
7
+ }
8
+ export interface LocalTool {
9
+ name: string;
10
+ description: string;
11
+ optional?: boolean;
12
+ run: (context: ToolContext) => Promise<string> | string;
13
+ }
14
+ export type PluginApi = OpenClawPluginApi;
15
+ export type RegisteredAgentTool = AnyAgentTool;
16
+ export type RegisteredService = OpenClawPluginService;
17
+ export type ServiceContext = OpenClawPluginServiceContext;
18
+ export type RegisteredCommand = OpenClawPluginCommandDefinition;
19
+ export type CommandContext = PluginCommandContext;
20
+ export type OpenClawPluginConfig = OpenClawConfig;
21
+ export type OpenClawPluginRuntime = PluginRuntime;
22
+ export type OpenClawPluginLogger = PluginLogger;
@@ -0,0 +1,2 @@
1
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
2
+ export { definePluginEntry };
@@ -0,0 +1,8 @@
1
+ import type { PluginConfig } from "../config/schema.js";
2
+ export interface LoadedProcessConfig {
3
+ config: PluginConfig;
4
+ configSource: "openclaw_plugin" | "local_config";
5
+ }
6
+ export declare function buildProcessConfigEnv(config: PluginConfig, configSource: "openclaw_plugin" | "local_config"): NodeJS.ProcessEnv;
7
+ export declare function loadProcessConfig(): Promise<LoadedProcessConfig>;
8
+ export declare function loadLocalConfig(): Promise<PluginConfig>;
@@ -0,0 +1,35 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { normalizePluginConfig, resolvePluginConfigPaths } from "../config/normalize.js";
4
+ const CONFIG_B64_ENV = "TICKFLOW_ASSIST_CONFIG_B64";
5
+ const CONFIG_SOURCE_ENV = "TICKFLOW_ASSIST_CONFIG_SOURCE";
6
+ export function buildProcessConfigEnv(config, configSource) {
7
+ return {
8
+ ...process.env,
9
+ [CONFIG_B64_ENV]: Buffer.from(JSON.stringify(config), "utf-8").toString("base64"),
10
+ [CONFIG_SOURCE_ENV]: configSource,
11
+ };
12
+ }
13
+ export async function loadProcessConfig() {
14
+ const encoded = process.env[CONFIG_B64_ENV];
15
+ if (encoded) {
16
+ const decoded = Buffer.from(encoded, "base64").toString("utf-8");
17
+ return {
18
+ config: resolvePluginConfigPaths(normalizePluginConfig(JSON.parse(decoded)), process.cwd()),
19
+ configSource: normalizeConfigSource(process.env[CONFIG_SOURCE_ENV]),
20
+ };
21
+ }
22
+ return {
23
+ config: await loadLocalConfig(),
24
+ configSource: "local_config",
25
+ };
26
+ }
27
+ export async function loadLocalConfig() {
28
+ const configPath = path.resolve("local.config.json");
29
+ const raw = await readFile(configPath, "utf-8");
30
+ const parsed = JSON.parse(raw);
31
+ return resolvePluginConfigPaths(normalizePluginConfig(parsed.plugin ?? {}), process.cwd());
32
+ }
33
+ function normalizeConfigSource(value) {
34
+ return value === "openclaw_plugin" ? "openclaw_plugin" : "local_config";
35
+ }
@@ -0,0 +1,45 @@
1
+ import type { OpenClawPluginConfig, OpenClawPluginRuntime } from "../runtime/plugin-api.js";
2
+ interface AlertRuntimeContext {
3
+ config: OpenClawPluginConfig;
4
+ runtime: OpenClawPluginRuntime;
5
+ }
6
+ interface AlertServiceOptions {
7
+ openclawCliBin: string;
8
+ channel: string;
9
+ account: string;
10
+ target: string;
11
+ runtime?: AlertRuntimeContext;
12
+ }
13
+ export declare class AlertService {
14
+ private readonly options;
15
+ private lastError;
16
+ constructor(options: AlertServiceOptions);
17
+ send(message: string): Promise<boolean>;
18
+ getLastError(): string | null;
19
+ getCliBinary(): string;
20
+ formatSystemNotification(title: string, lines: string[]): string;
21
+ formatPriceAlert(params: {
22
+ symbol: string;
23
+ name: string;
24
+ currentPrice: number;
25
+ ruleName: string;
26
+ ruleDescription: string;
27
+ levelPrice: number;
28
+ costPrice: number | null;
29
+ }): string;
30
+ formatVolumeAlert(params: {
31
+ symbol: string;
32
+ name: string;
33
+ currentPrice: number;
34
+ currentVolume: number;
35
+ avgVolume: number;
36
+ ratio: number;
37
+ }): string;
38
+ private get channel();
39
+ private combineErrors;
40
+ private trySendViaRuntime;
41
+ private trySendViaRuntimeCommand;
42
+ private trySendViaSpawn;
43
+ private buildCliArgs;
44
+ }
45
+ export {};
@@ -0,0 +1,198 @@
1
+ import { spawn } from "node:child_process";
2
+ import { calculateProfitPct, formatCostPrice } from "../utils/cost-price.js";
3
+ export class AlertService {
4
+ options;
5
+ lastError = null;
6
+ constructor(options) {
7
+ this.options = options;
8
+ }
9
+ async send(message) {
10
+ this.lastError = null;
11
+ const runtimeError = await this.trySendViaRuntime(message);
12
+ if (runtimeError === null) {
13
+ return true;
14
+ }
15
+ const fallbackError = this.options.runtime
16
+ ? await this.trySendViaRuntimeCommand(message)
17
+ : await this.trySendViaSpawn(message);
18
+ if (fallbackError === null) {
19
+ return true;
20
+ }
21
+ this.lastError = this.combineErrors(runtimeError, fallbackError);
22
+ return false;
23
+ }
24
+ getLastError() {
25
+ return this.lastError;
26
+ }
27
+ getCliBinary() {
28
+ return this.options.openclawCliBin;
29
+ }
30
+ formatSystemNotification(title, lines) {
31
+ return [title, lines.join("\n")].filter(Boolean).join("\n\n").trim();
32
+ }
33
+ formatPriceAlert(params) {
34
+ const profitPct = calculateProfitPct(params.currentPrice, params.costPrice);
35
+ return [
36
+ `🚨 ${params.ruleName}告警`,
37
+ "",
38
+ `📌 ${params.name}(${params.symbol})`,
39
+ `💹 当前价: ${params.currentPrice.toFixed(2)}`,
40
+ `📊 触发价位: ${params.levelPrice.toFixed(2)}`,
41
+ `📝 ${params.ruleDescription}`,
42
+ ...(profitPct == null
43
+ ? []
44
+ : [
45
+ `💰 持仓盈亏: ${profitPct >= 0 ? "+" : ""}${profitPct.toFixed(2)}%(成本 ${formatCostPrice(params.costPrice)})`,
46
+ ]),
47
+ "",
48
+ "⏰ 请及时关注!",
49
+ ].join("\n");
50
+ }
51
+ formatVolumeAlert(params) {
52
+ return [
53
+ "📊 成交量异动",
54
+ "",
55
+ `📌 ${params.name}(${params.symbol})`,
56
+ `💹 当前价: ${params.currentPrice.toFixed(2)}`,
57
+ `📈 当前成交量: ${params.currentVolume.toLocaleString("en-US")}`,
58
+ `📉 近5日均量: ${params.avgVolume.toFixed(0)}`,
59
+ `⚡ 量比: ${params.ratio.toFixed(1)}倍`,
60
+ "",
61
+ "⚠️ 成交量显著放大,请关注盘面变化!",
62
+ ].join("\n");
63
+ }
64
+ get channel() {
65
+ return this.options.channel.trim().toLowerCase();
66
+ }
67
+ combineErrors(runtimeError, fallbackError) {
68
+ if (!runtimeError || runtimeError === "runtime delivery unavailable") {
69
+ return fallbackError;
70
+ }
71
+ return `${runtimeError}; ${fallbackError}`;
72
+ }
73
+ async trySendViaRuntime(message) {
74
+ const runtimeContext = this.options.runtime;
75
+ if (!runtimeContext || !this.options.target.trim()) {
76
+ return "runtime delivery unavailable";
77
+ }
78
+ const baseOptions = {
79
+ accountId: this.options.account || undefined,
80
+ cfg: runtimeContext.config,
81
+ };
82
+ try {
83
+ switch (this.channel) {
84
+ case "telegram":
85
+ await runtimeContext.runtime.channel.telegram.sendMessageTelegram(this.options.target, message, baseOptions);
86
+ return null;
87
+ case "discord":
88
+ await runtimeContext.runtime.channel.discord.sendMessageDiscord(this.options.target, message, baseOptions);
89
+ return null;
90
+ case "slack":
91
+ await runtimeContext.runtime.channel.slack.sendMessageSlack(this.options.target, message, baseOptions);
92
+ return null;
93
+ case "signal":
94
+ await runtimeContext.runtime.channel.signal.sendMessageSignal(this.options.target, message, baseOptions);
95
+ return null;
96
+ case "imessage":
97
+ await runtimeContext.runtime.channel.imessage.sendMessageIMessage(this.options.target, message, {
98
+ accountId: this.options.account || undefined,
99
+ config: runtimeContext.config,
100
+ });
101
+ return null;
102
+ case "whatsapp":
103
+ await runtimeContext.runtime.channel.whatsapp.sendMessageWhatsApp(this.options.target, message, {
104
+ verbose: false,
105
+ cfg: runtimeContext.config,
106
+ accountId: this.options.account || undefined,
107
+ });
108
+ return null;
109
+ default:
110
+ return `runtime delivery not supported for channel: ${this.channel}`;
111
+ }
112
+ }
113
+ catch (error) {
114
+ return `runtime delivery failed: ${formatErrorMessage(error)}`;
115
+ }
116
+ }
117
+ async trySendViaRuntimeCommand(message) {
118
+ const runtimeContext = this.options.runtime;
119
+ if (!runtimeContext) {
120
+ return "runtime command unavailable";
121
+ }
122
+ try {
123
+ const result = await runtimeContext.runtime.system.runCommandWithTimeout(this.buildCliArgs(message), { timeoutMs: 15_000 });
124
+ if (result.code === 0) {
125
+ return null;
126
+ }
127
+ return (result.stderr.trim()
128
+ || result.stdout.trim()
129
+ || `command exited with ${result.code ?? "unknown"}`);
130
+ }
131
+ catch (error) {
132
+ return `runtime command failed: ${formatErrorMessage(error)}`;
133
+ }
134
+ }
135
+ async trySendViaSpawn(message) {
136
+ const argv = this.buildCliArgs(message);
137
+ const [command, ...args] = argv;
138
+ return new Promise((resolve) => {
139
+ const child = spawn(command, args, {
140
+ stdio: ["ignore", "pipe", "pipe"],
141
+ });
142
+ let stdout = "";
143
+ let stderr = "";
144
+ let settled = false;
145
+ const finish = (value) => {
146
+ if (settled) {
147
+ return;
148
+ }
149
+ settled = true;
150
+ resolve(value);
151
+ };
152
+ child.stdout.on("data", (chunk) => {
153
+ stdout += chunk.toString();
154
+ });
155
+ child.stderr.on("data", (chunk) => {
156
+ stderr += chunk.toString();
157
+ });
158
+ child.on("error", (error) => {
159
+ finish(`spawn failed: ${error.message}`);
160
+ });
161
+ child.on("close", (code) => {
162
+ finish(code === 0 ? null : (stderr || stdout || `exit code ${code}`).trim());
163
+ });
164
+ });
165
+ }
166
+ buildCliArgs(message) {
167
+ const args = [
168
+ this.options.openclawCliBin,
169
+ "message",
170
+ "send",
171
+ "--channel",
172
+ this.channel,
173
+ "--message",
174
+ message,
175
+ ];
176
+ if (this.options.target) {
177
+ args.push("--target", this.options.target);
178
+ }
179
+ if (this.options.account) {
180
+ args.push("--account", this.options.account);
181
+ }
182
+ return args;
183
+ }
184
+ }
185
+ function formatErrorMessage(error) {
186
+ if (error instanceof Error) {
187
+ return error.message;
188
+ }
189
+ if (typeof error === "string") {
190
+ return error;
191
+ }
192
+ try {
193
+ return JSON.stringify(error);
194
+ }
195
+ catch {
196
+ return String(error);
197
+ }
198
+ }
@@ -0,0 +1,20 @@
1
+ import { AnalysisTask } from "../analysis/tasks/analysis-task.js";
2
+ import { AnalysisLogRepository } from "../storage/repositories/analysis-log-repo.js";
3
+ import type { AnalysisLogEntry } from "../types/domain.js";
4
+ export interface GenerateTextOptions {
5
+ maxTokens?: number;
6
+ temperature?: number;
7
+ }
8
+ export declare class AnalysisService {
9
+ private readonly llmBaseUrl;
10
+ private readonly llmApiKey;
11
+ private readonly llmModel;
12
+ private readonly analysisLogRepository;
13
+ constructor(llmBaseUrl: string, llmApiKey: string, llmModel: string, analysisLogRepository: AnalysisLogRepository);
14
+ isConfigured(): boolean;
15
+ getConfigurationError(): string | null;
16
+ runTask<TInput, TResult>(task: AnalysisTask<TInput, TResult>, input: TInput): Promise<TResult>;
17
+ generateText(systemPrompt: string, userPrompt: string, options?: GenerateTextOptions): Promise<string>;
18
+ getLatestAnalysis(symbol: string): Promise<AnalysisLogEntry | null>;
19
+ private callLlm;
20
+ }