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,91 @@
1
+ import { keyLevelsHistorySchema } from "../schemas.js";
2
+ const KEY_LEVELS_HISTORY_TABLE = "key_levels_history";
3
+ export class KeyLevelsHistoryRepository {
4
+ db;
5
+ constructor(db) {
6
+ this.db = db;
7
+ }
8
+ async saveDailySnapshot(entry) {
9
+ const row = toKeyLevelsHistoryRow(entry);
10
+ if (!(await this.db.hasTable(KEY_LEVELS_HISTORY_TABLE))) {
11
+ await this.db.createTable(KEY_LEVELS_HISTORY_TABLE, [row], keyLevelsHistorySchema);
12
+ return;
13
+ }
14
+ const table = await this.db.openTable(KEY_LEVELS_HISTORY_TABLE);
15
+ await table.delete(`symbol = '${escapeSqlString(entry.symbol)}' and analysis_date = '${escapeSqlString(entry.analysis_date)}'`);
16
+ await table.add([row]);
17
+ }
18
+ async listBySymbol(symbol, limit) {
19
+ if (!(await this.db.hasTable(KEY_LEVELS_HISTORY_TABLE))) {
20
+ return [];
21
+ }
22
+ const table = await this.db.openTable(KEY_LEVELS_HISTORY_TABLE);
23
+ const rows = (await table
24
+ .query()
25
+ .where(`symbol = '${escapeSqlString(symbol)}'`)
26
+ .toArray());
27
+ return limitRows(rows, limit).map(fromKeyLevelsHistoryRow);
28
+ }
29
+ async listLatest(limit) {
30
+ if (!(await this.db.hasTable(KEY_LEVELS_HISTORY_TABLE))) {
31
+ return [];
32
+ }
33
+ const table = await this.db.openTable(KEY_LEVELS_HISTORY_TABLE);
34
+ const rows = (await table.query().toArray());
35
+ return limitRows(rows, limit).map(fromKeyLevelsHistoryRow);
36
+ }
37
+ }
38
+ function limitRows(rows, limit) {
39
+ const normalized = limit == null ? rows.length : Math.max(1, Math.trunc(limit));
40
+ return rows.slice(-normalized).reverse();
41
+ }
42
+ function toKeyLevelsHistoryRow(entry) {
43
+ return {
44
+ symbol: entry.symbol,
45
+ analysis_date: entry.analysis_date,
46
+ activated_at: entry.activated_at,
47
+ profile: entry.profile,
48
+ current_price: entry.current_price,
49
+ stop_loss: entry.stop_loss,
50
+ breakthrough: entry.breakthrough,
51
+ support: entry.support,
52
+ cost_level: entry.cost_level,
53
+ resistance: entry.resistance,
54
+ take_profit: entry.take_profit,
55
+ gap: entry.gap,
56
+ target: entry.target,
57
+ round_number: entry.round_number,
58
+ analysis_text: entry.analysis_text,
59
+ score: entry.score == null ? null : Math.trunc(entry.score),
60
+ };
61
+ }
62
+ function fromKeyLevelsHistoryRow(row) {
63
+ return {
64
+ symbol: String(row.symbol),
65
+ analysis_date: String(row.analysis_date),
66
+ activated_at: String(row.activated_at ?? ""),
67
+ profile: "composite",
68
+ current_price: toNullableNumber(row.current_price),
69
+ stop_loss: toNullableNumber(row.stop_loss),
70
+ breakthrough: toNullableNumber(row.breakthrough),
71
+ support: toNullableNumber(row.support),
72
+ cost_level: toNullableNumber(row.cost_level),
73
+ resistance: toNullableNumber(row.resistance),
74
+ take_profit: toNullableNumber(row.take_profit),
75
+ gap: toNullableNumber(row.gap),
76
+ target: toNullableNumber(row.target),
77
+ round_number: toNullableNumber(row.round_number),
78
+ analysis_text: String(row.analysis_text ?? ""),
79
+ score: toNullableNumber(row.score),
80
+ };
81
+ }
82
+ function toNullableNumber(value) {
83
+ if (value == null) {
84
+ return null;
85
+ }
86
+ const num = Number(value);
87
+ return Number.isFinite(num) ? num : null;
88
+ }
89
+ function escapeSqlString(value) {
90
+ return value.replace(/'/g, "''");
91
+ }
@@ -0,0 +1,9 @@
1
+ import { Database } from "../db.js";
2
+ import type { KeyLevels } from "../../types/domain.js";
3
+ export declare class KeyLevelsRepository {
4
+ private readonly db;
5
+ constructor(db: Database);
6
+ save(symbol: string, levels: KeyLevels): Promise<void>;
7
+ remove(symbol: string): Promise<void>;
8
+ getBySymbol(symbol: string): Promise<KeyLevels | null>;
9
+ }
@@ -0,0 +1,83 @@
1
+ import { keyLevelsSchema } from "../schemas.js";
2
+ const KEY_LEVELS_TABLE = "key_levels";
3
+ export class KeyLevelsRepository {
4
+ db;
5
+ constructor(db) {
6
+ this.db = db;
7
+ }
8
+ async save(symbol, levels) {
9
+ const row = toKeyLevelsRow(symbol, levels);
10
+ if (!(await this.db.hasTable(KEY_LEVELS_TABLE))) {
11
+ await this.db.createTable(KEY_LEVELS_TABLE, [row], keyLevelsSchema);
12
+ return;
13
+ }
14
+ const table = await this.db.openTable(KEY_LEVELS_TABLE);
15
+ await table.delete(`symbol = '${escapeSqlString(symbol)}'`);
16
+ await table.add([row]);
17
+ }
18
+ async remove(symbol) {
19
+ if (!(await this.db.hasTable(KEY_LEVELS_TABLE))) {
20
+ return;
21
+ }
22
+ const table = await this.db.openTable(KEY_LEVELS_TABLE);
23
+ await table.delete(`symbol = '${escapeSqlString(symbol)}'`);
24
+ }
25
+ async getBySymbol(symbol) {
26
+ if (!(await this.db.hasTable(KEY_LEVELS_TABLE))) {
27
+ return null;
28
+ }
29
+ const table = await this.db.openTable(KEY_LEVELS_TABLE);
30
+ const rows = (await table
31
+ .query()
32
+ .where(`symbol = '${escapeSqlString(symbol)}'`)
33
+ .toArray());
34
+ if (rows.length === 0) {
35
+ return null;
36
+ }
37
+ const row = rows[rows.length - 1];
38
+ return {
39
+ symbol: String(row.symbol),
40
+ analysis_date: String(row.analysis_date),
41
+ current_price: Number(row.current_price),
42
+ stop_loss: toNullableNumber(row.stop_loss),
43
+ breakthrough: toNullableNumber(row.breakthrough),
44
+ support: toNullableNumber(row.support),
45
+ cost_level: toNullableNumber(row.cost_level),
46
+ resistance: toNullableNumber(row.resistance),
47
+ take_profit: toNullableNumber(row.take_profit),
48
+ gap: toNullableNumber(row.gap),
49
+ target: toNullableNumber(row.target),
50
+ round_number: toNullableNumber(row.round_number),
51
+ analysis_text: String(row.analysis_text ?? ""),
52
+ score: Number(row.score ?? 0),
53
+ };
54
+ }
55
+ }
56
+ function toKeyLevelsRow(symbol, levels) {
57
+ return {
58
+ symbol,
59
+ analysis_date: levels.analysis_date ?? "",
60
+ current_price: levels.current_price,
61
+ stop_loss: levels.stop_loss ?? null,
62
+ breakthrough: levels.breakthrough ?? null,
63
+ support: levels.support ?? null,
64
+ cost_level: levels.cost_level ?? null,
65
+ resistance: levels.resistance ?? null,
66
+ take_profit: levels.take_profit ?? null,
67
+ gap: levels.gap ?? null,
68
+ target: levels.target ?? null,
69
+ round_number: levels.round_number ?? null,
70
+ analysis_text: levels.analysis_text,
71
+ score: levels.score,
72
+ };
73
+ }
74
+ function toNullableNumber(value) {
75
+ if (value == null) {
76
+ return null;
77
+ }
78
+ const num = Number(value);
79
+ return Number.isFinite(num) ? num : null;
80
+ }
81
+ function escapeSqlString(value) {
82
+ return value.replace(/'/g, "''");
83
+ }
@@ -0,0 +1,8 @@
1
+ import { Database } from "../db.js";
2
+ import type { TickFlowKlineRow } from "../../types/tickflow.js";
3
+ export declare class KlinesRepository {
4
+ private readonly db;
5
+ constructor(db: Database);
6
+ saveAll(symbol: string, rows: TickFlowKlineRow[]): Promise<void>;
7
+ listBySymbol(symbol: string): Promise<TickFlowKlineRow[]>;
8
+ }
@@ -0,0 +1,60 @@
1
+ import { klinesDailySchema } from "../schemas.js";
2
+ const KLINES_TABLE = "klines_daily";
3
+ export class KlinesRepository {
4
+ db;
5
+ constructor(db) {
6
+ this.db = db;
7
+ }
8
+ async saveAll(symbol, rows) {
9
+ if (!(await this.db.hasTable(KLINES_TABLE))) {
10
+ await this.db.createTable(KLINES_TABLE, rows.map(toKlineRow), klinesDailySchema);
11
+ return;
12
+ }
13
+ const table = await this.db.openTable(KLINES_TABLE);
14
+ await table.delete(`symbol = '${escapeSqlString(symbol)}'`);
15
+ if (rows.length > 0) {
16
+ await table.add(rows.map(toKlineRow));
17
+ }
18
+ }
19
+ async listBySymbol(symbol) {
20
+ if (!(await this.db.hasTable(KLINES_TABLE))) {
21
+ return [];
22
+ }
23
+ const table = await this.db.openTable(KLINES_TABLE);
24
+ const query = table
25
+ .query()
26
+ .where(`symbol = '${escapeSqlString(symbol)}'`);
27
+ const rows = (await query.toArray());
28
+ return rows
29
+ .map((row) => ({
30
+ symbol: String(row.symbol),
31
+ trade_date: String(row.trade_date),
32
+ timestamp: Number(row.timestamp),
33
+ open: Number(row.open),
34
+ high: Number(row.high),
35
+ low: Number(row.low),
36
+ close: Number(row.close),
37
+ volume: Number(row.volume),
38
+ amount: Number(row.amount),
39
+ prev_close: Number(row.prev_close ?? 0),
40
+ }))
41
+ .sort((a, b) => a.trade_date.localeCompare(b.trade_date));
42
+ }
43
+ }
44
+ function toKlineRow(row) {
45
+ return {
46
+ symbol: row.symbol,
47
+ trade_date: row.trade_date,
48
+ timestamp: row.timestamp,
49
+ open: row.open,
50
+ high: row.high,
51
+ low: row.low,
52
+ close: row.close,
53
+ volume: row.volume,
54
+ amount: row.amount,
55
+ prev_close: row.prev_close,
56
+ };
57
+ }
58
+ function escapeSqlString(value) {
59
+ return value.replace(/'/g, "''");
60
+ }
@@ -0,0 +1,9 @@
1
+ import type { NewsAnalysisEntry } from "../../types/domain.js";
2
+ import { Database } from "../db.js";
3
+ export declare class NewsAnalysisRepository {
4
+ private readonly db;
5
+ constructor(db: Database);
6
+ append(entry: NewsAnalysisEntry): Promise<void>;
7
+ getLatest(symbol: string): Promise<NewsAnalysisEntry | null>;
8
+ listLatest(symbol: string, limit: number): Promise<NewsAnalysisEntry[]>;
9
+ }
@@ -0,0 +1,107 @@
1
+ import { newsAnalysisSchema } from "../schemas.js";
2
+ const NEWS_ANALYSIS_TABLE = "news_analysis";
3
+ export class NewsAnalysisRepository {
4
+ db;
5
+ constructor(db) {
6
+ this.db = db;
7
+ }
8
+ async append(entry) {
9
+ const row = toNewsAnalysisRow(entry);
10
+ if (!(await this.db.hasTable(NEWS_ANALYSIS_TABLE))) {
11
+ await this.db.createTable(NEWS_ANALYSIS_TABLE, [row], newsAnalysisSchema);
12
+ return;
13
+ }
14
+ const table = await this.db.openTable(NEWS_ANALYSIS_TABLE);
15
+ await table.add([row]);
16
+ }
17
+ async getLatest(symbol) {
18
+ const rows = await this.listLatest(symbol, 1);
19
+ return rows[0] ?? null;
20
+ }
21
+ async listLatest(symbol, limit) {
22
+ if (!(await this.db.hasTable(NEWS_ANALYSIS_TABLE))) {
23
+ return [];
24
+ }
25
+ const table = await this.db.openTable(NEWS_ANALYSIS_TABLE);
26
+ const rows = (await table
27
+ .query()
28
+ .where(`symbol = '${escapeSqlString(symbol)}'`)
29
+ .toArray());
30
+ return rows.slice(-Math.max(1, limit)).reverse().map((row) => fromNewsAnalysisRow(row));
31
+ }
32
+ }
33
+ function toNewsAnalysisRow(entry) {
34
+ return {
35
+ symbol: entry.symbol,
36
+ analysis_date: entry.analysis_date,
37
+ query: entry.query,
38
+ analysis_text: entry.analysis_text,
39
+ score: entry.score == null ? null : Math.trunc(entry.score),
40
+ bias: entry.bias,
41
+ catalysts_json: JSON.stringify(entry.catalysts),
42
+ risks_json: JSON.stringify(entry.risks),
43
+ watch_items_json: JSON.stringify(entry.watch_items),
44
+ source_count: entry.source_count,
45
+ evidence_json: JSON.stringify(entry.evidence),
46
+ };
47
+ }
48
+ function fromNewsAnalysisRow(row) {
49
+ return {
50
+ symbol: String(row.symbol),
51
+ analysis_date: String(row.analysis_date),
52
+ query: String(row.query ?? ""),
53
+ analysis_text: String(row.analysis_text ?? ""),
54
+ score: toNullableNumber(row.score),
55
+ bias: normalizeBias(row.bias),
56
+ catalysts: parseJsonArray(row.catalysts_json),
57
+ risks: parseJsonArray(row.risks_json),
58
+ watch_items: parseJsonArray(row.watch_items_json),
59
+ source_count: Number(row.source_count ?? 0),
60
+ evidence: parseJsonObject(row.evidence_json, {
61
+ available: false,
62
+ source_count: 0,
63
+ documents: [],
64
+ }),
65
+ };
66
+ }
67
+ function toNullableNumber(value) {
68
+ if (value == null) {
69
+ return null;
70
+ }
71
+ const num = Number(value);
72
+ return Number.isFinite(num) ? num : null;
73
+ }
74
+ function parseJsonArray(value) {
75
+ if (typeof value !== "string" || !value.trim()) {
76
+ return [];
77
+ }
78
+ try {
79
+ const parsed = JSON.parse(value);
80
+ return Array.isArray(parsed)
81
+ ? parsed.map((item) => String(item ?? "").trim()).filter(Boolean)
82
+ : [];
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ }
88
+ function parseJsonObject(value, fallback) {
89
+ if (typeof value !== "string" || !value.trim()) {
90
+ return fallback;
91
+ }
92
+ try {
93
+ return JSON.parse(value);
94
+ }
95
+ catch {
96
+ return fallback;
97
+ }
98
+ }
99
+ function normalizeBias(value) {
100
+ if (value === "positive" || value === "negative") {
101
+ return value;
102
+ }
103
+ return "neutral";
104
+ }
105
+ function escapeSqlString(value) {
106
+ return value.replace(/'/g, "''");
107
+ }
@@ -0,0 +1,9 @@
1
+ import type { TechnicalAnalysisEntry } from "../../types/domain.js";
2
+ import { Database } from "../db.js";
3
+ export declare class TechnicalAnalysisRepository {
4
+ private readonly db;
5
+ constructor(db: Database);
6
+ append(entry: TechnicalAnalysisEntry): Promise<void>;
7
+ getLatest(symbol: string): Promise<TechnicalAnalysisEntry | null>;
8
+ listLatest(symbol: string, limit: number): Promise<TechnicalAnalysisEntry[]>;
9
+ }
@@ -0,0 +1,80 @@
1
+ import { technicalAnalysisSchema } from "../schemas.js";
2
+ const TECHNICAL_ANALYSIS_TABLE = "technical_analysis";
3
+ export class TechnicalAnalysisRepository {
4
+ db;
5
+ constructor(db) {
6
+ this.db = db;
7
+ }
8
+ async append(entry) {
9
+ const row = toTechnicalAnalysisRow(entry);
10
+ if (!(await this.db.hasTable(TECHNICAL_ANALYSIS_TABLE))) {
11
+ await this.db.createTable(TECHNICAL_ANALYSIS_TABLE, [row], technicalAnalysisSchema);
12
+ return;
13
+ }
14
+ const table = await this.db.openTable(TECHNICAL_ANALYSIS_TABLE);
15
+ await table.add([row]);
16
+ }
17
+ async getLatest(symbol) {
18
+ const rows = await this.listLatest(symbol, 1);
19
+ return rows[0] ?? null;
20
+ }
21
+ async listLatest(symbol, limit) {
22
+ if (!(await this.db.hasTable(TECHNICAL_ANALYSIS_TABLE))) {
23
+ return [];
24
+ }
25
+ const table = await this.db.openTable(TECHNICAL_ANALYSIS_TABLE);
26
+ const rows = (await table
27
+ .query()
28
+ .where(`symbol = '${escapeSqlString(symbol)}'`)
29
+ .toArray());
30
+ return rows.slice(-Math.max(1, limit)).reverse().map((row) => fromTechnicalAnalysisRow(row));
31
+ }
32
+ }
33
+ function toTechnicalAnalysisRow(entry) {
34
+ return {
35
+ symbol: entry.symbol,
36
+ analysis_date: entry.analysis_date,
37
+ analysis_text: entry.analysis_text,
38
+ structured_ok: entry.structured_ok ? 1 : 0,
39
+ current_price: entry.current_price,
40
+ stop_loss: entry.stop_loss,
41
+ breakthrough: entry.breakthrough,
42
+ support: entry.support,
43
+ cost_level: entry.cost_level,
44
+ resistance: entry.resistance,
45
+ take_profit: entry.take_profit,
46
+ gap: entry.gap,
47
+ target: entry.target,
48
+ round_number: entry.round_number,
49
+ score: entry.score == null ? null : Math.trunc(entry.score),
50
+ };
51
+ }
52
+ function fromTechnicalAnalysisRow(row) {
53
+ return {
54
+ symbol: String(row.symbol),
55
+ analysis_date: String(row.analysis_date),
56
+ analysis_text: String(row.analysis_text ?? ""),
57
+ structured_ok: Boolean(Number(row.structured_ok ?? 0)),
58
+ current_price: toNullableNumber(row.current_price),
59
+ stop_loss: toNullableNumber(row.stop_loss),
60
+ breakthrough: toNullableNumber(row.breakthrough),
61
+ support: toNullableNumber(row.support),
62
+ cost_level: toNullableNumber(row.cost_level),
63
+ resistance: toNullableNumber(row.resistance),
64
+ take_profit: toNullableNumber(row.take_profit),
65
+ gap: toNullableNumber(row.gap),
66
+ target: toNullableNumber(row.target),
67
+ round_number: toNullableNumber(row.round_number),
68
+ score: toNullableNumber(row.score),
69
+ };
70
+ }
71
+ function toNullableNumber(value) {
72
+ if (value == null) {
73
+ return null;
74
+ }
75
+ const num = Number(value);
76
+ return Number.isFinite(num) ? num : null;
77
+ }
78
+ function escapeSqlString(value) {
79
+ return value.replace(/'/g, "''");
80
+ }
@@ -0,0 +1,10 @@
1
+ import type { WatchlistItem } from "../../types/domain.js";
2
+ import { Database } from "../db.js";
3
+ export declare class WatchlistRepository {
4
+ private readonly db;
5
+ constructor(db: Database);
6
+ list(): Promise<WatchlistItem[]>;
7
+ upsert(item: WatchlistItem): Promise<void>;
8
+ remove(symbol: string): Promise<boolean>;
9
+ private ensureSchema;
10
+ }
@@ -0,0 +1,124 @@
1
+ import { watchlistSchema } from "../schemas.js";
2
+ import { normalizeCostPrice } from "../../utils/cost-price.js";
3
+ const WATCHLIST_TABLE = "watchlist";
4
+ export class WatchlistRepository {
5
+ db;
6
+ constructor(db) {
7
+ this.db = db;
8
+ }
9
+ async list() {
10
+ await this.ensureSchema();
11
+ const items = await this.db.tableToArray(WATCHLIST_TABLE);
12
+ return items
13
+ .map((item) => ({
14
+ symbol: String(item.symbol),
15
+ name: String(item.name ?? ""),
16
+ costPrice: normalizeCostPrice(item.costPrice),
17
+ addedAt: String(item.addedAt ?? ""),
18
+ sector: normalizeNullableString(item.sector),
19
+ themes: parseThemes(item.themes),
20
+ themeQuery: normalizeNullableString(item.themeQuery),
21
+ themeUpdatedAt: normalizeNullableString(item.themeUpdatedAt),
22
+ }))
23
+ .sort((a, b) => a.symbol.localeCompare(b.symbol));
24
+ }
25
+ async upsert(item) {
26
+ const row = toWatchlistRow(item);
27
+ if (!(await this.db.hasTable(WATCHLIST_TABLE))) {
28
+ await this.db.createTable(WATCHLIST_TABLE, [row], watchlistSchema);
29
+ return;
30
+ }
31
+ await this.ensureSchema();
32
+ const table = await this.db.openTable(WATCHLIST_TABLE);
33
+ await table.delete(`symbol = '${escapeSqlString(item.symbol)}'`);
34
+ await table.add([row]);
35
+ }
36
+ async remove(symbol) {
37
+ if (!(await this.db.hasTable(WATCHLIST_TABLE))) {
38
+ return false;
39
+ }
40
+ const table = await this.db.openTable(WATCHLIST_TABLE);
41
+ const countBefore = await table.countRows(`symbol = '${escapeSqlString(symbol)}'`);
42
+ if (countBefore === 0) {
43
+ return false;
44
+ }
45
+ await table.delete(`symbol = '${escapeSqlString(symbol)}'`);
46
+ return true;
47
+ }
48
+ async ensureSchema() {
49
+ if (!(await this.db.hasTable(WATCHLIST_TABLE))) {
50
+ return;
51
+ }
52
+ const fields = await this.db.describeTable(WATCHLIST_TABLE);
53
+ const fieldNames = new Set(fields.map((field) => field.name));
54
+ const missingColumns = [];
55
+ if (!fieldNames.has("sector")) {
56
+ missingColumns.push({ name: "sector", valueSql: "''" });
57
+ }
58
+ if (!fieldNames.has("themes")) {
59
+ missingColumns.push({ name: "themes", valueSql: "'[]'" });
60
+ }
61
+ if (!fieldNames.has("themeQuery")) {
62
+ missingColumns.push({ name: "themeQuery", valueSql: "''" });
63
+ }
64
+ if (!fieldNames.has("themeUpdatedAt")) {
65
+ missingColumns.push({ name: "themeUpdatedAt", valueSql: "''" });
66
+ }
67
+ if (missingColumns.length === 0) {
68
+ return;
69
+ }
70
+ const table = await this.db.openTable(WATCHLIST_TABLE);
71
+ await table.addColumns(missingColumns);
72
+ }
73
+ }
74
+ function escapeSqlString(value) {
75
+ return value.replace(/'/g, "''");
76
+ }
77
+ function toWatchlistRow(item) {
78
+ return {
79
+ symbol: item.symbol,
80
+ name: item.name,
81
+ costPrice: item.costPrice ?? 0,
82
+ addedAt: item.addedAt,
83
+ sector: item.sector ?? "",
84
+ themes: JSON.stringify(item.themes),
85
+ themeQuery: item.themeQuery ?? "",
86
+ themeUpdatedAt: item.themeUpdatedAt ?? "",
87
+ };
88
+ }
89
+ function normalizeNullableString(value) {
90
+ if (typeof value !== "string") {
91
+ return null;
92
+ }
93
+ const text = value.trim();
94
+ return text || null;
95
+ }
96
+ function parseThemes(value) {
97
+ if (Array.isArray(value)) {
98
+ return value
99
+ .map((item) => String(item ?? "").trim())
100
+ .filter(Boolean);
101
+ }
102
+ if (typeof value !== "string") {
103
+ return [];
104
+ }
105
+ const text = value.trim();
106
+ if (!text) {
107
+ return [];
108
+ }
109
+ try {
110
+ const parsed = JSON.parse(text);
111
+ if (Array.isArray(parsed)) {
112
+ return parsed
113
+ .map((item) => String(item ?? "").trim())
114
+ .filter(Boolean);
115
+ }
116
+ }
117
+ catch {
118
+ // Fall back to delimiter parsing for legacy rows.
119
+ }
120
+ return text
121
+ .split(/[、,,;;|]/)
122
+ .map((item) => item.trim())
123
+ .filter(Boolean);
124
+ }
@@ -0,0 +1,13 @@
1
+ import { Schema } from "apache-arrow";
2
+ export declare const watchlistSchema: Schema<any>;
3
+ export declare const klinesDailySchema: Schema<any>;
4
+ export declare const klinesIntradaySchema: Schema<any>;
5
+ export declare const indicatorsSchema: Schema<any>;
6
+ export declare const keyLevelsSchema: Schema<any>;
7
+ export declare const keyLevelsHistorySchema: Schema<any>;
8
+ export declare const analysisLogSchema: Schema<any>;
9
+ export declare const technicalAnalysisSchema: Schema<any>;
10
+ export declare const financialAnalysisSchema: Schema<any>;
11
+ export declare const newsAnalysisSchema: Schema<any>;
12
+ export declare const compositeAnalysisSchema: Schema<any>;
13
+ export declare const alertLogSchema: Schema<any>;