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,286 @@
1
+ export class MxSearchServiceError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "MxSearchServiceError";
5
+ }
6
+ }
7
+ export class MxApiService {
8
+ apiBaseUrl;
9
+ apiKey;
10
+ constructor(apiBaseUrl, apiKey) {
11
+ this.apiBaseUrl = apiBaseUrl;
12
+ this.apiKey = apiKey;
13
+ }
14
+ isConfigured() {
15
+ return Boolean(this.apiBaseUrl.trim() && this.apiKey.trim());
16
+ }
17
+ getConfigurationError() {
18
+ if (!this.apiBaseUrl.trim()) {
19
+ return "mx_search 未配置接口地址,请设置 mxSearchApiUrl 或环境变量 MX_SEARCH_API_URL";
20
+ }
21
+ if (!this.apiKey.trim()) {
22
+ return "mx_search 未配置 API Key,请设置插件配置 mxSearchApiKey 或环境变量 MX_APIKEY";
23
+ }
24
+ return null;
25
+ }
26
+ async search(query) {
27
+ const json = await this.postJson("news-search", { query }, "mx_search");
28
+ const apiError = extractApiError(json);
29
+ if (apiError) {
30
+ throw new MxSearchServiceError(`mx_search 返回错误: ${apiError.code ?? "UNKNOWN"} ${apiError.message ?? ""}`.trim());
31
+ }
32
+ return normalizeMxSearchDocuments(json);
33
+ }
34
+ async selectStocks(input) {
35
+ const json = await this.postJson("stock-screen", {
36
+ keyword: input.keyword,
37
+ pageNo: input.pageNo ?? 1,
38
+ pageSize: input.pageSize ?? 20,
39
+ }, "mx_select_stock");
40
+ return normalizeMxSelectStockResult(json);
41
+ }
42
+ async postJson(endpoint, body, toolName) {
43
+ const configError = this.getConfigurationError();
44
+ if (configError) {
45
+ throw new MxSearchServiceError(configError);
46
+ }
47
+ const response = await fetch(buildMxEndpointUrl(this.apiBaseUrl, endpoint), {
48
+ method: "POST",
49
+ headers: {
50
+ "Content-Type": "application/json",
51
+ apikey: this.apiKey,
52
+ },
53
+ body: JSON.stringify(body),
54
+ });
55
+ if (!response.ok) {
56
+ const text = await response.text();
57
+ throw new MxSearchServiceError(`${toolName} 请求失败: ${response.status} ${response.statusText} ${text}`.trim());
58
+ }
59
+ return response.json();
60
+ }
61
+ }
62
+ export class MxSearchService extends MxApiService {
63
+ }
64
+ function buildMxEndpointUrl(baseUrl, endpoint) {
65
+ const trimmedBase = baseUrl.trim().replace(/\/+$/, "");
66
+ const normalizedEndpoint = endpoint.replace(/^\/+/, "");
67
+ const normalizedBase = trimmedBase.replace(/\/(news-search|stock-screen)$/i, "");
68
+ if (trimmedBase.endsWith(`/${normalizedEndpoint}`)) {
69
+ return trimmedBase;
70
+ }
71
+ return `${normalizedBase}/${normalizedEndpoint}`;
72
+ }
73
+ function extractApiError(value) {
74
+ if (typeof value !== "object" || value === null) {
75
+ return null;
76
+ }
77
+ const record = value;
78
+ const code = typeof record.code === "string" ? record.code : undefined;
79
+ const message = typeof record.message === "string" ? record.message : undefined;
80
+ const numericStatus = toNullableNumber(record.status);
81
+ const numericCode = toNullableNumber(record.code);
82
+ if ((numericStatus != null && numericStatus === 0) || (numericCode != null && numericCode === 0)) {
83
+ return null;
84
+ }
85
+ if (message && ["ok", "success", "成功"].includes(message.trim().toLowerCase())) {
86
+ return null;
87
+ }
88
+ if (!code && !message) {
89
+ return null;
90
+ }
91
+ if ("title" in record || "trunk" in record || Array.isArray(record.list) || Array.isArray(record.data)) {
92
+ return null;
93
+ }
94
+ return { code, message };
95
+ }
96
+ export function normalizeMxSearchDocuments(value) {
97
+ const root = asRecord(value);
98
+ const nestedData = asRecord(asRecord(root.data).data);
99
+ const llmSearchResponse = asRecord(nestedData.llmSearchResponse);
100
+ const sourceValue = Array.isArray(llmSearchResponse.data) ? llmSearchResponse.data : value;
101
+ const candidates = collectCandidateItems(sourceValue);
102
+ const documents = [];
103
+ const seen = new Set();
104
+ for (const item of candidates) {
105
+ const normalized = normalizeDocument(item);
106
+ if (!normalized) {
107
+ continue;
108
+ }
109
+ const fingerprint = `${normalized.title}\n${normalized.trunk}`;
110
+ if (seen.has(fingerprint)) {
111
+ continue;
112
+ }
113
+ seen.add(fingerprint);
114
+ documents.push(normalized);
115
+ }
116
+ return documents;
117
+ }
118
+ function collectCandidateItems(value, depth = 0) {
119
+ if (depth > 4) {
120
+ return [];
121
+ }
122
+ if (Array.isArray(value)) {
123
+ return value.flatMap((item) => collectCandidateItems(item, depth + 1));
124
+ }
125
+ if (typeof value !== "object" || value === null) {
126
+ return [];
127
+ }
128
+ const record = value;
129
+ if (looksLikeDocument(record)) {
130
+ return [record];
131
+ }
132
+ const candidates = [];
133
+ for (const key of ["data", "list", "result", "results", "items", "records"]) {
134
+ if (key in record) {
135
+ candidates.push(...collectCandidateItems(record[key], depth + 1));
136
+ }
137
+ }
138
+ if (candidates.length > 0) {
139
+ return candidates;
140
+ }
141
+ return Object.values(record).flatMap((item) => collectCandidateItems(item, depth + 1));
142
+ }
143
+ function looksLikeDocument(value) {
144
+ return (typeof value.title === "string"
145
+ || typeof value.name === "string"
146
+ || typeof value.headline === "string"
147
+ || typeof value.trunk === "string"
148
+ || typeof value.content === "string"
149
+ || typeof value.summary === "string"
150
+ || typeof value.abstract === "string"
151
+ || typeof value.text === "string"
152
+ || Array.isArray(value.secuList));
153
+ }
154
+ function normalizeDocument(value) {
155
+ const title = pickFirstString(value, ["title", "name", "headline"]);
156
+ const trunk = pickFirstString(value, ["trunk", "content", "summary", "abstract", "text"]);
157
+ if (!title && !trunk) {
158
+ return null;
159
+ }
160
+ return {
161
+ title: title || "未命名资讯",
162
+ trunk: trunk || "",
163
+ secuList: normalizeSecurities(value.secuList),
164
+ source: pickFirstString(value, ["source", "media", "sourceName", "mediaName"]) ?? null,
165
+ publishedAt: pickFirstString(value, ["publishTime", "publishedAt", "showTime", "date", "ctime"]) ?? null,
166
+ raw: value,
167
+ };
168
+ }
169
+ function normalizeSecurities(value) {
170
+ if (!Array.isArray(value)) {
171
+ return [];
172
+ }
173
+ return value
174
+ .filter((item) => typeof item === "object" && item !== null)
175
+ .map((item) => ({
176
+ ...item,
177
+ secuCode: toNullableString(item.secuCode),
178
+ secuName: toNullableString(item.secuName),
179
+ secuType: toNullableString(item.secuType),
180
+ }));
181
+ }
182
+ function pickFirstString(value, keys) {
183
+ for (const key of keys) {
184
+ const candidate = value[key];
185
+ if (typeof candidate === "string" && candidate.trim()) {
186
+ return candidate.trim();
187
+ }
188
+ }
189
+ return undefined;
190
+ }
191
+ function toNullableString(value) {
192
+ if (typeof value !== "string") {
193
+ return null;
194
+ }
195
+ const text = value.trim();
196
+ return text || null;
197
+ }
198
+ function normalizeMxSelectStockResult(value) {
199
+ const root = asRecord(value);
200
+ const data = asRecord(root.data);
201
+ const nestedData = asRecord(data.data);
202
+ const allResults = asRecord(nestedData.allResults);
203
+ const result = asRecord(allResults.result);
204
+ const businessCode = toNullableString(allResults.code) ?? toNullableString(nestedData.responseCode) ?? toNullableString(data.code);
205
+ const rawTotalCondition = normalizeCondition(allResults.totalCondition);
206
+ const flatTotalCondition = toNullableString(nestedData.totalCondition);
207
+ return {
208
+ status: toNullableNumber(root.status),
209
+ message: toNullableString(root.message),
210
+ code: businessCode,
211
+ msg: toNullableString(data.message) ?? toNullableString(data.msg),
212
+ resultType: toNullableNumber(result.resultType) ?? toNullableNumber(nestedData.resultType),
213
+ total: toSafeNumber(result.total),
214
+ totalRecordCount: toSafeNumber(result.totalRecordCount),
215
+ parserText: toNullableString(nestedData.parserText),
216
+ columns: normalizeColumns(result.columns),
217
+ dataList: normalizeDataList(result.dataList),
218
+ responseConditionList: normalizeConditions(allResults.responseConditionList ?? nestedData.responseConditionList),
219
+ totalCondition: rawTotalCondition ??
220
+ (flatTotalCondition
221
+ ? {
222
+ describe: flatTotalCondition,
223
+ stockCount: toNullableNumber(nestedData.securityCount),
224
+ }
225
+ : null),
226
+ };
227
+ }
228
+ function normalizeColumns(value) {
229
+ if (!Array.isArray(value)) {
230
+ return [];
231
+ }
232
+ return value
233
+ .filter((item) => typeof item === "object" && item !== null)
234
+ .map((item) => ({
235
+ title: String(item.title ?? item.key ?? ""),
236
+ key: String(item.key ?? ""),
237
+ dateMsg: toNullableString(item.dateMsg),
238
+ sortable: Boolean(item.sortable),
239
+ sortWay: toNullableString(item.sortWay),
240
+ redGreenAble: Boolean(item.redGreenAble),
241
+ unit: toNullableString(item.unit),
242
+ dataType: toNullableString(item.dataType),
243
+ }))
244
+ .filter((item) => item.key);
245
+ }
246
+ function normalizeDataList(value) {
247
+ if (!Array.isArray(value)) {
248
+ return [];
249
+ }
250
+ return value.filter((item) => typeof item === "object" && item !== null);
251
+ }
252
+ function normalizeConditions(value) {
253
+ if (!Array.isArray(value)) {
254
+ return [];
255
+ }
256
+ return value
257
+ .map((item) => normalizeCondition(item))
258
+ .filter((item) => item != null);
259
+ }
260
+ function normalizeCondition(value) {
261
+ if (typeof value !== "object" || value === null) {
262
+ return null;
263
+ }
264
+ const record = value;
265
+ const describe = toNullableString(record.describe);
266
+ if (!describe) {
267
+ return null;
268
+ }
269
+ return {
270
+ describe,
271
+ stockCount: toNullableNumber(record.stockCount),
272
+ };
273
+ }
274
+ function asRecord(value) {
275
+ return typeof value === "object" && value !== null ? value : {};
276
+ }
277
+ function toNullableNumber(value) {
278
+ if (value == null || value === "") {
279
+ return null;
280
+ }
281
+ const num = Number(value);
282
+ return Number.isFinite(num) ? num : null;
283
+ }
284
+ function toSafeNumber(value) {
285
+ return toNullableNumber(value) ?? 0;
286
+ }
@@ -0,0 +1,31 @@
1
+ import { CompositeAnalysisOrchestrator } from "../analysis/orchestrators/composite-analysis.orchestrator.js";
2
+ import { PostCloseReviewTask } from "../analysis/tasks/post-close-review.task.js";
3
+ import { WatchlistService } from "./watchlist-service.js";
4
+ import { AnalysisService } from "./analysis-service.js";
5
+ import { KeyLevelsRepository } from "../storage/repositories/key-levels-repo.js";
6
+ import { KeyLevelsHistoryRepository } from "../storage/repositories/key-levels-history-repo.js";
7
+ import { KlinesRepository } from "../storage/repositories/klines-repo.js";
8
+ import { IntradayKlinesRepository } from "../storage/repositories/intraday-klines-repo.js";
9
+ export interface PostCloseReviewRunResult {
10
+ overviewMessage: string;
11
+ detailMessages: string[];
12
+ combinedText: string;
13
+ }
14
+ export declare class PostCloseReviewService {
15
+ private readonly watchlistService;
16
+ private readonly compositeAnalysisOrchestrator;
17
+ private readonly analysisService;
18
+ private readonly postCloseReviewTask;
19
+ private readonly keyLevelsRepository;
20
+ private readonly keyLevelsHistoryRepository;
21
+ private readonly klinesRepository;
22
+ private readonly intradayKlinesRepository;
23
+ constructor(watchlistService: WatchlistService, compositeAnalysisOrchestrator: CompositeAnalysisOrchestrator, analysisService: AnalysisService, postCloseReviewTask: PostCloseReviewTask, keyLevelsRepository: KeyLevelsRepository, keyLevelsHistoryRepository: KeyLevelsHistoryRepository, klinesRepository: KlinesRepository, intradayKlinesRepository: IntradayKlinesRepository);
24
+ run(): Promise<PostCloseReviewRunResult>;
25
+ private persistReview;
26
+ private persistFallbackCompositeReview;
27
+ private buildValidationContext;
28
+ private formatOverviewMessage;
29
+ private formatDetailMessage;
30
+ private formatFailureMessage;
31
+ }