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.
- package/README.md +151 -0
- package/day_future.txt +8797 -0
- package/dist/analysis/orchestrators/composite-analysis.orchestrator.d.ts +35 -0
- package/dist/analysis/orchestrators/composite-analysis.orchestrator.js +232 -0
- package/dist/analysis/parsers/json-block.parser.d.ts +1 -0
- package/dist/analysis/parsers/json-block.parser.js +13 -0
- package/dist/analysis/parsers/key-levels.parser.d.ts +5 -0
- package/dist/analysis/parsers/key-levels.parser.js +61 -0
- package/dist/analysis/parsers/post-close-review.parser.d.ts +2 -0
- package/dist/analysis/parsers/post-close-review.parser.js +79 -0
- package/dist/analysis/parsers/watchlist-profile.parser.d.ts +6 -0
- package/dist/analysis/parsers/watchlist-profile.parser.js +93 -0
- package/dist/analysis/providers/financial-analysis.provider.d.ts +12 -0
- package/dist/analysis/providers/financial-analysis.provider.js +85 -0
- package/dist/analysis/providers/market-analysis.provider.d.ts +27 -0
- package/dist/analysis/providers/market-analysis.provider.js +187 -0
- package/dist/analysis/providers/news-analysis.provider.d.ts +9 -0
- package/dist/analysis/providers/news-analysis.provider.js +60 -0
- package/dist/analysis/tasks/analysis-step-task.d.ts +11 -0
- package/dist/analysis/tasks/analysis-step-task.js +1 -0
- package/dist/analysis/tasks/analysis-task.d.ts +13 -0
- package/dist/analysis/tasks/analysis-task.js +1 -0
- package/dist/analysis/tasks/composite-stock-analysis.task.d.ts +17 -0
- package/dist/analysis/tasks/composite-stock-analysis.task.js +47 -0
- package/dist/analysis/tasks/financial-fundamental-lite.task.d.ts +10 -0
- package/dist/analysis/tasks/financial-fundamental-lite.task.js +21 -0
- package/dist/analysis/tasks/financial-fundamental.task.d.ts +12 -0
- package/dist/analysis/tasks/financial-fundamental.task.js +64 -0
- package/dist/analysis/tasks/kline-technical-signal.task.d.ts +10 -0
- package/dist/analysis/tasks/kline-technical-signal.task.js +41 -0
- package/dist/analysis/tasks/kline-technical.task.d.ts +32 -0
- package/dist/analysis/tasks/kline-technical.task.js +61 -0
- package/dist/analysis/tasks/news-catalyst.task.d.ts +11 -0
- package/dist/analysis/tasks/news-catalyst.task.js +62 -0
- package/dist/analysis/tasks/post-close-review.task.d.ts +12 -0
- package/dist/analysis/tasks/post-close-review.task.js +35 -0
- package/dist/analysis/types/composite-analysis.d.ts +123 -0
- package/dist/analysis/types/composite-analysis.js +1 -0
- package/dist/background/daily-update.worker.d.ts +50 -0
- package/dist/background/daily-update.worker.js +546 -0
- package/dist/background/realtime-monitor.worker.d.ts +8 -0
- package/dist/background/realtime-monitor.worker.js +28 -0
- package/dist/bootstrap.d.ts +45 -0
- package/dist/bootstrap.js +214 -0
- package/dist/config/normalize.d.ts +4 -0
- package/dist/config/normalize.js +99 -0
- package/dist/config/schema.d.ts +23 -0
- package/dist/config/schema.js +18 -0
- package/dist/config/tickflow-access.d.ts +4 -0
- package/dist/config/tickflow-access.js +28 -0
- package/dist/constants/market-indexes.d.ts +5 -0
- package/dist/constants/market-indexes.js +4 -0
- package/dist/dev/run-daily-update-loop.d.ts +1 -0
- package/dist/dev/run-daily-update-loop.js +48 -0
- package/dist/dev/run-monitor-loop.d.ts +1 -0
- package/dist/dev/run-monitor-loop.js +60 -0
- package/dist/dev/run-tool.d.ts +1 -0
- package/dist/dev/run-tool.js +49 -0
- package/dist/dev/tickflow-assist-cli.d.ts +2 -0
- package/dist/dev/tickflow-assist-cli.js +525 -0
- package/dist/dev/validate-mx-search.d.ts +1 -0
- package/dist/dev/validate-mx-search.js +212 -0
- package/dist/plugin-commands.d.ts +3 -0
- package/dist/plugin-commands.js +229 -0
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.js +151 -0
- package/dist/prompts/analysis/common-system-prompt.d.ts +1 -0
- package/dist/prompts/analysis/common-system-prompt.js +44 -0
- package/dist/prompts/analysis/composite-analysis-user-prompt.d.ts +11 -0
- package/dist/prompts/analysis/composite-analysis-user-prompt.js +102 -0
- package/dist/prompts/analysis/financial-analysis-user-prompt.d.ts +7 -0
- package/dist/prompts/analysis/financial-analysis-user-prompt.js +106 -0
- package/dist/prompts/analysis/financial-lite-analysis-user-prompt.d.ts +7 -0
- package/dist/prompts/analysis/financial-lite-analysis-user-prompt.js +59 -0
- package/dist/prompts/analysis/index.d.ts +8 -0
- package/dist/prompts/analysis/index.js +8 -0
- package/dist/prompts/analysis/kline-analysis-user-prompt.d.ts +13 -0
- package/dist/prompts/analysis/kline-analysis-user-prompt.js +215 -0
- package/dist/prompts/analysis/news-analysis-user-prompt.d.ts +8 -0
- package/dist/prompts/analysis/news-analysis-user-prompt.js +57 -0
- package/dist/prompts/analysis/post-close-review-user-prompt.d.ts +3 -0
- package/dist/prompts/analysis/post-close-review-user-prompt.js +129 -0
- package/dist/prompts/analysis/watchlist-profile-extraction-prompt.d.ts +7 -0
- package/dist/prompts/analysis/watchlist-profile-extraction-prompt.js +52 -0
- package/dist/prompts/analysis-system-prompt.d.ts +1 -0
- package/dist/prompts/analysis-system-prompt.js +1 -0
- package/dist/prompts/analysis-user-prompt.d.ts +1 -0
- package/dist/prompts/analysis-user-prompt.js +1 -0
- package/dist/runtime/daily-update-process.d.ts +3 -0
- package/dist/runtime/daily-update-process.js +24 -0
- package/dist/runtime/monitor-process.d.ts +3 -0
- package/dist/runtime/monitor-process.js +24 -0
- package/dist/runtime/plugin-api.d.ts +22 -0
- package/dist/runtime/plugin-api.js +2 -0
- package/dist/runtime/process-config.d.ts +8 -0
- package/dist/runtime/process-config.js +35 -0
- package/dist/services/alert-service.d.ts +45 -0
- package/dist/services/alert-service.js +198 -0
- package/dist/services/analysis-service.d.ts +20 -0
- package/dist/services/analysis-service.js +73 -0
- package/dist/services/analysis-view-service.d.ts +23 -0
- package/dist/services/analysis-view-service.js +343 -0
- package/dist/services/financial-lite-service.d.ts +23 -0
- package/dist/services/financial-lite-service.js +201 -0
- package/dist/services/financial-service.d.ts +20 -0
- package/dist/services/financial-service.js +47 -0
- package/dist/services/indicator-service.d.ts +9 -0
- package/dist/services/indicator-service.js +67 -0
- package/dist/services/instrument-service.d.ts +6 -0
- package/dist/services/instrument-service.js +21 -0
- package/dist/services/key-level-service.d.ts +2 -0
- package/dist/services/key-level-service.js +2 -0
- package/dist/services/key-levels-backtest-service.d.ts +71 -0
- package/dist/services/key-levels-backtest-service.js +427 -0
- package/dist/services/kline-service.d.ts +19 -0
- package/dist/services/kline-service.js +91 -0
- package/dist/services/monitor-service.d.ts +44 -0
- package/dist/services/monitor-service.js +598 -0
- package/dist/services/mx-search-service.d.ts +22 -0
- package/dist/services/mx-search-service.js +286 -0
- package/dist/services/post-close-review-service.d.ts +31 -0
- package/dist/services/post-close-review-service.js +402 -0
- package/dist/services/quote-service.d.ts +7 -0
- package/dist/services/quote-service.js +9 -0
- package/dist/services/review-memory-service.d.ts +7 -0
- package/dist/services/review-memory-service.js +76 -0
- package/dist/services/tickflow-client.d.ts +43 -0
- package/dist/services/tickflow-client.js +126 -0
- package/dist/services/trading-calendar-service.d.ts +20 -0
- package/dist/services/trading-calendar-service.js +102 -0
- package/dist/services/update-service.d.ts +21 -0
- package/dist/services/update-service.js +130 -0
- package/dist/services/watchlist-profile-service.d.ts +20 -0
- package/dist/services/watchlist-profile-service.js +76 -0
- package/dist/services/watchlist-service.d.ts +43 -0
- package/dist/services/watchlist-service.js +204 -0
- package/dist/storage/db.d.ts +23 -0
- package/dist/storage/db.js +70 -0
- package/dist/storage/repositories/alert-log-repo.d.ts +15 -0
- package/dist/storage/repositories/alert-log-repo.js +54 -0
- package/dist/storage/repositories/analysis-log-repo.d.ts +8 -0
- package/dist/storage/repositories/analysis-log-repo.js +48 -0
- package/dist/storage/repositories/composite-analysis-repo.d.ts +9 -0
- package/dist/storage/repositories/composite-analysis-repo.js +116 -0
- package/dist/storage/repositories/financial-analysis-repo.d.ts +9 -0
- package/dist/storage/repositories/financial-analysis-repo.js +107 -0
- package/dist/storage/repositories/indicators-repo.d.ts +8 -0
- package/dist/storage/repositories/indicators-repo.js +98 -0
- package/dist/storage/repositories/intraday-klines-repo.d.ts +9 -0
- package/dist/storage/repositories/intraday-klines-repo.js +102 -0
- package/dist/storage/repositories/key-levels-history-repo.d.ts +9 -0
- package/dist/storage/repositories/key-levels-history-repo.js +91 -0
- package/dist/storage/repositories/key-levels-repo.d.ts +9 -0
- package/dist/storage/repositories/key-levels-repo.js +83 -0
- package/dist/storage/repositories/klines-repo.d.ts +8 -0
- package/dist/storage/repositories/klines-repo.js +60 -0
- package/dist/storage/repositories/news-analysis-repo.d.ts +9 -0
- package/dist/storage/repositories/news-analysis-repo.js +107 -0
- package/dist/storage/repositories/technical-analysis-repo.d.ts +9 -0
- package/dist/storage/repositories/technical-analysis-repo.js +80 -0
- package/dist/storage/repositories/watchlist-repo.d.ts +10 -0
- package/dist/storage/repositories/watchlist-repo.js +124 -0
- package/dist/storage/schemas.d.ts +13 -0
- package/dist/storage/schemas.js +177 -0
- package/dist/tools/add-stock.tool.d.ts +13 -0
- package/dist/tools/add-stock.tool.js +123 -0
- package/dist/tools/analyze.tool.d.ts +8 -0
- package/dist/tools/analyze.tool.js +24 -0
- package/dist/tools/backtest-key-levels.tool.d.ts +8 -0
- package/dist/tools/backtest-key-levels.tool.js +43 -0
- package/dist/tools/daily-update-status.tool.d.ts +6 -0
- package/dist/tools/daily-update-status.tool.js +9 -0
- package/dist/tools/fetch-financials.tool.d.ts +8 -0
- package/dist/tools/fetch-financials.tool.js +224 -0
- package/dist/tools/fetch-intraday-klines.tool.d.ts +11 -0
- package/dist/tools/fetch-intraday-klines.tool.js +58 -0
- package/dist/tools/fetch-klines.tool.d.ts +11 -0
- package/dist/tools/fetch-klines.tool.js +61 -0
- package/dist/tools/list-watchlist.tool.d.ts +6 -0
- package/dist/tools/list-watchlist.tool.js +22 -0
- package/dist/tools/monitor-status.tool.d.ts +6 -0
- package/dist/tools/monitor-status.tool.js +9 -0
- package/dist/tools/mx-search.tool.d.ts +8 -0
- package/dist/tools/mx-search.tool.js +77 -0
- package/dist/tools/mx-select-stock.tool.d.ts +8 -0
- package/dist/tools/mx-select-stock.tool.js +118 -0
- package/dist/tools/query-database.tool.d.ts +8 -0
- package/dist/tools/query-database.tool.js +283 -0
- package/dist/tools/refresh-watchlist-names.tool.d.ts +7 -0
- package/dist/tools/refresh-watchlist-names.tool.js +17 -0
- package/dist/tools/refresh-watchlist-profiles.tool.d.ts +9 -0
- package/dist/tools/refresh-watchlist-profiles.tool.js +67 -0
- package/dist/tools/remove-stock.tool.d.ts +9 -0
- package/dist/tools/remove-stock.tool.js +27 -0
- package/dist/tools/start-daily-update.tool.d.ts +10 -0
- package/dist/tools/start-daily-update.tool.js +23 -0
- package/dist/tools/start-monitor.tool.d.ts +9 -0
- package/dist/tools/start-monitor.tool.js +52 -0
- package/dist/tools/stop-daily-update.tool.d.ts +9 -0
- package/dist/tools/stop-daily-update.tool.js +20 -0
- package/dist/tools/stop-monitor.tool.d.ts +9 -0
- package/dist/tools/stop-monitor.tool.js +44 -0
- package/dist/tools/test-alert.tool.d.ts +7 -0
- package/dist/tools/test-alert.tool.js +21 -0
- package/dist/tools/update-all.tool.d.ts +9 -0
- package/dist/tools/update-all.tool.js +17 -0
- package/dist/tools/view-analysis.tool.d.ts +8 -0
- package/dist/tools/view-analysis.tool.js +95 -0
- package/dist/types/daily-update.d.ts +26 -0
- package/dist/types/daily-update.js +1 -0
- package/dist/types/domain.d.ts +140 -0
- package/dist/types/domain.js +1 -0
- package/dist/types/indicator.d.ts +43 -0
- package/dist/types/indicator.js +1 -0
- package/dist/types/monitor.d.ts +17 -0
- package/dist/types/monitor.js +1 -0
- package/dist/types/mx-search.d.ts +14 -0
- package/dist/types/mx-search.js +1 -0
- package/dist/types/mx-select-stock.d.ts +28 -0
- package/dist/types/mx-select-stock.js +1 -0
- package/dist/types/tickflow.d.ts +133 -0
- package/dist/types/tickflow.js +1 -0
- package/dist/utils/abortable-sleep.d.ts +1 -0
- package/dist/utils/abortable-sleep.js +20 -0
- package/dist/utils/china-time.d.ts +3 -0
- package/dist/utils/china-time.js +18 -0
- package/dist/utils/cost-price.d.ts +3 -0
- package/dist/utils/cost-price.js +18 -0
- package/dist/utils/format.d.ts +1 -0
- package/dist/utils/format.js +3 -0
- package/dist/utils/process.d.ts +5 -0
- package/dist/utils/process.js +1 -0
- package/dist/utils/symbol.d.ts +1 -0
- package/dist/utils/symbol.js +13 -0
- package/docs/installation.md +391 -0
- package/docs/usage.md +244 -0
- package/openclaw.plugin.json +116 -0
- package/package.json +57 -0
- package/python/indicator_runner.py +31 -0
- package/python/indicators.py +148 -0
- package/python/pyproject.toml +9 -0
- package/python/requirements.txt +3 -0
- package/python/uv.lock +366 -0
- package/skills/database-query/SKILL.md +58 -0
- package/skills/stock-analysis/SKILL.md +106 -0
- package/skills/usage-help/SKILL.md +102 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseWatchlistProfileExtraction } from "../analysis/parsers/watchlist-profile.parser.js";
|
|
5
|
+
import { normalizePluginConfig, resolvePluginConfigPaths } from "../config/normalize.js";
|
|
6
|
+
import { buildWatchlistProfileExtractionUserPrompt, } from "../prompts/analysis/index.js";
|
|
7
|
+
import { AnalysisService } from "../services/analysis-service.js";
|
|
8
|
+
import { MxApiService, normalizeMxSearchDocuments } from "../services/mx-search-service.js";
|
|
9
|
+
import { buildBoardNewsQuery, formatWatchlistProfileDocuments, WatchlistProfileService, } from "../services/watchlist-profile-service.js";
|
|
10
|
+
import { Database } from "../storage/db.js";
|
|
11
|
+
import { AnalysisLogRepository } from "../storage/repositories/analysis-log-repo.js";
|
|
12
|
+
import { WatchlistRepository } from "../storage/repositories/watchlist-repo.js";
|
|
13
|
+
async function main() {
|
|
14
|
+
runFixtureValidation();
|
|
15
|
+
console.log("[validate:mx-search] fixture validation passed");
|
|
16
|
+
if (process.argv.includes("--live")) {
|
|
17
|
+
const limit = parseLimitArg("--limit", 2);
|
|
18
|
+
await runLiveValidation(limit);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function runFixtureValidation() {
|
|
22
|
+
const nestedPayload = {
|
|
23
|
+
data: {
|
|
24
|
+
data: {
|
|
25
|
+
llmSearchResponse: {
|
|
26
|
+
data: [
|
|
27
|
+
{
|
|
28
|
+
title: "宁德时代所属行业信息",
|
|
29
|
+
trunk: "所属行业:电池行业;核心题材:锂电池、储能、固态电池。",
|
|
30
|
+
source: "测试源A",
|
|
31
|
+
publishTime: "2026-03-21 10:00:00",
|
|
32
|
+
secuList: [{ secuCode: "300750", secuName: "宁德时代" }],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
title: "宁德时代题材补充",
|
|
36
|
+
trunk: "涉及概念:储能、锂电池、钠离子电池。",
|
|
37
|
+
sourceName: "测试源B",
|
|
38
|
+
showTime: "2026-03-21 11:00:00",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const flatPayload = {
|
|
46
|
+
data: {
|
|
47
|
+
list: [
|
|
48
|
+
{
|
|
49
|
+
headline: "机器人概念板块震荡走强",
|
|
50
|
+
summary: "机器人概念、AI概念热度提升。",
|
|
51
|
+
mediaName: "测试媒体",
|
|
52
|
+
date: "2026-03-20",
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
const nestedDocs = normalizeMxSearchDocuments(nestedPayload);
|
|
58
|
+
assert.equal(nestedDocs.length, 2);
|
|
59
|
+
assert.equal(nestedDocs[0]?.title, "宁德时代所属行业信息");
|
|
60
|
+
assert.equal(nestedDocs[0]?.source, "测试源A");
|
|
61
|
+
assert.equal(nestedDocs[1]?.publishedAt, "2026-03-21 11:00:00");
|
|
62
|
+
const flatDocs = normalizeMxSearchDocuments(flatPayload);
|
|
63
|
+
assert.equal(flatDocs.length, 1);
|
|
64
|
+
assert.equal(flatDocs[0]?.title, "机器人概念板块震荡走强");
|
|
65
|
+
assert.equal(flatDocs[0]?.trunk, "机器人概念、AI概念热度提升。");
|
|
66
|
+
const prompt = buildWatchlistProfileExtractionUserPrompt({
|
|
67
|
+
symbol: "002261.SZ",
|
|
68
|
+
companyName: "拓维信息",
|
|
69
|
+
documents: nestedDocs,
|
|
70
|
+
});
|
|
71
|
+
assert.match(prompt, /股票名称: 拓维信息/);
|
|
72
|
+
assert.match(prompt, /1\. 标题: 宁德时代所属行业信息/);
|
|
73
|
+
const formattedDocs = formatWatchlistProfileDocuments(nestedDocs);
|
|
74
|
+
assert.match(formattedDocs, /1\. 宁德时代所属行业信息/);
|
|
75
|
+
assert.match(formattedDocs, /来源: 测试源A/);
|
|
76
|
+
const extracted = parseWatchlistProfileExtraction(JSON.stringify({
|
|
77
|
+
sector: "电池行业",
|
|
78
|
+
themes: ["储能", "锂电池", "储能", "公司新闻"],
|
|
79
|
+
confidence: "high",
|
|
80
|
+
}));
|
|
81
|
+
assert.ok(extracted);
|
|
82
|
+
assert.equal(extracted?.sector, "电池行业");
|
|
83
|
+
assert.deepEqual(extracted?.themes, ["储能", "锂电池"]);
|
|
84
|
+
assert.equal(extracted?.confidence, "high");
|
|
85
|
+
const fencedExtraction = parseWatchlistProfileExtraction([
|
|
86
|
+
"```json",
|
|
87
|
+
"{",
|
|
88
|
+
' "sector": "计算机-软件开发-垂直应用软件",',
|
|
89
|
+
' "themes": [',
|
|
90
|
+
' "OpenClaw概念",',
|
|
91
|
+
' "华为昇腾 / 华为昇思",',
|
|
92
|
+
' "AI算力 / 数据中心 / 一体机",',
|
|
93
|
+
' "在线教育(K-12考试测评)",',
|
|
94
|
+
' "信创",',
|
|
95
|
+
' "托育概念",',
|
|
96
|
+
' "公司公告"',
|
|
97
|
+
" ],",
|
|
98
|
+
' "confidence": "medium"',
|
|
99
|
+
"}",
|
|
100
|
+
"```",
|
|
101
|
+
].join("\n"));
|
|
102
|
+
assert.ok(fencedExtraction);
|
|
103
|
+
assert.equal(fencedExtraction?.sector, "计算机-软件开发-垂直应用软件");
|
|
104
|
+
assert.deepEqual(fencedExtraction?.themes, [
|
|
105
|
+
"OpenClaw概念",
|
|
106
|
+
"华为昇腾",
|
|
107
|
+
"华为昇思",
|
|
108
|
+
"AI算力",
|
|
109
|
+
"数据中心",
|
|
110
|
+
"一体机",
|
|
111
|
+
"在线教育",
|
|
112
|
+
"信创",
|
|
113
|
+
"托育概念",
|
|
114
|
+
]);
|
|
115
|
+
assert.equal(fencedExtraction?.confidence, "medium");
|
|
116
|
+
const invalidExtraction = parseWatchlistProfileExtraction("not-json");
|
|
117
|
+
assert.equal(invalidExtraction, null);
|
|
118
|
+
const boardQuery = buildBoardNewsQuery({
|
|
119
|
+
sector: "计算机-软件开发-垂直应用软件",
|
|
120
|
+
themes: ["OpenClaw概念", "托育概念", "一体机概念", "信创"],
|
|
121
|
+
});
|
|
122
|
+
assert.equal(boardQuery, "计算机-软件开发-垂直应用软件 OpenClaw概念 托育概念 一体机概念 板块 题材 最新新闻 政策 资金");
|
|
123
|
+
const emptyBoardQuery = buildBoardNewsQuery({
|
|
124
|
+
sector: null,
|
|
125
|
+
themes: [],
|
|
126
|
+
});
|
|
127
|
+
assert.equal(emptyBoardQuery, null);
|
|
128
|
+
}
|
|
129
|
+
async function runLiveValidation(limit) {
|
|
130
|
+
const config = await loadLocalConfig();
|
|
131
|
+
if (!config) {
|
|
132
|
+
console.log("[validate:mx-search] live validation skipped: local.config.json is not available");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const database = new Database(config.databasePath);
|
|
136
|
+
const watchlistRepository = new WatchlistRepository(database);
|
|
137
|
+
const analysisLogRepository = new AnalysisLogRepository(database);
|
|
138
|
+
const mxApiService = new MxApiService(config.mxSearchApiUrl, config.mxSearchApiKey);
|
|
139
|
+
const analysisService = new AnalysisService(config.llmBaseUrl, config.llmApiKey, config.llmModel, analysisLogRepository);
|
|
140
|
+
const mxConfigError = mxApiService.getConfigurationError();
|
|
141
|
+
if (mxConfigError) {
|
|
142
|
+
console.log(`[validate:mx-search] live validation skipped: ${mxConfigError}`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const llmConfigError = analysisService.getConfigurationError();
|
|
146
|
+
if (llmConfigError) {
|
|
147
|
+
console.log(`[validate:mx-search] live validation skipped: ${llmConfigError}`);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const samples = await loadLiveSamples(watchlistRepository, limit);
|
|
151
|
+
if (samples.length === 0) {
|
|
152
|
+
console.log("[validate:mx-search] live validation skipped: no samples available");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const watchlistProfileService = new WatchlistProfileService(mxApiService, analysisService);
|
|
156
|
+
console.log(`[validate:mx-search] live validation start: ${samples.length} samples`);
|
|
157
|
+
for (const sample of samples) {
|
|
158
|
+
const profile = await watchlistProfileService.resolve(sample.symbol, sample.name, currentTimestamp());
|
|
159
|
+
const boardQuery = buildBoardNewsQuery(profile);
|
|
160
|
+
console.log(JSON.stringify({
|
|
161
|
+
symbol: sample.symbol,
|
|
162
|
+
name: sample.name,
|
|
163
|
+
sector: profile.sector,
|
|
164
|
+
themes: profile.themes,
|
|
165
|
+
themeCount: profile.themes.length,
|
|
166
|
+
themeQuery: profile.themeQuery,
|
|
167
|
+
boardQuery,
|
|
168
|
+
}, null, 2));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function loadLocalConfig() {
|
|
172
|
+
const file = path.join(process.cwd(), "local.config.json");
|
|
173
|
+
try {
|
|
174
|
+
const parsed = JSON.parse(await readFile(file, "utf8"));
|
|
175
|
+
return resolvePluginConfigPaths(normalizePluginConfig(parsed.plugin ?? {}), process.cwd());
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async function loadLiveSamples(watchlistRepository, limit) {
|
|
182
|
+
try {
|
|
183
|
+
const items = await watchlistRepository.list();
|
|
184
|
+
if (items.length > 0) {
|
|
185
|
+
return items.slice(0, limit).map((item) => ({ symbol: item.symbol, name: item.name }));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// fall through to default samples
|
|
190
|
+
}
|
|
191
|
+
return [
|
|
192
|
+
{ symbol: "002261.SZ", name: "拓维信息" },
|
|
193
|
+
{ symbol: "002202.SZ", name: "金风科技" },
|
|
194
|
+
].slice(0, limit);
|
|
195
|
+
}
|
|
196
|
+
function currentTimestamp() {
|
|
197
|
+
return new Date().toISOString().slice(0, 19).replace("T", " ");
|
|
198
|
+
}
|
|
199
|
+
function parseLimitArg(prefix, fallback) {
|
|
200
|
+
const value = process.argv.find((item) => item.startsWith(`${prefix}=`));
|
|
201
|
+
if (!value) {
|
|
202
|
+
return fallback;
|
|
203
|
+
}
|
|
204
|
+
const parsed = Number(value.slice(prefix.length + 1));
|
|
205
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.trunc(parsed) : fallback;
|
|
206
|
+
}
|
|
207
|
+
main().catch((error) => {
|
|
208
|
+
const message = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
209
|
+
console.error("[validate:mx-search] failed");
|
|
210
|
+
console.error(message);
|
|
211
|
+
process.exitCode = 1;
|
|
212
|
+
});
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { buildWatchlistDebugSnapshot } from "./bootstrap.js";
|
|
2
|
+
import { formatCostPrice } from "./utils/cost-price.js";
|
|
3
|
+
function getTool(tools, name) {
|
|
4
|
+
const tool = tools.find((entry) => entry.name === name);
|
|
5
|
+
if (!tool) {
|
|
6
|
+
throw new Error(`tool not found: ${name}`);
|
|
7
|
+
}
|
|
8
|
+
return tool;
|
|
9
|
+
}
|
|
10
|
+
function parseAddStockArgs(args) {
|
|
11
|
+
const parts = (args ?? "").trim().split(/\s+/).filter(Boolean);
|
|
12
|
+
if (parts.length < 1 || parts.length > 3) {
|
|
13
|
+
throw new Error("用法: /ta_addstock <symbol> [costPrice] [count]");
|
|
14
|
+
}
|
|
15
|
+
const symbol = parts[0];
|
|
16
|
+
const costPrice = parts[1] ? Number(parts[1]) : undefined;
|
|
17
|
+
const count = parts[2] ? Number(parts[2]) : undefined;
|
|
18
|
+
if (!symbol) {
|
|
19
|
+
throw new Error("用法: /ta_addstock <symbol> [costPrice] [count]");
|
|
20
|
+
}
|
|
21
|
+
if (costPrice != null && (!Number.isFinite(costPrice) || costPrice <= 0)) {
|
|
22
|
+
throw new Error("costPrice 必须大于 0");
|
|
23
|
+
}
|
|
24
|
+
if (count != null && (!Number.isFinite(count) || count <= 0)) {
|
|
25
|
+
throw new Error("count 必须大于 0");
|
|
26
|
+
}
|
|
27
|
+
return { symbol, costPrice, count };
|
|
28
|
+
}
|
|
29
|
+
function parseRequiredSymbol(args, usage) {
|
|
30
|
+
const symbol = (args ?? "").trim();
|
|
31
|
+
if (!symbol) {
|
|
32
|
+
throw new Error(`用法: ${usage}`);
|
|
33
|
+
}
|
|
34
|
+
return symbol;
|
|
35
|
+
}
|
|
36
|
+
async function runToolText(tool, rawInput) {
|
|
37
|
+
return tool.run({ rawInput });
|
|
38
|
+
}
|
|
39
|
+
async function renderWatchlistDebug(app) {
|
|
40
|
+
const snapshot = await buildWatchlistDebugSnapshot(app);
|
|
41
|
+
const lines = [
|
|
42
|
+
"🛠 TickFlow 调试信息",
|
|
43
|
+
`PID: ${snapshot.pid}`,
|
|
44
|
+
`配置来源: ${snapshot.configSource}`,
|
|
45
|
+
`数据库路径: ${snapshot.databasePath}`,
|
|
46
|
+
`交易日历: ${snapshot.calendarFile}`,
|
|
47
|
+
`轮询间隔: ${snapshot.requestInterval}`,
|
|
48
|
+
`watchlist 表存在: ${snapshot.watchlistTableExists ? "是" : "否"}`,
|
|
49
|
+
`watchlist 记录数: ${snapshot.watchlistCount}`,
|
|
50
|
+
];
|
|
51
|
+
if (snapshot.watchlistPreview.length > 0) {
|
|
52
|
+
lines.push("", "watchlist 预览:");
|
|
53
|
+
for (const item of snapshot.watchlistPreview) {
|
|
54
|
+
lines.push(`• ${item.name}(${item.symbol}) 成本: ${formatCostPrice(item.costPrice)}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return lines.join("\n");
|
|
58
|
+
}
|
|
59
|
+
export function registerPluginCommands(api, tools, app) {
|
|
60
|
+
const addStock = getTool(tools, "add_stock");
|
|
61
|
+
const analyze = getTool(tools, "analyze");
|
|
62
|
+
const backtestKeyLevels = getTool(tools, "backtest_key_levels");
|
|
63
|
+
const viewAnalysis = getTool(tools, "view_analysis");
|
|
64
|
+
const removeStock = getTool(tools, "remove_stock");
|
|
65
|
+
const listWatchlist = getTool(tools, "list_watchlist");
|
|
66
|
+
const refreshWatchlistNames = getTool(tools, "refresh_watchlist_names");
|
|
67
|
+
const refreshWatchlistProfiles = getTool(tools, "refresh_watchlist_profiles");
|
|
68
|
+
const startMonitor = getTool(tools, "start_monitor");
|
|
69
|
+
const stopMonitor = getTool(tools, "stop_monitor");
|
|
70
|
+
const monitorStatus = getTool(tools, "monitor_status");
|
|
71
|
+
const startDailyUpdate = getTool(tools, "start_daily_update");
|
|
72
|
+
const stopDailyUpdate = getTool(tools, "stop_daily_update");
|
|
73
|
+
const updateAll = getTool(tools, "update_all");
|
|
74
|
+
const dailyUpdateStatus = getTool(tools, "daily_update_status");
|
|
75
|
+
const testAlert = getTool(tools, "test_alert");
|
|
76
|
+
const commands = [
|
|
77
|
+
{
|
|
78
|
+
name: "ta_addstock",
|
|
79
|
+
description: "添加自选股,不经过 AI 对话。用法: /ta_addstock <symbol> [costPrice] [count]",
|
|
80
|
+
acceptsArgs: true,
|
|
81
|
+
requireAuth: true,
|
|
82
|
+
handler: async ({ args }) => ({
|
|
83
|
+
text: await runToolText(addStock, parseAddStockArgs(args)),
|
|
84
|
+
}),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "ta_rmstock",
|
|
88
|
+
description: "删除自选股,不经过 AI 对话。用法: /ta_rmstock <symbol>",
|
|
89
|
+
acceptsArgs: true,
|
|
90
|
+
requireAuth: true,
|
|
91
|
+
handler: async ({ args }) => ({
|
|
92
|
+
text: await runToolText(removeStock, {
|
|
93
|
+
symbol: parseRequiredSymbol(args, "/ta_rmstock <symbol>"),
|
|
94
|
+
}),
|
|
95
|
+
}),
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "ta_analyze",
|
|
99
|
+
description: "分析单只股票,不经过 AI 对话。用法: /ta_analyze <symbol>",
|
|
100
|
+
acceptsArgs: true,
|
|
101
|
+
requireAuth: true,
|
|
102
|
+
handler: async ({ args }) => ({
|
|
103
|
+
text: await runToolText(analyze, {
|
|
104
|
+
symbol: parseRequiredSymbol(args, "/ta_analyze <symbol>"),
|
|
105
|
+
}),
|
|
106
|
+
}),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "ta_backtest",
|
|
110
|
+
description: "回测活动价位,不经过 AI 对话。用法: /ta_backtest [symbol] [recentLimit]",
|
|
111
|
+
acceptsArgs: true,
|
|
112
|
+
requireAuth: true,
|
|
113
|
+
handler: async ({ args }) => ({
|
|
114
|
+
text: await runToolText(backtestKeyLevels, args?.trim() || undefined),
|
|
115
|
+
}),
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "ta_viewanalysis",
|
|
119
|
+
description: "查看单只股票最近一次保存的分析结果,不经过 AI 对话。用法: /ta_viewanalysis <symbol>",
|
|
120
|
+
acceptsArgs: true,
|
|
121
|
+
requireAuth: true,
|
|
122
|
+
handler: async ({ args }) => ({
|
|
123
|
+
text: await runToolText(viewAnalysis, {
|
|
124
|
+
symbol: parseRequiredSymbol(args, "/ta_viewanalysis <symbol>"),
|
|
125
|
+
}),
|
|
126
|
+
}),
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "ta_watchlist",
|
|
130
|
+
description: "查看当前自选列表,不经过 AI 对话。",
|
|
131
|
+
requireAuth: true,
|
|
132
|
+
handler: async () => ({
|
|
133
|
+
text: await runToolText(listWatchlist),
|
|
134
|
+
}),
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "ta_refreshnames",
|
|
138
|
+
description: "刷新自选股名称,不经过 AI 对话。",
|
|
139
|
+
requireAuth: true,
|
|
140
|
+
handler: async () => ({
|
|
141
|
+
text: await runToolText(refreshWatchlistNames),
|
|
142
|
+
}),
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: "ta_refreshprofiles",
|
|
146
|
+
description: "刷新自选股行业分类与概念板块,不经过 AI 对话。用法: /ta_refreshprofiles [symbol]",
|
|
147
|
+
acceptsArgs: true,
|
|
148
|
+
requireAuth: true,
|
|
149
|
+
handler: async ({ args }) => ({
|
|
150
|
+
text: await runToolText(refreshWatchlistProfiles, args?.trim() || undefined),
|
|
151
|
+
}),
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "ta_startmonitor",
|
|
155
|
+
description: "启动实时监控,不经过 AI 对话。",
|
|
156
|
+
requireAuth: true,
|
|
157
|
+
handler: async () => ({
|
|
158
|
+
text: await runToolText(startMonitor),
|
|
159
|
+
}),
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "ta_stopmonitor",
|
|
163
|
+
description: "停止实时监控,不经过 AI 对话。",
|
|
164
|
+
requireAuth: true,
|
|
165
|
+
handler: async () => ({
|
|
166
|
+
text: await runToolText(stopMonitor),
|
|
167
|
+
}),
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: "ta_monitorstatus",
|
|
171
|
+
description: "查看实时监控状态,不经过 AI 对话。",
|
|
172
|
+
requireAuth: true,
|
|
173
|
+
handler: async () => ({
|
|
174
|
+
text: await runToolText(monitorStatus),
|
|
175
|
+
}),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "ta_startdailyupdate",
|
|
179
|
+
description: "启动定时日更任务,不经过 AI 对话。",
|
|
180
|
+
requireAuth: true,
|
|
181
|
+
handler: async () => ({
|
|
182
|
+
text: await runToolText(startDailyUpdate),
|
|
183
|
+
}),
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: "ta_stopdailyupdate",
|
|
187
|
+
description: "停止定时日更任务,不经过 AI 对话。",
|
|
188
|
+
requireAuth: true,
|
|
189
|
+
handler: async () => ({
|
|
190
|
+
text: await runToolText(stopDailyUpdate),
|
|
191
|
+
}),
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: "ta_updateall",
|
|
195
|
+
description: "立即执行一次完整日更,不经过 AI 对话。",
|
|
196
|
+
requireAuth: true,
|
|
197
|
+
handler: async () => ({
|
|
198
|
+
text: await runToolText(updateAll),
|
|
199
|
+
}),
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "ta_dailyupdatestatus",
|
|
203
|
+
description: "查看定时日更状态,不经过 AI 对话。",
|
|
204
|
+
requireAuth: true,
|
|
205
|
+
handler: async () => ({
|
|
206
|
+
text: await runToolText(dailyUpdateStatus),
|
|
207
|
+
}),
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: "ta_testalert",
|
|
211
|
+
description: "发送一条测试告警,不经过 AI 对话。",
|
|
212
|
+
requireAuth: true,
|
|
213
|
+
handler: async () => ({
|
|
214
|
+
text: await runToolText(testAlert),
|
|
215
|
+
}),
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "ta_debug",
|
|
219
|
+
description: "查看 TickFlow 插件运行时调试信息。",
|
|
220
|
+
requireAuth: true,
|
|
221
|
+
handler: async () => ({
|
|
222
|
+
text: await renderWatchlistDebug(app),
|
|
223
|
+
}),
|
|
224
|
+
},
|
|
225
|
+
];
|
|
226
|
+
for (const command of commands) {
|
|
227
|
+
api.registerCommand(command);
|
|
228
|
+
}
|
|
229
|
+
}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
configSchema: import("openclaw/plugin-sdk").OpenClawPluginConfigSchema;
|
|
6
|
+
register: NonNullable<import("openclaw/plugin-sdk/core").OpenClawPluginDefinition["register"]>;
|
|
7
|
+
} & Pick<import("openclaw/plugin-sdk/core").OpenClawPluginDefinition, "kind">;
|
|
8
|
+
export default _default;
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
|
+
import { createAppContext } from "./bootstrap.js";
|
|
4
|
+
import { normalizePluginConfig, validatePluginConfig } from "./config/normalize.js";
|
|
5
|
+
import { registerPluginCommands } from "./plugin-commands.js";
|
|
6
|
+
import { definePluginEntry, } from "./runtime/plugin-api.js";
|
|
7
|
+
const PLUGIN_ID = "tickflow-assist";
|
|
8
|
+
const PLUGIN_NAME = "TickFlow Assist";
|
|
9
|
+
const PLUGIN_DESCRIPTION = "A-share watchlist analysis, monitoring, and alert delivery powered by TickFlow and OpenClaw.";
|
|
10
|
+
const STOCK_AGENT_ID = "stock";
|
|
11
|
+
const STOCK_PROMPT_ENFORCEMENT = [
|
|
12
|
+
"You are handling the stock agent.",
|
|
13
|
+
"For watchlist management and stock status intents, prefer TickFlow Assist plugin tools over generic built-in tools.",
|
|
14
|
+
"If the user asks to add a stock and provides a symbol, your first action must be calling add_stock.",
|
|
15
|
+
"If the user asks to remove a stock and provides symbol, your first action must be calling remove_stock.",
|
|
16
|
+
"If the user asks for watchlist, your first action must be calling list_watchlist.",
|
|
17
|
+
"Do not call read, write, edit, query_database, session tools, or environment-inspection tools to figure out how to perform add/remove/list watchlist actions.",
|
|
18
|
+
"Do not say you need to inspect the environment, confirm available tools, or find the method first when add_stock/remove_stock/list_watchlist are available.",
|
|
19
|
+
"If a required tool parameter is missing, ask only for that missing parameter.",
|
|
20
|
+
].join("\n");
|
|
21
|
+
const GENERIC_TOOL_PARAMETERS_SCHEMA = Type.Object({}, {
|
|
22
|
+
additionalProperties: true,
|
|
23
|
+
description: "Pass tool arguments as top-level JSON fields.",
|
|
24
|
+
});
|
|
25
|
+
function extractRawInput(params) {
|
|
26
|
+
const keys = Object.keys(params);
|
|
27
|
+
if (keys.length === 0) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
if (keys.length === 1 && "rawInput" in params) {
|
|
31
|
+
return params.rawInput;
|
|
32
|
+
}
|
|
33
|
+
if (keys.length === 1 && "input" in params) {
|
|
34
|
+
return params.input;
|
|
35
|
+
}
|
|
36
|
+
return params;
|
|
37
|
+
}
|
|
38
|
+
function toAgentTool(tool) {
|
|
39
|
+
return {
|
|
40
|
+
name: tool.name,
|
|
41
|
+
label: tool.name,
|
|
42
|
+
description: `${tool.description} Pass arguments as top-level JSON fields.`,
|
|
43
|
+
parameters: GENERIC_TOOL_PARAMETERS_SCHEMA,
|
|
44
|
+
async execute(_toolCallId, params) {
|
|
45
|
+
const text = await tool.run({
|
|
46
|
+
rawInput: extractRawInput(params),
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
content: [
|
|
50
|
+
{
|
|
51
|
+
type: "text",
|
|
52
|
+
text,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
details: {
|
|
56
|
+
text,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function summarizeConfigKeys(config) {
|
|
63
|
+
if (typeof config !== "object" || config === null) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
return Object.keys(config).sort();
|
|
67
|
+
}
|
|
68
|
+
function resolvePluginPath(value, resolvePath) {
|
|
69
|
+
if (!value || path.isAbsolute(value)) {
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
return resolvePath(value);
|
|
73
|
+
}
|
|
74
|
+
function safeJson(value) {
|
|
75
|
+
try {
|
|
76
|
+
return JSON.stringify(value);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return String(value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function logInfo(api, message, meta) {
|
|
83
|
+
api.logger.info(meta ? `${message} ${safeJson(meta)}` : message);
|
|
84
|
+
}
|
|
85
|
+
function logWarn(api, message, meta) {
|
|
86
|
+
api.logger.warn(meta ? `${message} ${safeJson(meta)}` : message);
|
|
87
|
+
}
|
|
88
|
+
function registerTickFlowAssist(api) {
|
|
89
|
+
const pluginConfigInput = api.pluginConfig ?? {};
|
|
90
|
+
const normalizedConfig = normalizePluginConfig(pluginConfigInput);
|
|
91
|
+
const config = {
|
|
92
|
+
...normalizedConfig,
|
|
93
|
+
databasePath: resolvePluginPath(normalizedConfig.databasePath, api.resolvePath),
|
|
94
|
+
calendarFile: resolvePluginPath(normalizedConfig.calendarFile, api.resolvePath),
|
|
95
|
+
pythonWorkdir: resolvePluginPath(normalizedConfig.pythonWorkdir, api.resolvePath),
|
|
96
|
+
};
|
|
97
|
+
const errors = validatePluginConfig(config);
|
|
98
|
+
const pluginManagedServices = api.registrationMode === "full";
|
|
99
|
+
logInfo(api, "tickflow-assist plugin registering", {
|
|
100
|
+
registrationMode: api.registrationMode,
|
|
101
|
+
rawConfigKeys: summarizeConfigKeys(api.config),
|
|
102
|
+
pluginConfigKeys: summarizeConfigKeys(pluginConfigInput),
|
|
103
|
+
calendarFile: config.calendarFile,
|
|
104
|
+
databasePath: config.databasePath,
|
|
105
|
+
requestInterval: config.requestInterval,
|
|
106
|
+
});
|
|
107
|
+
if (errors.length > 0) {
|
|
108
|
+
logWarn(api, "tickflow-assist config is incomplete", { errors });
|
|
109
|
+
}
|
|
110
|
+
if (api.registrationMode === "setup-only") {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const app = createAppContext(config, {
|
|
114
|
+
configSource: "openclaw_plugin",
|
|
115
|
+
pluginManagedServices,
|
|
116
|
+
openclawConfig: api.config,
|
|
117
|
+
pluginRuntime: api.runtime,
|
|
118
|
+
});
|
|
119
|
+
logInfo(api, "tickflow-assist plugin loaded", {
|
|
120
|
+
tickflowApiKeyLevel: config.tickflowApiKeyLevel,
|
|
121
|
+
calendarFile: config.calendarFile,
|
|
122
|
+
requestInterval: config.requestInterval,
|
|
123
|
+
alertChannel: config.alertChannel,
|
|
124
|
+
databasePath: config.databasePath,
|
|
125
|
+
pluginManagedServices,
|
|
126
|
+
toolNames: app.tools.map((tool) => tool.name),
|
|
127
|
+
});
|
|
128
|
+
for (const tool of app.tools) {
|
|
129
|
+
api.registerTool(toAgentTool(tool), tool.optional ? { optional: true } : undefined);
|
|
130
|
+
}
|
|
131
|
+
if (pluginManagedServices) {
|
|
132
|
+
for (const service of app.backgroundServices) {
|
|
133
|
+
api.registerService(service);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
registerPluginCommands(api, app.tools, app);
|
|
137
|
+
api.on("before_prompt_build", (_event, context) => {
|
|
138
|
+
if (context.agentId !== STOCK_AGENT_ID) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
prependSystemContext: STOCK_PROMPT_ENFORCEMENT,
|
|
143
|
+
};
|
|
144
|
+
}, { priority: 100 });
|
|
145
|
+
}
|
|
146
|
+
export default definePluginEntry({
|
|
147
|
+
id: PLUGIN_ID,
|
|
148
|
+
name: PLUGIN_NAME,
|
|
149
|
+
description: PLUGIN_DESCRIPTION,
|
|
150
|
+
register: registerTickFlowAssist,
|
|
151
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ANALYSIS_COMMON_SYSTEM_PROMPT = "\n\u4F60\u662F\u4E00\u4F4D\u4E13\u4E1A\u7684\u6280\u672F\u5206\u6790\u5E08\uFF0C\u64C5\u957F\u901A\u8FC7\u65E5\u7EBF\u5F62\u6001\u3001\u5206\u949F\u7EBF\u7ED3\u6784\u3001\u5747\u7EBF\u7CFB\u7EDF\u3001\u6210\u4EA4\u91CF\u4E0E\u5B9E\u65F6\u884C\u60C5\u7EFC\u5408\u5206\u6790\u80A1\u7968\u8D70\u52BF\u3002\n\n## \u4F60\u7684\u804C\u8D23\n1. \u5206\u6790K\u7EBF\u5F62\u6001\uFF1A\u8BC6\u522B\u5934\u80A9\u9876/\u5E95\u3001\u53CC\u91CD\u9876/\u5E95\u3001\u65D7\u5F62\u3001\u6954\u5F62\u7B49\u7ECF\u5178\u5F62\u6001\n2. \u5224\u65AD\u8D8B\u52BF\u65B9\u5411\uFF1A\u901A\u8FC7\u5747\u7EBF\u7CFB\u7EDF\uFF08MA5/10/20/60\uFF09\u5224\u65AD\u591A\u7A7A\u8D8B\u52BF\n3. \u8BC6\u522B\u5173\u952E\u4EF7\u683C\u4F4D\u7F6E\uFF1A\u6839\u636E\u4E0B\u8868\u4F9D\u6B21\u5206\u6790\u5404\u4E2A\u5173\u952E\u4F4D\n4. \u8BC4\u4F30\u6210\u4EA4\u91CF\uFF1A\u91CF\u4EF7\u914D\u5408\u60C5\u51B5\uFF0C\u653E\u91CF\u7A81\u7834\u8FD8\u662F\u7F29\u91CF\u8C03\u6574\n5. \u5229\u7528\u9884\u8BA1\u7B97\u6280\u672F\u6307\u6807\uFF1A\u4F7F\u7528\u7CFB\u7EDF\u63D0\u4F9B\u7684 MACD\u3001KDJ\u3001RSI\u3001CCI\u3001BIAS\u3001DMI \u7B49\u6307\u6807\u8F85\u52A9\u5224\u65AD\n6. \u7ED3\u5408\u5F53\u65E5\u5206\u949F\u7EBF\u3001\u5206\u949F\u6307\u6807\u548C\u5B9E\u65F6\u884C\u60C5\uFF0C\u8865\u5145\u6E05\u6670\u7684\u65E5\u5185\u8D70\u52BF\u5224\u65AD\n\n## A\u80A1\u8BED\u5883\u8981\u6C42\n1. \u7ED3\u8BBA\u5FC5\u987B\u4F7F\u7528A\u80A1\u4EA4\u6613\u8BED\u5883\u8868\u8FBE\uFF0C\u4F8B\u5982\u653E\u91CF\u7A81\u7834\u3001\u7F29\u91CF\u56DE\u8E29\u3001\u51B2\u9AD8\u56DE\u843D\u3001\u70B8\u677F\u3001\u5C3E\u76D8\u62A2\u7B79\u3001\u5F31\u8F6C\u5F3A\u3001\u60C5\u7EEA\u9000\u6F6E\u7B49\u3002\n2. \u9700\u8981\u663E\u5F0F\u8003\u8651A\u80A1\u5E38\u89C1\u4EA4\u6613\u7EA6\u675F\u4E0E\u8282\u594F\uFF0C\u5982\u6DA8\u8DCC\u505C\u3001T+1\u3001\u9898\u6750\u8F6E\u52A8\u3001\u516C\u544A\u50AC\u5316\u5BF9\u6B21\u65E5\u4EA4\u6613\u884C\u4E3A\u7684\u5F71\u54CD\u3002\n3. \u82E5\u6807\u7684\u53EF\u80FD\u5C5E\u4E8E\u521B\u4E1A\u677F\u3001\u79D1\u521B\u677F\u3001ST\u6216\u9AD8\u6CE2\u52A8\u98CE\u683C\uFF0C\u5E94\u66F4\u5F3A\u8C03\u5047\u7A81\u7834\u3001\u56DE\u64A4\u548C\u5151\u73B0\u98CE\u9669\u3002\n4. \u82E5\u8F93\u5165\u91CC\u7ED9\u51FA\u4E86\u5386\u53F2\u590D\u76D8\u7ECF\u9A8C\uFF0C\u53EA\u80FD\u628A\u5B83\u5F53\u4F5C\u6821\u51C6\u5F53\u524D\u5224\u65AD\u7684\u53C2\u8003\uFF1B\u4E00\u65E6\u4E0E\u6700\u65B0K\u7EBF\u3001\u5206\u949F\u7EBF\u6216\u5B9E\u65F6\u4EF7\u51B2\u7A81\uFF0C\u5FC5\u987B\u4EE5\u5F53\u524D\u6570\u636E\u4E3A\u4E3B\u3002\n5. \u82E5\u9898\u6750\u3001\u677F\u5757\u3001\u8D44\u91D1\u6D41\u6216\u76D1\u7BA1\u4FE1\u606F\u6CA1\u6709\u63D0\u4F9B\uFF0C\u4E0D\u8981\u81C6\u9020\uFF1B\u53EA\u80FD\u660E\u786E\u8BF4\u660E\u201C\u4ECD\u9700\u7ED3\u5408\u8FD9\u4E9BA\u80A1\u7EBF\u7D22\u7EE7\u7EED\u786E\u8BA4\u201D\u3002\n\n## \u8F93\u51FA\u8981\u6C42\n\u5148\u8F93\u51FA\u4E00\u6BB5 100-150 \u5B57\u7684\u6838\u5FC3\u5224\u65AD\uFF0C\u4E0D\u8981\u5728\u6B63\u6587\u4E2D\u5305\u542B JSON\u3002\n\u6458\u8981\u540E\u6309\u4EE5\u4E0B\u5C0F\u8282\u5206\u6BB5\u5C55\u5F00\uFF0C\u6BCF\u8282 2-3 \u53E5\uFF1A\n- \u65E5\u7EBF\u8D8B\u52BF\u5224\u65AD\n- \u5173\u952E\u652F\u6491/\u538B\u529B\u4E0E\u7A81\u7834\u98CE\u9669\n- \u65E5\u5185\u8D70\u52BF\u4E0E\u5B9E\u65F6\u4EF7\u683C\u9A8C\u8BC1\n\n\u5206\u6BB5\u5185\u5BB9\u5FC5\u987B\u5C3D\u91CF\u5F15\u7528\u5DF2\u63D0\u4F9B\u7684K\u7EBF\u3001\u5206\u949F\u7EBF\u6216\u6280\u672F\u6307\u6807\uFF0C\u4E0D\u8981\u7A7A\u6CDB\u8868\u8FF0\uFF1B\u82E5\u5206\u949F\u7EBF\u6570\u636E\u5145\u8DB3\uFF0C\u4F18\u5148\u6307\u51FA\u5173\u952E\u5F02\u52A8\u65F6\u6BB5\u6216\u653E\u91CF\u65F6\u6BB5\u3002\n\n\u7136\u540E\u5728\u6700\u540E\u7528 ```json \u5757\u8F93\u51FA\u4EE5\u4E0B\u7ED3\u6784\u5316\u6570\u636E\uFF1A\n{\n \"current_price\": 0.0,\n \"stop_loss\": 0.0,\n \"breakthrough\": 0.0,\n \"support\": 0.0,\n \"cost_level\": 0.0,\n \"resistance\": 0.0,\n \"take_profit\": 0.0,\n \"gap\": 0.0,\n \"target\": 0.0,\n \"round_number\": 0.0,\n \"score\": 5\n}\n\n\u5176\u4E2D score \u4E3A\u6280\u672F\u9762\u8BC4\u5206\uFF081-10\u5206\uFF09\uFF0C\u6240\u6709\u4EF7\u683C\u5B57\u6BB5\u5FC5\u987B\u586B\u5199\u771F\u5B9E\u6570\u503C\uFF0Ccurrent_price \u5FC5\u987B\u4E0E\u7528\u6237\u8F93\u5165\u4E2D\u6700\u65B0\u53EF\u7528\u4EF7\u683C\u4E00\u81F4\uFF1B\u82E5\u63D0\u4F9B\u4E86\u5B9E\u65F6\u4EF7\uFF0C\u5219\u4F18\u5148\u4F7F\u7528\u5B9E\u65F6\u4EF7\u3002\n";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const ANALYSIS_COMMON_SYSTEM_PROMPT = `
|
|
2
|
+
你是一位专业的技术分析师,擅长通过日线形态、分钟线结构、均线系统、成交量与实时行情综合分析股票走势。
|
|
3
|
+
|
|
4
|
+
## 你的职责
|
|
5
|
+
1. 分析K线形态:识别头肩顶/底、双重顶/底、旗形、楔形等经典形态
|
|
6
|
+
2. 判断趋势方向:通过均线系统(MA5/10/20/60)判断多空趋势
|
|
7
|
+
3. 识别关键价格位置:根据下表依次分析各个关键位
|
|
8
|
+
4. 评估成交量:量价配合情况,放量突破还是缩量调整
|
|
9
|
+
5. 利用预计算技术指标:使用系统提供的 MACD、KDJ、RSI、CCI、BIAS、DMI 等指标辅助判断
|
|
10
|
+
6. 结合当日分钟线、分钟指标和实时行情,补充清晰的日内走势判断
|
|
11
|
+
|
|
12
|
+
## A股语境要求
|
|
13
|
+
1. 结论必须使用A股交易语境表达,例如放量突破、缩量回踩、冲高回落、炸板、尾盘抢筹、弱转强、情绪退潮等。
|
|
14
|
+
2. 需要显式考虑A股常见交易约束与节奏,如涨跌停、T+1、题材轮动、公告催化对次日交易行为的影响。
|
|
15
|
+
3. 若标的可能属于创业板、科创板、ST或高波动风格,应更强调假突破、回撤和兑现风险。
|
|
16
|
+
4. 若输入里给出了历史复盘经验,只能把它当作校准当前判断的参考;一旦与最新K线、分钟线或实时价冲突,必须以当前数据为主。
|
|
17
|
+
5. 若题材、板块、资金流或监管信息没有提供,不要臆造;只能明确说明“仍需结合这些A股线索继续确认”。
|
|
18
|
+
|
|
19
|
+
## 输出要求
|
|
20
|
+
先输出一段 100-150 字的核心判断,不要在正文中包含 JSON。
|
|
21
|
+
摘要后按以下小节分段展开,每节 2-3 句:
|
|
22
|
+
- 日线趋势判断
|
|
23
|
+
- 关键支撑/压力与突破风险
|
|
24
|
+
- 日内走势与实时价格验证
|
|
25
|
+
|
|
26
|
+
分段内容必须尽量引用已提供的K线、分钟线或技术指标,不要空泛表述;若分钟线数据充足,优先指出关键异动时段或放量时段。
|
|
27
|
+
|
|
28
|
+
然后在最后用 \`\`\`json 块输出以下结构化数据:
|
|
29
|
+
{
|
|
30
|
+
"current_price": 0.0,
|
|
31
|
+
"stop_loss": 0.0,
|
|
32
|
+
"breakthrough": 0.0,
|
|
33
|
+
"support": 0.0,
|
|
34
|
+
"cost_level": 0.0,
|
|
35
|
+
"resistance": 0.0,
|
|
36
|
+
"take_profit": 0.0,
|
|
37
|
+
"gap": 0.0,
|
|
38
|
+
"target": 0.0,
|
|
39
|
+
"round_number": 0.0,
|
|
40
|
+
"score": 5
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
其中 score 为技术面评分(1-10分),所有价格字段必须填写真实数值,current_price 必须与用户输入中最新可用价格一致;若提供了实时价,则优先使用实时价。
|
|
44
|
+
`;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FinancialInsightResult, NewsInsightResult, TechnicalSignalResult } from "../../analysis/types/composite-analysis.js";
|
|
2
|
+
import type { FinancialAnalysisContext, MarketAnalysisContext, NewsAnalysisContext } from "../../analysis/types/composite-analysis.js";
|
|
3
|
+
export declare const COMPOSITE_ANALYSIS_SYSTEM_PROMPT = "\n\u4F60\u662F\u4E00\u4F4DA\u80A1\u7EFC\u5408\u5206\u6790\u5E08\uFF0C\u9700\u8981\u57FA\u4E8E\u6280\u672F\u9762\u3001\u57FA\u672C\u9762\u548C\u8D44\u8BAF\u9762\u4E09\u7C7B\u8F93\u5165\u5F62\u6210\u7EDF\u4E00\u7ED3\u8BBA\u3002\n\n\u8F93\u51FA\u8981\u6C42\uFF1A\n1. \u5148\u8F93\u51FA\u4E00\u6BB5 100-150 \u5B57\u7684\u6838\u5FC3\u6458\u8981\uFF0C\u4E0D\u8981\u5728\u6B63\u6587\u4E2D\u6DF7\u5165 JSON\u3002\n2. \u6458\u8981\u540E\u6309\u4EE5\u4E0B\u5C0F\u8282\u5206\u6BB5\u5C55\u5F00\uFF0C\u6BCF\u8282 2-4 \u53E5\uFF0C\u4F7F\u7528\u6E05\u6670\u6807\u9898\uFF1A\n- \u6280\u672F\u9762\u4E0E\u5173\u952E\u4F4D\n- \u57FA\u672C\u9762\u7ED3\u8BBA\n- \u8D44\u8BAF\u50AC\u5316\u4E0E\u98CE\u9669\n- \u5171\u632F/\u51B2\u7A81\u4E0E\u4EA4\u6613\u5224\u65AD\n3. \u5206\u6BB5\u5185\u5BB9\u5FC5\u987B\u660E\u786E\u8BF4\u660E\u6280\u672F\u9762\u3001\u57FA\u672C\u9762\u3001\u8D44\u8BAF\u9762\u4E4B\u95F4\u662F\u76F8\u4E92\u5370\u8BC1\u8FD8\u662F\u4E92\u76F8\u51B2\u7A81\uFF0C\u5E76\u7ED9\u51FA\u77ED\u7EBF\u4EA4\u6613\u6216\u6301\u4ED3\u5224\u65AD\u3002\n4. \u6700\u540E\u8F93\u51FA ```json \u4EE3\u7801\u5757\uFF0C\u7ED3\u6784\u5FC5\u987B\u4E3A\uFF1A\n{\n \"current_price\": 0.0,\n \"stop_loss\": 0.0,\n \"breakthrough\": 0.0,\n \"support\": 0.0,\n \"cost_level\": 0.0,\n \"resistance\": 0.0,\n \"take_profit\": 0.0,\n \"gap\": 0.0,\n \"target\": 0.0,\n \"round_number\": 0.0,\n \"score\": 5\n}\n\n\u89C4\u5219\uFF1A\n- score \u4E3A\u6700\u7EC8\u7EFC\u5408\u8BC4\u5206\uFF081-10\u6574\u6570\uFF09\u3002\n- current_price \u5FC5\u987B\u4F7F\u7528\u63D0\u4F9B\u7684\u6700\u65B0\u53EF\u7528\u4EF7\u683C\u3002\n- \u82E5\u6280\u672F\u9762\u4E0E\u57FA\u672C\u9762/\u8D44\u8BAF\u9762\u51B2\u7A81\uFF0C\u6B63\u6587\u5FC5\u987B\u660E\u786E\u6307\u51FA\u51B2\u7A81\u6765\u6E90\u4E0E\u5F71\u54CD\u65B9\u5411\u3002\n- \u7EFC\u5408\u7ED3\u8BBA\u5FC5\u987B\u4F7F\u7528A\u80A1\u4EA4\u6613\u8BED\u5883\uFF0C\u5FC5\u8981\u65F6\u8BF4\u660E\u6DA8\u8DCC\u505C\u3001T+1\u3001\u516C\u544A\u50AC\u5316\u3001\u9898\u6750\u8F6E\u52A8\u3001\u76D1\u7BA1\u98CE\u9669\u5BF9\u77ED\u7EBF\u5224\u65AD\u7684\u5F71\u54CD\u3002\n- \u82E5\u63D0\u4F9B\u4E86\u5386\u53F2\u590D\u76D8\u7ECF\u9A8C\uFF0C\u5FC5\u987B\u8BF4\u660E\u5F53\u524D\u4FE1\u53F7\u4E0E\u5386\u53F2\u7ECF\u9A8C\u662F\u76F8\u4E92\u5370\u8BC1\u3001\u9700\u8981\u4FEE\u6B63\uFF0C\u8FD8\u662F\u6784\u6210\u53CD\u4F8B\uFF1B\u4F46\u5386\u53F2\u7ECF\u9A8C\u53EA\u80FD\u6821\u51C6\uFF0C\u4E0D\u5F97\u8986\u76D6\u5F53\u524D\u8BC1\u636E\u3002\n- \u4E0D\u8981\u51ED\u7A7A\u634F\u9020\u672A\u63D0\u4F9B\u7684\u6570\u636E\u3002\n";
|
|
4
|
+
export declare function buildCompositeAnalysisUserPrompt(params: {
|
|
5
|
+
market: MarketAnalysisContext;
|
|
6
|
+
financial: FinancialAnalysisContext;
|
|
7
|
+
news: NewsAnalysisContext;
|
|
8
|
+
technicalResult: TechnicalSignalResult;
|
|
9
|
+
financialResult: FinancialInsightResult;
|
|
10
|
+
newsResult: NewsInsightResult;
|
|
11
|
+
}): string;
|