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,35 @@
1
+ import { AnalysisService } from "../../services/analysis-service.js";
2
+ import { CompositeAnalysisRepository } from "../../storage/repositories/composite-analysis-repo.js";
3
+ import { FinancialAnalysisRepository } from "../../storage/repositories/financial-analysis-repo.js";
4
+ import { NewsAnalysisRepository } from "../../storage/repositories/news-analysis-repo.js";
5
+ import { TechnicalAnalysisRepository } from "../../storage/repositories/technical-analysis-repo.js";
6
+ import { FinancialAnalysisProvider } from "../providers/financial-analysis.provider.js";
7
+ import { MarketAnalysisProvider } from "../providers/market-analysis.provider.js";
8
+ import { NewsAnalysisProvider } from "../providers/news-analysis.provider.js";
9
+ import { CompositeStockAnalysisTask } from "../tasks/composite-stock-analysis.task.js";
10
+ import { FinancialFundamentalTask } from "../tasks/financial-fundamental.task.js";
11
+ import { FinancialFundamentalLiteTask } from "../tasks/financial-fundamental-lite.task.js";
12
+ import { KlineTechnicalSignalTask } from "../tasks/kline-technical-signal.task.js";
13
+ import { NewsCatalystTask } from "../tasks/news-catalyst.task.js";
14
+ import type { CompositeAnalysisInput, CompositeAnalysisResult } from "../types/composite-analysis.js";
15
+ export declare class CompositeAnalysisOrchestrator {
16
+ private readonly analysisService;
17
+ private readonly marketProvider;
18
+ private readonly financialProvider;
19
+ private readonly newsProvider;
20
+ private readonly technicalTask;
21
+ private readonly financialTask;
22
+ private readonly financialLiteTask;
23
+ private readonly newsTask;
24
+ private readonly compositeTask;
25
+ private readonly technicalAnalysisRepository;
26
+ private readonly financialAnalysisRepository;
27
+ private readonly newsAnalysisRepository;
28
+ private readonly compositeAnalysisRepository;
29
+ constructor(analysisService: AnalysisService, marketProvider: MarketAnalysisProvider, financialProvider: FinancialAnalysisProvider, newsProvider: NewsAnalysisProvider, technicalTask: KlineTechnicalSignalTask, financialTask: FinancialFundamentalTask, financialLiteTask: FinancialFundamentalLiteTask, newsTask: NewsCatalystTask, compositeTask: CompositeStockAnalysisTask, technicalAnalysisRepository: TechnicalAnalysisRepository, financialAnalysisRepository: FinancialAnalysisRepository, newsAnalysisRepository: NewsAnalysisRepository, compositeAnalysisRepository: CompositeAnalysisRepository);
30
+ buildInput(symbol: string): Promise<CompositeAnalysisInput>;
31
+ analyze(symbol: string): Promise<CompositeAnalysisResult>;
32
+ analyzeInput(compositeInput: CompositeAnalysisInput): Promise<CompositeAnalysisResult>;
33
+ formatForUser(result: CompositeAnalysisResult): string;
34
+ private runStep;
35
+ }
@@ -0,0 +1,232 @@
1
+ import { formatChinaDateTime } from "../../utils/china-time.js";
2
+ import { buildFinancialFallbackResult, } from "../tasks/financial-fundamental.task.js";
3
+ import { buildNewsFallbackResult } from "../tasks/news-catalyst.task.js";
4
+ export class CompositeAnalysisOrchestrator {
5
+ analysisService;
6
+ marketProvider;
7
+ financialProvider;
8
+ newsProvider;
9
+ technicalTask;
10
+ financialTask;
11
+ financialLiteTask;
12
+ newsTask;
13
+ compositeTask;
14
+ technicalAnalysisRepository;
15
+ financialAnalysisRepository;
16
+ newsAnalysisRepository;
17
+ compositeAnalysisRepository;
18
+ constructor(analysisService, marketProvider, financialProvider, newsProvider, technicalTask, financialTask, financialLiteTask, newsTask, compositeTask, technicalAnalysisRepository, financialAnalysisRepository, newsAnalysisRepository, compositeAnalysisRepository) {
19
+ this.analysisService = analysisService;
20
+ this.marketProvider = marketProvider;
21
+ this.financialProvider = financialProvider;
22
+ this.newsProvider = newsProvider;
23
+ this.technicalTask = technicalTask;
24
+ this.financialTask = financialTask;
25
+ this.financialLiteTask = financialLiteTask;
26
+ this.newsTask = newsTask;
27
+ this.compositeTask = compositeTask;
28
+ this.technicalAnalysisRepository = technicalAnalysisRepository;
29
+ this.financialAnalysisRepository = financialAnalysisRepository;
30
+ this.newsAnalysisRepository = newsAnalysisRepository;
31
+ this.compositeAnalysisRepository = compositeAnalysisRepository;
32
+ }
33
+ async buildInput(symbol) {
34
+ const market = await this.marketProvider.load(symbol);
35
+ const [financial, news] = await Promise.all([
36
+ this.financialProvider.load(symbol, market.companyName),
37
+ this.newsProvider.load(symbol, market.companyName, market.watchlistItem),
38
+ ]);
39
+ const technicalPromise = this.runStep(this.technicalTask, market);
40
+ const financialPromise = financial.available
41
+ ? financial.mode === "lite"
42
+ ? this.runStep(this.financialLiteTask, financial)
43
+ : this.runStep(this.financialTask, financial)
44
+ : Promise.resolve(buildFinancialFallbackResult());
45
+ const newsPromise = news.available
46
+ ? this.runStep(this.newsTask, news)
47
+ : Promise.resolve(buildNewsFallbackResult());
48
+ const [technicalResult, financialResult, newsResult] = await Promise.all([
49
+ technicalPromise,
50
+ financialPromise,
51
+ newsPromise,
52
+ ]);
53
+ return {
54
+ market,
55
+ financial,
56
+ news,
57
+ technicalResult,
58
+ financialResult,
59
+ newsResult,
60
+ };
61
+ }
62
+ async analyze(symbol) {
63
+ const input = await this.buildInput(symbol);
64
+ return this.analyzeInput(input);
65
+ }
66
+ async analyzeInput(compositeInput) {
67
+ const analysisDate = formatChinaDateTime().slice(0, 10);
68
+ await Promise.all([
69
+ this.technicalAnalysisRepository.append(buildTechnicalAnalysisEntry(compositeInput.market.symbol, analysisDate, compositeInput.technicalResult)),
70
+ this.financialAnalysisRepository.append(buildFinancialAnalysisEntry(analysisDate, compositeInput.financial, compositeInput.financialResult)),
71
+ this.newsAnalysisRepository.append(buildNewsAnalysisEntry(analysisDate, compositeInput.news, compositeInput.newsResult)),
72
+ ]);
73
+ const result = await this.analysisService.runTask(this.compositeTask, compositeInput);
74
+ await this.compositeAnalysisRepository.append(buildCompositeAnalysisEntry(analysisDate, compositeInput, result));
75
+ return result;
76
+ }
77
+ formatForUser(result) {
78
+ return this.compositeTask.formatForUser(result);
79
+ }
80
+ async runStep(task, input) {
81
+ const prepared = await task.prepare(input);
82
+ const analysisText = await this.analysisService.generateText(prepared.systemPrompt, prepared.userPrompt);
83
+ return task.parseResult(analysisText, input);
84
+ }
85
+ }
86
+ function buildTechnicalAnalysisEntry(symbol, analysisDate, result) {
87
+ return {
88
+ symbol,
89
+ analysis_date: analysisDate,
90
+ analysis_text: result.analysisText,
91
+ structured_ok: result.levels != null,
92
+ ...toLevelsSnapshot(result.levels),
93
+ };
94
+ }
95
+ function buildFinancialAnalysisEntry(analysisDate, context, result) {
96
+ const evidence = buildFinancialEvidence(context);
97
+ return {
98
+ symbol: context.symbol,
99
+ analysis_date: analysisDate,
100
+ analysis_text: result.analysisText,
101
+ score: normalizeScore(result.score),
102
+ bias: result.bias,
103
+ strengths: result.strengths,
104
+ risks: result.risks,
105
+ watch_items: result.watchItems,
106
+ evidence,
107
+ };
108
+ }
109
+ function buildNewsAnalysisEntry(analysisDate, context, result) {
110
+ return {
111
+ symbol: context.symbol,
112
+ analysis_date: analysisDate,
113
+ query: context.query,
114
+ analysis_text: result.analysisText,
115
+ score: normalizeScore(result.score),
116
+ bias: result.bias,
117
+ catalysts: result.catalysts,
118
+ risks: result.risks,
119
+ watch_items: result.watchItems,
120
+ source_count: context.documents.length,
121
+ evidence: {
122
+ available: context.available,
123
+ source_count: context.documents.length,
124
+ documents: context.documents.slice(0, 5).map((document) => ({
125
+ title: document.title,
126
+ source: document.source,
127
+ published_at: document.publishedAt,
128
+ securities: document.secuList
129
+ .map((security) => [security.secuCode, security.secuName].filter(Boolean).join(":"))
130
+ .filter(Boolean),
131
+ })),
132
+ },
133
+ };
134
+ }
135
+ function buildCompositeAnalysisEntry(analysisDate, input, result) {
136
+ return {
137
+ symbol: input.market.symbol,
138
+ analysis_date: analysisDate,
139
+ analysis_text: result.analysisText,
140
+ structured_ok: result.levels != null,
141
+ ...toLevelsSnapshot(result.levels),
142
+ technical_score: normalizeScore(input.technicalResult.levels?.score ?? null),
143
+ financial_score: normalizeScore(input.financialResult.score),
144
+ news_score: normalizeScore(input.newsResult.score),
145
+ financial_bias: input.financialResult.bias,
146
+ news_bias: input.newsResult.bias,
147
+ evidence: {
148
+ technical_structured: input.technicalResult.levels != null,
149
+ financial_available: input.financial.available,
150
+ financial_mode: input.financial.mode,
151
+ financial_source: input.financial.source,
152
+ financial_latest_period_end: getLatestFinancialPeriodEnd(input.financial.snapshot),
153
+ financial_lite_as_of: input.financial.liteSnapshot?.asOf ?? null,
154
+ news_available: input.news.available,
155
+ news_query: input.news.query,
156
+ news_source_count: input.news.documents.length,
157
+ },
158
+ };
159
+ }
160
+ function buildFinancialEvidence(context) {
161
+ const latest = getLatestFinancialMeta(context.snapshot);
162
+ return {
163
+ available: context.available,
164
+ mode: context.mode,
165
+ source: context.source,
166
+ note: context.note,
167
+ latest_period_end: latest.periodEnd,
168
+ latest_announce_date: latest.announceDate,
169
+ income_count: context.snapshot?.income.length ?? 0,
170
+ metrics_count: context.snapshot?.metrics.length ?? 0,
171
+ cash_flow_count: context.snapshot?.cashFlow.length ?? 0,
172
+ balance_sheet_count: context.snapshot?.balanceSheet.length ?? 0,
173
+ lite_as_of: context.liteSnapshot?.asOf ?? null,
174
+ lite_query: context.liteSnapshot?.query ?? null,
175
+ lite_metric_count: context.liteSnapshot?.metrics.length ?? 0,
176
+ lite_metric_labels: buildLiteMetricLabels(context.liteSnapshot),
177
+ };
178
+ }
179
+ function getLatestFinancialMeta(snapshot) {
180
+ if (!snapshot) {
181
+ return {
182
+ periodEnd: null,
183
+ announceDate: null,
184
+ };
185
+ }
186
+ const records = [
187
+ ...snapshot.metrics,
188
+ ...snapshot.income,
189
+ ...snapshot.cashFlow,
190
+ ...snapshot.balanceSheet,
191
+ ];
192
+ const latest = records
193
+ .filter((record) => Boolean(record.period_end))
194
+ .sort((left, right) => right.period_end.localeCompare(left.period_end))[0];
195
+ return {
196
+ periodEnd: latest?.period_end ?? null,
197
+ announceDate: latest?.announce_date ?? null,
198
+ };
199
+ }
200
+ function getLatestFinancialPeriodEnd(snapshot) {
201
+ return getLatestFinancialMeta(snapshot).periodEnd;
202
+ }
203
+ function buildLiteMetricLabels(snapshot) {
204
+ if (!snapshot) {
205
+ return [];
206
+ }
207
+ return snapshot.metrics.map((metric) => metric.label).slice(0, 8);
208
+ }
209
+ function toLevelsSnapshot(levels) {
210
+ return {
211
+ current_price: toNullableNumber(levels?.current_price),
212
+ stop_loss: toNullableNumber(levels?.stop_loss),
213
+ breakthrough: toNullableNumber(levels?.breakthrough),
214
+ support: toNullableNumber(levels?.support),
215
+ cost_level: toNullableNumber(levels?.cost_level),
216
+ resistance: toNullableNumber(levels?.resistance),
217
+ take_profit: toNullableNumber(levels?.take_profit),
218
+ gap: toNullableNumber(levels?.gap),
219
+ target: toNullableNumber(levels?.target),
220
+ round_number: toNullableNumber(levels?.round_number),
221
+ score: normalizeScore(levels?.score ?? null),
222
+ };
223
+ }
224
+ function toNullableNumber(value) {
225
+ return Number.isFinite(Number(value)) ? Number(value) : null;
226
+ }
227
+ function normalizeScore(value) {
228
+ if (!Number.isFinite(Number(value))) {
229
+ return null;
230
+ }
231
+ return Math.trunc(Number(value));
232
+ }
@@ -0,0 +1 @@
1
+ export declare function parseJsonBlock<T>(responseText: string): T | null;
@@ -0,0 +1,13 @@
1
+ export function parseJsonBlock(responseText) {
2
+ const fenced = responseText.match(/```json\s*([\s\S]*?)\s*```/i);
3
+ const candidate = fenced?.[1] ?? responseText.match(/\{[\s\S]*\}/)?.[0];
4
+ if (!candidate) {
5
+ return null;
6
+ }
7
+ try {
8
+ return JSON.parse(candidate);
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ }
@@ -0,0 +1,5 @@
1
+ import type { KeyLevels } from "../../types/domain.js";
2
+ export declare function parseKeyLevels(responseText: string): KeyLevels | null;
3
+ export declare function extractAnalysisConclusion(analysisText: string): string;
4
+ export declare function validateKeyLevels(levels: KeyLevels): void;
5
+ export declare function formatKeyLevelsAnalysis(analysisText: string, levels: KeyLevels | null): string;
@@ -0,0 +1,61 @@
1
+ const PRICE_FIELDS = [
2
+ ["当前价格", "current_price"],
3
+ ["止损位", "stop_loss"],
4
+ ["突破位", "breakthrough"],
5
+ ["支撑位", "support"],
6
+ ["成本位", "cost_level"],
7
+ ["压力位", "resistance"],
8
+ ["止盈位", "take_profit"],
9
+ ["缺口位", "gap"],
10
+ ["目标位", "target"],
11
+ ["整数关", "round_number"],
12
+ ];
13
+ export function parseKeyLevels(responseText) {
14
+ const fenced = responseText.match(/```json\s*([\s\S]*?)\s*```/);
15
+ const candidate = fenced?.[1] ?? responseText.match(/\{[\s\S]*"current_price"[\s\S]*\}/)?.[0];
16
+ if (!candidate) {
17
+ return null;
18
+ }
19
+ try {
20
+ return JSON.parse(candidate);
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ export function extractAnalysisConclusion(analysisText) {
27
+ return analysisText.replace(/```json\s*[\s\S]*?\s*```/g, "").trim();
28
+ }
29
+ export function validateKeyLevels(levels) {
30
+ if (!(levels.current_price > 0)) {
31
+ throw new Error(`current_price must be > 0, got ${levels.current_price}`);
32
+ }
33
+ if (!Number.isInteger(levels.score) || levels.score < 1 || levels.score > 10) {
34
+ throw new Error(`score must be integer 1-10, got ${levels.score}`);
35
+ }
36
+ for (const [, key] of PRICE_FIELDS.slice(1)) {
37
+ if (key === "gap") {
38
+ continue;
39
+ }
40
+ const value = levels[key];
41
+ if (value != null && value < 0) {
42
+ throw new Error(`${String(key)} must be >= 0, got ${value}`);
43
+ }
44
+ }
45
+ if (levels.gap != null && !Number.isFinite(levels.gap)) {
46
+ throw new Error(`gap must be a finite number, got ${levels.gap}`);
47
+ }
48
+ }
49
+ export function formatKeyLevelsAnalysis(analysisText, levels) {
50
+ const conclusion = extractAnalysisConclusion(analysisText);
51
+ const lines = [conclusion];
52
+ if (levels) {
53
+ lines.push("", "📊 关键价位汇总:");
54
+ for (const [label, key] of PRICE_FIELDS) {
55
+ const value = levels[key];
56
+ lines.push(` ${label}: ${value != null ? value.toFixed(2) : "暂无"}`);
57
+ }
58
+ lines.push("", ` 技术面评分: ${levels.score}/10`);
59
+ }
60
+ return lines.join("\n");
61
+ }
@@ -0,0 +1,2 @@
1
+ import type { PostCloseReviewResult } from "../types/composite-analysis.js";
2
+ export declare function parsePostCloseReviewPayload(analysisText: string): Omit<PostCloseReviewResult, "analysisText">;
@@ -0,0 +1,79 @@
1
+ import { parseJsonBlock } from "./json-block.parser.js";
2
+ export function parsePostCloseReviewPayload(analysisText) {
3
+ const parsed = parseJsonBlock(analysisText) ?? {};
4
+ return {
5
+ decision: normalizeDecision(parsed.decision),
6
+ decisionReason: normalizeText(parsed.decision_reason),
7
+ sessionSummary: normalizeText(parsed.session_summary),
8
+ marketSectorSummary: normalizeText(parsed.market_sector_summary),
9
+ newsSummary: normalizeText(parsed.news_summary),
10
+ actionAdvice: normalizeText(parsed.action_advice),
11
+ marketBias: normalizeMarketBias(parsed.market_bias),
12
+ sectorBias: normalizeMarketBias(parsed.sector_bias),
13
+ newsImpact: normalizeNewsImpact(parsed.news_impact),
14
+ levels: normalizeLevels(parsed.levels),
15
+ };
16
+ }
17
+ function normalizeDecision(value) {
18
+ switch (String(value ?? "").trim().toLowerCase()) {
19
+ case "keep":
20
+ case "adjust":
21
+ case "recompute":
22
+ case "invalidate":
23
+ return String(value).trim().toLowerCase();
24
+ default:
25
+ return "recompute";
26
+ }
27
+ }
28
+ function normalizeMarketBias(value) {
29
+ switch (String(value ?? "").trim().toLowerCase()) {
30
+ case "tailwind":
31
+ case "headwind":
32
+ return String(value).trim().toLowerCase();
33
+ default:
34
+ return "neutral";
35
+ }
36
+ }
37
+ function normalizeNewsImpact(value) {
38
+ switch (String(value ?? "").trim().toLowerCase()) {
39
+ case "supportive":
40
+ case "disruptive":
41
+ return String(value).trim().toLowerCase();
42
+ default:
43
+ return "neutral";
44
+ }
45
+ }
46
+ function normalizeText(value) {
47
+ return String(value ?? "").trim();
48
+ }
49
+ function normalizeLevels(value) {
50
+ if (!value || typeof value !== "object") {
51
+ return null;
52
+ }
53
+ const currentPrice = Number(value.current_price ?? NaN);
54
+ const score = Number(value.score ?? NaN);
55
+ if (!Number.isFinite(currentPrice) || !Number.isFinite(score)) {
56
+ return null;
57
+ }
58
+ return {
59
+ current_price: currentPrice,
60
+ stop_loss: toNullableNumber(value.stop_loss),
61
+ breakthrough: toNullableNumber(value.breakthrough),
62
+ support: toNullableNumber(value.support),
63
+ cost_level: toNullableNumber(value.cost_level),
64
+ resistance: toNullableNumber(value.resistance),
65
+ take_profit: toNullableNumber(value.take_profit),
66
+ gap: toNullableNumber(value.gap),
67
+ target: toNullableNumber(value.target),
68
+ round_number: toNullableNumber(value.round_number),
69
+ score: Math.max(1, Math.min(10, Math.trunc(score))),
70
+ analysis_text: "",
71
+ };
72
+ }
73
+ function toNullableNumber(value) {
74
+ if (value == null || value === "") {
75
+ return null;
76
+ }
77
+ const num = Number(value);
78
+ return Number.isFinite(num) ? num : null;
79
+ }
@@ -0,0 +1,6 @@
1
+ export interface WatchlistProfileExtraction {
2
+ sector: string | null;
3
+ themes: string[];
4
+ confidence: "low" | "medium" | "high";
5
+ }
6
+ export declare function parseWatchlistProfileExtraction(responseText: string): WatchlistProfileExtraction | null;
@@ -0,0 +1,93 @@
1
+ import { parseJsonBlock } from "./json-block.parser.js";
2
+ const MAX_THEMES = 20;
3
+ const GENERIC_THEME_LABELS = new Set([
4
+ "公司新闻",
5
+ "最新公告",
6
+ "最新新闻",
7
+ "市场快讯",
8
+ "公司公告",
9
+ "行业动态",
10
+ "板块动态",
11
+ "题材动态",
12
+ "概念动态",
13
+ "资金流向",
14
+ "龙虎榜",
15
+ ]);
16
+ export function parseWatchlistProfileExtraction(responseText) {
17
+ const parsed = parseJsonBlock(responseText);
18
+ if (!parsed) {
19
+ return null;
20
+ }
21
+ return {
22
+ sector: normalizeSector(parsed.sector),
23
+ themes: normalizeThemes(parsed.themes),
24
+ confidence: normalizeConfidence(parsed.confidence),
25
+ };
26
+ }
27
+ function normalizeSector(value) {
28
+ if (typeof value !== "string") {
29
+ return null;
30
+ }
31
+ const text = cleanLabel(value);
32
+ if (!text || isNullLike(text)) {
33
+ return null;
34
+ }
35
+ return text;
36
+ }
37
+ function normalizeThemes(value) {
38
+ const rawItems = Array.isArray(value)
39
+ ? value
40
+ : typeof value === "string"
41
+ ? [value]
42
+ : [];
43
+ const result = [];
44
+ const seen = new Set();
45
+ for (const rawItem of rawItems) {
46
+ const parts = splitThemeItem(String(rawItem ?? ""));
47
+ for (const part of parts) {
48
+ const cleaned = cleanLabel(part);
49
+ if (!cleaned || isNullLike(cleaned) || isGenericTheme(cleaned) || seen.has(cleaned)) {
50
+ continue;
51
+ }
52
+ seen.add(cleaned);
53
+ result.push(cleaned);
54
+ if (result.length >= MAX_THEMES) {
55
+ return result;
56
+ }
57
+ }
58
+ }
59
+ return result;
60
+ }
61
+ function splitThemeItem(value) {
62
+ return value
63
+ .split(/[、,,;;|]/)
64
+ .flatMap((item) => item.split(/\s*\/\s*/))
65
+ .map((item) => item.trim())
66
+ .filter(Boolean);
67
+ }
68
+ function cleanLabel(value) {
69
+ return value
70
+ .trim()
71
+ .replace(/[《》"'“”]/g, "")
72
+ .replace(/^[::、,,;;\-]+/, "")
73
+ .replace(/[::、,,;;。]+$/g, "")
74
+ .replace(/等+$/g, "")
75
+ .replace(/[((].*?[))]/g, "")
76
+ .replace(/\s+/g, "")
77
+ .trim();
78
+ }
79
+ function isNullLike(value) {
80
+ return ["无", "暂无", "未知", "未识别", "未提及", "null", "none", "n/a"].includes(value.toLowerCase());
81
+ }
82
+ function isGenericTheme(value) {
83
+ if (GENERIC_THEME_LABELS.has(value)) {
84
+ return true;
85
+ }
86
+ return /(新闻|公告|快讯|资讯|消息|复盘)$/.test(value);
87
+ }
88
+ function normalizeConfidence(value) {
89
+ if (value === "high" || value === "medium") {
90
+ return value;
91
+ }
92
+ return "low";
93
+ }
@@ -0,0 +1,12 @@
1
+ import type { TickflowApiKeyLevel } from "../../config/tickflow-access.js";
2
+ import { FinancialService } from "../../services/financial-service.js";
3
+ import { FinancialLiteService } from "../../services/financial-lite-service.js";
4
+ import type { FinancialAnalysisContext } from "../types/composite-analysis.js";
5
+ export declare class FinancialAnalysisProvider {
6
+ private readonly tickflowApiKeyLevel;
7
+ private readonly financialService;
8
+ private readonly financialLiteService;
9
+ constructor(tickflowApiKeyLevel: TickflowApiKeyLevel, financialService: FinancialService, financialLiteService: FinancialLiteService);
10
+ load(symbol: string, companyName: string): Promise<FinancialAnalysisContext>;
11
+ private loadLite;
12
+ }
@@ -0,0 +1,85 @@
1
+ export class FinancialAnalysisProvider {
2
+ tickflowApiKeyLevel;
3
+ financialService;
4
+ financialLiteService;
5
+ constructor(tickflowApiKeyLevel, financialService, financialLiteService) {
6
+ this.tickflowApiKeyLevel = tickflowApiKeyLevel;
7
+ this.financialService = financialService;
8
+ this.financialLiteService = financialLiteService;
9
+ }
10
+ async load(symbol, companyName) {
11
+ if (this.tickflowApiKeyLevel === "expert") {
12
+ try {
13
+ const snapshot = await this.financialService.fetchSnapshot(symbol, { latest: true });
14
+ const available = snapshot.income.length > 0 ||
15
+ snapshot.metrics.length > 0 ||
16
+ snapshot.cashFlow.length > 0 ||
17
+ snapshot.balanceSheet.length > 0;
18
+ if (available) {
19
+ return {
20
+ symbol,
21
+ companyName,
22
+ mode: "full",
23
+ source: "tickflow",
24
+ snapshot,
25
+ liteSnapshot: null,
26
+ available: true,
27
+ note: null,
28
+ };
29
+ }
30
+ }
31
+ catch (error) {
32
+ const message = error instanceof Error ? error.message : String(error);
33
+ console.warn(`[analyze] financial full fetch skipped for ${symbol}: ${message}`);
34
+ const liteContext = await this.loadLite(symbol, companyName, `tickflow_full_failed: ${message}`);
35
+ if (liteContext) {
36
+ return liteContext;
37
+ }
38
+ return buildUnavailableFinancialContext(symbol, companyName, `tickflow_full_failed: ${message}`);
39
+ }
40
+ }
41
+ const liteReason = this.tickflowApiKeyLevel === "expert"
42
+ ? "tickflow_full_empty"
43
+ : `tickflow_level_${this.tickflowApiKeyLevel}_uses_lite`;
44
+ const liteContext = await this.loadLite(symbol, companyName, liteReason);
45
+ if (liteContext) {
46
+ return liteContext;
47
+ }
48
+ return buildUnavailableFinancialContext(symbol, companyName, liteReason);
49
+ }
50
+ async loadLite(symbol, companyName, note) {
51
+ try {
52
+ const snapshot = await this.financialLiteService.fetchSnapshot(symbol, companyName);
53
+ if (!snapshot || snapshot.metrics.length === 0) {
54
+ return null;
55
+ }
56
+ return {
57
+ symbol,
58
+ companyName,
59
+ mode: "lite",
60
+ source: "mx_select_stock",
61
+ snapshot: null,
62
+ liteSnapshot: snapshot,
63
+ available: true,
64
+ note,
65
+ };
66
+ }
67
+ catch (error) {
68
+ const message = error instanceof Error ? error.message : String(error);
69
+ console.warn(`[analyze] financial lite fetch skipped for ${symbol}: ${message}`);
70
+ return null;
71
+ }
72
+ }
73
+ }
74
+ function buildUnavailableFinancialContext(symbol, companyName, note) {
75
+ return {
76
+ symbol,
77
+ companyName,
78
+ mode: "none",
79
+ source: "none",
80
+ snapshot: null,
81
+ liteSnapshot: null,
82
+ available: false,
83
+ note,
84
+ };
85
+ }
@@ -0,0 +1,27 @@
1
+ import { type TickflowApiKeyLevel } from "../../config/tickflow-access.js";
2
+ import { WatchlistService } from "../../services/watchlist-service.js";
3
+ import { KlineService } from "../../services/kline-service.js";
4
+ import { QuoteService } from "../../services/quote-service.js";
5
+ import { IndicatorService } from "../../services/indicator-service.js";
6
+ import { ReviewMemoryService } from "../../services/review-memory-service.js";
7
+ import { TradingCalendarService } from "../../services/trading-calendar-service.js";
8
+ import { KlinesRepository } from "../../storage/repositories/klines-repo.js";
9
+ import { IntradayKlinesRepository } from "../../storage/repositories/intraday-klines-repo.js";
10
+ import { IndicatorsRepository } from "../../storage/repositories/indicators-repo.js";
11
+ import type { MarketAnalysisContext } from "../types/composite-analysis.js";
12
+ export declare class MarketAnalysisProvider {
13
+ private readonly tickflowApiKeyLevel;
14
+ private readonly watchlistService;
15
+ private readonly klineService;
16
+ private readonly quoteService;
17
+ private readonly indicatorService;
18
+ private readonly reviewMemoryService;
19
+ private readonly tradingCalendarService;
20
+ private readonly klinesRepository;
21
+ private readonly intradayKlinesRepository;
22
+ private readonly indicatorsRepository;
23
+ constructor(tickflowApiKeyLevel: TickflowApiKeyLevel, watchlistService: WatchlistService, klineService: KlineService, quoteService: QuoteService, indicatorService: IndicatorService, reviewMemoryService: ReviewMemoryService, tradingCalendarService: TradingCalendarService, klinesRepository: KlinesRepository, intradayKlinesRepository: IntradayKlinesRepository, indicatorsRepository: IndicatorsRepository);
24
+ load(symbol: string): Promise<MarketAnalysisContext>;
25
+ private loadMarketOverview;
26
+ private loadIndexSnapshot;
27
+ }