tickflow-assist 0.2.19 → 0.3.2
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 +13 -6
- package/dist/analysis/parsers/flash-alert-decision.parser.d.ts +8 -0
- package/dist/analysis/parsers/flash-alert-decision.parser.js +34 -0
- package/dist/analysis/types/composite-analysis.d.ts +11 -0
- package/dist/background/jin10-flash.worker.d.ts +8 -0
- package/dist/background/jin10-flash.worker.js +24 -0
- package/dist/bootstrap.d.ts +4 -0
- package/dist/bootstrap.js +18 -1
- package/dist/config/normalize.js +16 -6
- package/dist/config/schema.d.ts +6 -1
- package/dist/config/schema.js +4 -0
- package/dist/dev/run-monitor-loop.js +6 -2
- package/dist/dev/tickflow-assist-cli.js +70 -0
- package/dist/plugin-commands.js +7 -0
- package/dist/prompts/analysis/flash-monitor-alert-prompt.d.ts +11 -0
- package/dist/prompts/analysis/flash-monitor-alert-prompt.js +44 -0
- package/dist/prompts/analysis/index.d.ts +1 -0
- package/dist/prompts/analysis/index.js +1 -0
- package/dist/prompts/analysis/post-close-review-user-prompt.js +18 -0
- package/dist/services/alert-service.d.ts +1 -0
- package/dist/services/alert-service.js +21 -3
- package/dist/services/jin10-flash-monitor-service.d.ts +33 -0
- package/dist/services/jin10-flash-monitor-service.js +587 -0
- package/dist/services/jin10-mcp-service.d.ts +29 -0
- package/dist/services/jin10-mcp-service.js +242 -0
- package/dist/services/post-close-review-service.d.ts +6 -1
- package/dist/services/post-close-review-service.js +35 -1
- package/dist/storage/repositories/jin10-flash-delivery-repo.d.ts +11 -0
- package/dist/storage/repositories/jin10-flash-delivery-repo.js +93 -0
- package/dist/storage/repositories/jin10-flash-repo.d.ts +16 -0
- package/dist/storage/repositories/jin10-flash-repo.js +144 -0
- package/dist/storage/schemas.d.ts +2 -0
- package/dist/storage/schemas.js +19 -0
- package/dist/tools/flash-monitor-status.tool.d.ts +6 -0
- package/dist/tools/flash-monitor-status.tool.js +9 -0
- package/dist/types/flash-monitor.d.ts +17 -0
- package/dist/types/flash-monitor.js +1 -0
- package/dist/types/jin10.d.ts +30 -0
- package/dist/types/jin10.js +1 -0
- package/dist/utils/china-time.d.ts +1 -0
- package/dist/utils/china-time.js +5 -0
- package/openclaw.plugin.json +53 -1
- package/package.json +14 -6
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# TickFlow Assist
|
|
2
2
|
|
|
3
|
-
基于 [OpenClaw](https://openclaw.ai) 的 A 股监控与分析插件。它使用 [TickFlow](https://tickflow.org/auth/register?ref=BUJ54JEDGE)
|
|
3
|
+
基于 [OpenClaw](https://openclaw.ai) 的 A 股监控与分析插件。它使用 [TickFlow](https://tickflow.org/auth/register?ref=BUJ54JEDGE) 获取行情与财务数据,并可选接入 [金十数据 MCP](https://mcp.jin10.com/app/) 快讯流,结合 LLM 生成技术面、基本面、资讯面的综合判断,并把结果持久化到本地 LanceDB。
|
|
4
4
|
|
|
5
|
-
最近更新:`v0.2
|
|
5
|
+
最近更新:`v0.3.2` 为金十快讯新增夜间静默配置并将默认值改为“开启静默”,同时把个股关联快讯与市场概览快讯接入收盘复盘上下文。完整发布记录见 <https://github.com/robinspt/tickflow-assist/blob/main/CHANGELOG.md>。
|
|
6
6
|
|
|
7
|
-
当前主线按 OpenClaw `v2026.3.31+` 对齐,并已验证社区安装在 `v2026.4.
|
|
7
|
+
当前主线按 OpenClaw `v2026.3.31+` 对齐,并已验证社区安装在 `v2026.4.5` 上兼容。
|
|
8
8
|
|
|
9
9
|
## 安装
|
|
10
10
|
|
|
@@ -81,23 +81,30 @@ plugins.entries["tickflow-assist"].config
|
|
|
81
81
|
- 核心运行:`tickflowApiKey`、`llmApiKey`、`llmBaseUrl`、`llmModel`
|
|
82
82
|
- 本地数据:`databasePath`、`calendarFile`
|
|
83
83
|
- 告警投递:`alertChannel`、`alertTarget`、`alertAccount`
|
|
84
|
-
- 能力补充:`mxSearchApiKey`
|
|
84
|
+
- 能力补充:`mxSearchApiKey`、`jin10ApiToken`
|
|
85
85
|
|
|
86
|
-
其中,`mxSearchApiKey` 用于 `mx_search`、`mx_select_stock` 以及非 `Expert` 财务链路的 lite 补充;`alertTarget`、`alertAccount` 建议在准备启用 `test_alert
|
|
86
|
+
其中,`mxSearchApiKey` 用于 `mx_search`、`mx_select_stock` 以及非 `Expert` 财务链路的 lite 补充;`jin10ApiToken` 用于 24 小时金十数据快讯监控;`jin10FlashNightAlert` 默认 `false`(开启夜间静默),设为 `true` 可恢复 24 小时快讯告警;`alertTarget`、`alertAccount` 建议在准备启用 `test_alert`、实时监控告警、金十数据快讯告警和定时通知前一并配好,避免配置不完整导致功能缺失。
|
|
87
87
|
|
|
88
88
|
## 功能
|
|
89
89
|
|
|
90
90
|
- 自选股管理、日 K / 分钟 K 抓取与指标计算
|
|
91
91
|
- 技术面、财务面、资讯面的综合分析
|
|
92
92
|
- 实时监控、定时日更、收盘后复盘
|
|
93
|
+
- 金十数据 24 小时快讯监控与自选关联提醒
|
|
93
94
|
- 本地 LanceDB 数据留痕与分析结果查看
|
|
94
95
|
|
|
95
96
|
## 运行说明
|
|
96
97
|
|
|
97
98
|
- 插件会在本地 `databasePath` 下持久化 LanceDB 数据。
|
|
98
|
-
-
|
|
99
|
+
- 后台服务会按配置执行定时日更、实时监控与金十数据快讯监控。
|
|
99
100
|
- Python 子模块仅用于技术指标计算,不承担主业务流程。
|
|
100
101
|
|
|
102
|
+
## 依赖与可选能力
|
|
103
|
+
|
|
104
|
+
- [TickFlow](https://tickflow.org/auth/register?ref=BUJ54JEDGE):提供日线、分钟线、实时行情与财务数据接口。
|
|
105
|
+
- [金十数据 MCP](https://mcp.jin10.com/app/):可选,用于 24 小时快讯流接入、自选关联筛选与事件驱动告警。
|
|
106
|
+
- [东方财富妙想 Skills](https://marketing.dfcfs.com/views/finskillshub/):可选,用于 `mx_search`、`mx_select_stock` 与非 `Expert` 财务链路的 lite 补充。
|
|
107
|
+
|
|
101
108
|
## 仓库
|
|
102
109
|
|
|
103
110
|
- GitHub: <https://github.com/robinspt/tickflow-assist>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { parseJsonBlock } from "./json-block.parser.js";
|
|
2
|
+
export function parseFlashAlertDecision(responseText) {
|
|
3
|
+
const parsed = parseJsonBlock(responseText, {
|
|
4
|
+
requiredKeys: ["alert", "relevant_symbols"],
|
|
5
|
+
});
|
|
6
|
+
return {
|
|
7
|
+
alert: normalizeBoolean(parsed?.alert),
|
|
8
|
+
importance: normalizeImportance(parsed?.importance),
|
|
9
|
+
relevantSymbols: normalizeSymbols(parsed?.relevant_symbols),
|
|
10
|
+
headline: normalizeText(parsed?.headline),
|
|
11
|
+
reason: normalizeText(parsed?.reason),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function normalizeBoolean(value) {
|
|
15
|
+
return value === true;
|
|
16
|
+
}
|
|
17
|
+
function normalizeImportance(value) {
|
|
18
|
+
if (value === "high" || value === "low") {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
return "medium";
|
|
22
|
+
}
|
|
23
|
+
function normalizeSymbols(value) {
|
|
24
|
+
if (!Array.isArray(value)) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
return value
|
|
28
|
+
.map((item) => String(item ?? "").trim())
|
|
29
|
+
.filter(Boolean)
|
|
30
|
+
.slice(0, 10);
|
|
31
|
+
}
|
|
32
|
+
function normalizeText(value) {
|
|
33
|
+
return typeof value === "string" ? value.trim() : "";
|
|
34
|
+
}
|
|
@@ -104,9 +104,20 @@ export interface PriorKeyLevelValidationContext {
|
|
|
104
104
|
summary: string;
|
|
105
105
|
lines: string[];
|
|
106
106
|
}
|
|
107
|
+
export interface FlashNewsItem {
|
|
108
|
+
publishedAt: string;
|
|
109
|
+
content: string;
|
|
110
|
+
headline: string;
|
|
111
|
+
source: "stock_alert" | "market_overview";
|
|
112
|
+
}
|
|
113
|
+
export interface FlashNewsContext {
|
|
114
|
+
stockAlerts: FlashNewsItem[];
|
|
115
|
+
marketOverviewFlashes: FlashNewsItem[];
|
|
116
|
+
}
|
|
107
117
|
export interface PostCloseReviewInput extends CompositeAnalysisInput {
|
|
108
118
|
compositeResult: CompositeAnalysisResult;
|
|
109
119
|
validation: PriorKeyLevelValidationContext;
|
|
120
|
+
flashContext: FlashNewsContext;
|
|
110
121
|
}
|
|
111
122
|
export interface PostCloseReviewResult {
|
|
112
123
|
analysisText: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Jin10FlashMonitorService } from "../services/jin10-flash-monitor-service.js";
|
|
2
|
+
export declare class Jin10FlashWorker {
|
|
3
|
+
private readonly monitorService;
|
|
4
|
+
private readonly intervalMs;
|
|
5
|
+
constructor(monitorService: Jin10FlashMonitorService, intervalMs: number);
|
|
6
|
+
runOnce(): Promise<number>;
|
|
7
|
+
runLoop(signal?: AbortSignal, runtimeHost?: "plugin_service" | "fallback_process"): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { sleepWithAbort } from "../utils/abortable-sleep.js";
|
|
2
|
+
export class Jin10FlashWorker {
|
|
3
|
+
monitorService;
|
|
4
|
+
intervalMs;
|
|
5
|
+
constructor(monitorService, intervalMs) {
|
|
6
|
+
this.monitorService = monitorService;
|
|
7
|
+
this.intervalMs = intervalMs;
|
|
8
|
+
}
|
|
9
|
+
async runOnce() {
|
|
10
|
+
return this.monitorService.runMonitorOnce();
|
|
11
|
+
}
|
|
12
|
+
async runLoop(signal, runtimeHost) {
|
|
13
|
+
while (!signal?.aborted) {
|
|
14
|
+
await this.monitorService.recordHeartbeat(runtimeHost);
|
|
15
|
+
try {
|
|
16
|
+
await this.runOnce();
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
await this.monitorService.recordLoopError(error);
|
|
20
|
+
}
|
|
21
|
+
await sleepWithAbort(this.intervalMs, signal);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/dist/bootstrap.d.ts
CHANGED
|
@@ -2,11 +2,13 @@ import type { PluginConfig } from "./config/schema.js";
|
|
|
2
2
|
import { Database } from "./storage/db.js";
|
|
3
3
|
import { WatchlistService } from "./services/watchlist-service.js";
|
|
4
4
|
import { MonitorService } from "./services/monitor-service.js";
|
|
5
|
+
import { Jin10FlashMonitorService } from "./services/jin10-flash-monitor-service.js";
|
|
5
6
|
import { AlertService } from "./services/alert-service.js";
|
|
6
7
|
import { AlertMediaService } from "./services/alert-media-service.js";
|
|
7
8
|
import type { LocalTool, OpenClawPluginConfig, OpenClawPluginRuntime, RegisteredService } from "./runtime/plugin-api.js";
|
|
8
9
|
import { RealtimeMonitorWorker } from "./background/realtime-monitor.worker.js";
|
|
9
10
|
import { DailyUpdateWorker } from "./background/daily-update.worker.js";
|
|
11
|
+
import { Jin10FlashWorker } from "./background/jin10-flash.worker.js";
|
|
10
12
|
import type { WatchlistItem } from "./types/domain.js";
|
|
11
13
|
export interface AppContext {
|
|
12
14
|
config: PluginConfig;
|
|
@@ -22,7 +24,9 @@ export interface AppContext {
|
|
|
22
24
|
alertService: AlertService;
|
|
23
25
|
alertMediaService: AlertMediaService;
|
|
24
26
|
monitorService: MonitorService;
|
|
27
|
+
jin10FlashMonitorService: Jin10FlashMonitorService;
|
|
25
28
|
realtimeMonitorWorker: RealtimeMonitorWorker;
|
|
29
|
+
jin10FlashWorker: Jin10FlashWorker;
|
|
26
30
|
dailyUpdateWorker: DailyUpdateWorker;
|
|
27
31
|
watchlistService: WatchlistService;
|
|
28
32
|
database: Database;
|
package/dist/bootstrap.js
CHANGED
|
@@ -6,6 +6,7 @@ import { IndicatorService } from "./services/indicator-service.js";
|
|
|
6
6
|
import { FinancialService } from "./services/financial-service.js";
|
|
7
7
|
import { FinancialLiteService } from "./services/financial-lite-service.js";
|
|
8
8
|
import { MxApiService } from "./services/mx-search-service.js";
|
|
9
|
+
import { Jin10McpService } from "./services/jin10-mcp-service.js";
|
|
9
10
|
import { Database } from "./storage/db.js";
|
|
10
11
|
import { WatchlistRepository } from "./storage/repositories/watchlist-repo.js";
|
|
11
12
|
import { KlinesRepository } from "./storage/repositories/klines-repo.js";
|
|
@@ -19,6 +20,8 @@ import { TechnicalAnalysisRepository } from "./storage/repositories/technical-an
|
|
|
19
20
|
import { FinancialAnalysisRepository } from "./storage/repositories/financial-analysis-repo.js";
|
|
20
21
|
import { NewsAnalysisRepository } from "./storage/repositories/news-analysis-repo.js";
|
|
21
22
|
import { CompositeAnalysisRepository } from "./storage/repositories/composite-analysis-repo.js";
|
|
23
|
+
import { Jin10FlashRepository } from "./storage/repositories/jin10-flash-repo.js";
|
|
24
|
+
import { Jin10FlashDeliveryRepository } from "./storage/repositories/jin10-flash-delivery-repo.js";
|
|
22
25
|
import { WatchlistService } from "./services/watchlist-service.js";
|
|
23
26
|
import { WatchlistProfileService } from "./services/watchlist-profile-service.js";
|
|
24
27
|
import { AnalysisService } from "./services/analysis-service.js";
|
|
@@ -26,6 +29,7 @@ import { AnalysisViewService } from "./services/analysis-view-service.js";
|
|
|
26
29
|
import { QuoteService } from "./services/quote-service.js";
|
|
27
30
|
import { TradingCalendarService } from "./services/trading-calendar-service.js";
|
|
28
31
|
import { MonitorService } from "./services/monitor-service.js";
|
|
32
|
+
import { Jin10FlashMonitorService } from "./services/jin10-flash-monitor-service.js";
|
|
29
33
|
import { AlertService } from "./services/alert-service.js";
|
|
30
34
|
import { AlertMediaService } from "./services/alert-media-service.js";
|
|
31
35
|
import { UpdateService } from "./services/update-service.js";
|
|
@@ -47,6 +51,7 @@ import { analyzeTool } from "./tools/analyze.tool.js";
|
|
|
47
51
|
import { fetchKlinesTool } from "./tools/fetch-klines.tool.js";
|
|
48
52
|
import { fetchIntradayKlinesTool } from "./tools/fetch-intraday-klines.tool.js";
|
|
49
53
|
import { fetchFinancialsTool } from "./tools/fetch-financials.tool.js";
|
|
54
|
+
import { flashMonitorStatusTool } from "./tools/flash-monitor-status.tool.js";
|
|
50
55
|
import { mxSearchTool } from "./tools/mx-search.tool.js";
|
|
51
56
|
import { mxSelectStockTool } from "./tools/mx-select-stock.tool.js";
|
|
52
57
|
import { listWatchlistTool } from "./tools/list-watchlist.tool.js";
|
|
@@ -68,6 +73,7 @@ import { createCommandRunner } from "./runtime/command-runner.js";
|
|
|
68
73
|
import { resolvePreferredOpenClawTmpDir } from "./runtime/openclaw-temp-dir.js";
|
|
69
74
|
import { RealtimeMonitorWorker } from "./background/realtime-monitor.worker.js";
|
|
70
75
|
import { DailyUpdateWorker } from "./background/daily-update.worker.js";
|
|
76
|
+
import { Jin10FlashWorker } from "./background/jin10-flash.worker.js";
|
|
71
77
|
export function createAppContext(config, options = {}) {
|
|
72
78
|
const runtime = {
|
|
73
79
|
configSource: options.configSource ?? "local_config",
|
|
@@ -90,11 +96,14 @@ export function createAppContext(config, options = {}) {
|
|
|
90
96
|
const financialAnalysisRepository = new FinancialAnalysisRepository(database);
|
|
91
97
|
const newsAnalysisRepository = new NewsAnalysisRepository(database);
|
|
92
98
|
const compositeAnalysisRepository = new CompositeAnalysisRepository(database);
|
|
99
|
+
const jin10FlashRepository = new Jin10FlashRepository(database);
|
|
100
|
+
const jin10FlashDeliveryRepository = new Jin10FlashDeliveryRepository(database);
|
|
93
101
|
const instrumentService = new InstrumentService(tickflowClient);
|
|
94
102
|
const klineService = new KlineService(tickflowClient);
|
|
95
103
|
const quoteService = new QuoteService(tickflowClient);
|
|
96
104
|
const financialService = new FinancialService(tickflowClient);
|
|
97
105
|
const mxApiService = new MxApiService(config.mxSearchApiUrl, config.mxSearchApiKey);
|
|
106
|
+
const jin10McpService = new Jin10McpService(config.jin10McpUrl, config.jin10ApiToken);
|
|
98
107
|
const financialLiteService = new FinancialLiteService(mxApiService);
|
|
99
108
|
const analysisService = new AnalysisService(config.llmBaseUrl, config.llmApiKey, config.llmModel, analysisLogRepository);
|
|
100
109
|
const watchlistProfileService = new WatchlistProfileService(mxApiService, analysisService);
|
|
@@ -128,9 +137,11 @@ export function createAppContext(config, options = {}) {
|
|
|
128
137
|
const compositeStockAnalysisTask = new CompositeStockAnalysisTask(keyLevelsRepository, analysisLogRepository);
|
|
129
138
|
const compositeAnalysisOrchestrator = new CompositeAnalysisOrchestrator(analysisService, marketAnalysisProvider, financialAnalysisProvider, newsAnalysisProvider, klineTechnicalSignalTask, financialFundamentalTask, financialFundamentalLiteTask, newsCatalystTask, compositeStockAnalysisTask, technicalAnalysisRepository, financialAnalysisRepository, newsAnalysisRepository, compositeAnalysisRepository);
|
|
130
139
|
const monitorService = new MonitorService(config.databasePath, config.requestInterval, config.alertChannel, watchlistService, quoteService, tradingCalendarService, keyLevelsRepository, alertLogRepository, klinesRepository, intradayKlinesRepository, klineService, alertService, alertMediaService);
|
|
140
|
+
const jin10FlashMonitorService = new Jin10FlashMonitorService(config.databasePath, config.jin10FlashPollInterval, config.jin10FlashRetentionDays, config.jin10FlashNightAlert, watchlistService, jin10McpService, analysisService, alertService, jin10FlashRepository, jin10FlashDeliveryRepository);
|
|
131
141
|
const updateService = new UpdateService(klineService, config.tickflowApiKeyLevel, indicatorService, klinesRepository, indicatorsRepository, intradayKlinesRepository, watchlistService, tradingCalendarService);
|
|
132
|
-
const postCloseReviewService = new PostCloseReviewService(watchlistService, compositeAnalysisOrchestrator, analysisService, postCloseReviewTask, keyLevelsRepository, keyLevelsHistoryRepository, klinesRepository, intradayKlinesRepository);
|
|
142
|
+
const postCloseReviewService = new PostCloseReviewService(watchlistService, compositeAnalysisOrchestrator, analysisService, postCloseReviewTask, keyLevelsRepository, keyLevelsHistoryRepository, klinesRepository, intradayKlinesRepository, jin10FlashDeliveryRepository, jin10FlashRepository);
|
|
133
143
|
const realtimeMonitorWorker = new RealtimeMonitorWorker(monitorService, config.requestInterval * 1000);
|
|
144
|
+
const jin10FlashWorker = new Jin10FlashWorker(jin10FlashMonitorService, config.jin10FlashPollInterval * 1000);
|
|
134
145
|
const dailyUpdateWorker = new DailyUpdateWorker(updateService, postCloseReviewService, tradingCalendarService, config.databasePath, alertService, config.dailyUpdateNotify, runtime.configSource);
|
|
135
146
|
let managedLoopAbortController = null;
|
|
136
147
|
let managedLoopPromise = null;
|
|
@@ -143,6 +154,7 @@ export function createAppContext(config, options = {}) {
|
|
|
143
154
|
dailyUpdateStatusTool(dailyUpdateWorker, runtime.configSource),
|
|
144
155
|
fetchIntradayKlinesTool(config.tickflowApiKeyLevel, klineService, intradayKlinesRepository, tradingCalendarService),
|
|
145
156
|
fetchFinancialsTool(financialService),
|
|
157
|
+
flashMonitorStatusTool(jin10FlashMonitorService),
|
|
146
158
|
fetchKlinesTool(klineService, klinesRepository, indicatorService, indicatorsRepository),
|
|
147
159
|
listWatchlistTool(watchlistService),
|
|
148
160
|
monitorStatusTool(monitorService),
|
|
@@ -175,6 +187,9 @@ export function createAppContext(config, options = {}) {
|
|
|
175
187
|
dailyUpdateWorker
|
|
176
188
|
.runLoop(abortController.signal, "plugin_service", runtime.configSource)
|
|
177
189
|
.catch(() => { }),
|
|
190
|
+
jin10FlashWorker
|
|
191
|
+
.runLoop(abortController.signal, "plugin_service")
|
|
192
|
+
.catch(() => { }),
|
|
178
193
|
realtimeMonitorWorker
|
|
179
194
|
.runLoop(abortController.signal, "plugin_service")
|
|
180
195
|
.catch(() => { }),
|
|
@@ -198,7 +213,9 @@ export function createAppContext(config, options = {}) {
|
|
|
198
213
|
alertService,
|
|
199
214
|
alertMediaService,
|
|
200
215
|
monitorService,
|
|
216
|
+
jin10FlashMonitorService,
|
|
201
217
|
realtimeMonitorWorker,
|
|
218
|
+
jin10FlashWorker,
|
|
202
219
|
dailyUpdateWorker,
|
|
203
220
|
watchlistService,
|
|
204
221
|
database,
|
package/dist/config/normalize.js
CHANGED
|
@@ -48,6 +48,11 @@ export function normalizePluginConfig(input) {
|
|
|
48
48
|
tickflowApiKeyLevel: normalizeTickflowApiKeyLevel(raw.tickflowApiKeyLevel, DEFAULT_PLUGIN_CONFIG.tickflowApiKeyLevel),
|
|
49
49
|
mxSearchApiUrl: normalizeString(raw.mxSearchApiUrl, envMxSearchApiUrl || DEFAULT_PLUGIN_CONFIG.mxSearchApiUrl),
|
|
50
50
|
mxSearchApiKey: normalizeString(raw.mxSearchApiKey, envMxSearchApiKey || DEFAULT_PLUGIN_CONFIG.mxSearchApiKey),
|
|
51
|
+
jin10McpUrl: normalizeString(raw.jin10McpUrl, DEFAULT_PLUGIN_CONFIG.jin10McpUrl),
|
|
52
|
+
jin10ApiToken: normalizeString(raw.jin10ApiToken),
|
|
53
|
+
jin10FlashPollInterval: normalizeInteger(raw.jin10FlashPollInterval, DEFAULT_PLUGIN_CONFIG.jin10FlashPollInterval),
|
|
54
|
+
jin10FlashRetentionDays: normalizeInteger(raw.jin10FlashRetentionDays, DEFAULT_PLUGIN_CONFIG.jin10FlashRetentionDays),
|
|
55
|
+
jin10FlashNightAlert: normalizeBoolean(raw.jin10FlashNightAlert, DEFAULT_PLUGIN_CONFIG.jin10FlashNightAlert),
|
|
51
56
|
llmBaseUrl: normalizeString(raw.llmBaseUrl, DEFAULT_PLUGIN_CONFIG.llmBaseUrl),
|
|
52
57
|
llmApiKey: normalizeString(raw.llmApiKey),
|
|
53
58
|
llmModel: normalizeString(raw.llmModel, DEFAULT_PLUGIN_CONFIG.llmModel),
|
|
@@ -74,18 +79,23 @@ export function resolvePluginConfigPaths(config, baseDir) {
|
|
|
74
79
|
}
|
|
75
80
|
export function validatePluginConfig(config) {
|
|
76
81
|
const errors = [];
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
if (!config.llmApiKey) {
|
|
81
|
-
errors.push("llmApiKey is required");
|
|
82
|
-
}
|
|
82
|
+
// Community install scans happen before `configure-openclaw` writes secrets,
|
|
83
|
+
// so registration-time validation should only flag structurally invalid values.
|
|
83
84
|
if (!config.tickflowApiUrl.startsWith("http://") && !config.tickflowApiUrl.startsWith("https://")) {
|
|
84
85
|
errors.push("tickflowApiUrl must be an absolute http(s) URL");
|
|
85
86
|
}
|
|
87
|
+
if (config.jin10McpUrl && !config.jin10McpUrl.startsWith("http://") && !config.jin10McpUrl.startsWith("https://")) {
|
|
88
|
+
errors.push("jin10McpUrl must be an absolute http(s) URL");
|
|
89
|
+
}
|
|
86
90
|
if (config.requestInterval < 5) {
|
|
87
91
|
errors.push("requestInterval must be at least 5 seconds");
|
|
88
92
|
}
|
|
93
|
+
if (config.jin10FlashPollInterval < 10) {
|
|
94
|
+
errors.push("jin10FlashPollInterval must be at least 10 seconds");
|
|
95
|
+
}
|
|
96
|
+
if (config.jin10FlashRetentionDays < 1) {
|
|
97
|
+
errors.push("jin10FlashRetentionDays must be at least 1 day");
|
|
98
|
+
}
|
|
89
99
|
return errors;
|
|
90
100
|
}
|
|
91
101
|
function resolveConfigPath(value, baseDir) {
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -5,6 +5,11 @@ export interface PluginConfig {
|
|
|
5
5
|
tickflowApiKeyLevel: TickflowApiKeyLevel;
|
|
6
6
|
mxSearchApiUrl: string;
|
|
7
7
|
mxSearchApiKey: string;
|
|
8
|
+
jin10McpUrl: string;
|
|
9
|
+
jin10ApiToken: string;
|
|
10
|
+
jin10FlashPollInterval: number;
|
|
11
|
+
jin10FlashRetentionDays: number;
|
|
12
|
+
jin10FlashNightAlert: boolean;
|
|
8
13
|
llmBaseUrl: string;
|
|
9
14
|
llmApiKey: string;
|
|
10
15
|
llmModel: string;
|
|
@@ -20,4 +25,4 @@ export interface PluginConfig {
|
|
|
20
25
|
pythonArgs: string[];
|
|
21
26
|
pythonWorkdir: string;
|
|
22
27
|
}
|
|
23
|
-
export declare const DEFAULT_PLUGIN_CONFIG: Omit<PluginConfig, "tickflowApiKey" | "llmApiKey" | "alertTarget">;
|
|
28
|
+
export declare const DEFAULT_PLUGIN_CONFIG: Omit<PluginConfig, "tickflowApiKey" | "jin10ApiToken" | "llmApiKey" | "alertTarget">;
|
package/dist/config/schema.js
CHANGED
|
@@ -3,6 +3,10 @@ export const DEFAULT_PLUGIN_CONFIG = {
|
|
|
3
3
|
tickflowApiKeyLevel: "free",
|
|
4
4
|
mxSearchApiUrl: "https://mkapi2.dfcfs.com/finskillshub/api/claw",
|
|
5
5
|
mxSearchApiKey: "",
|
|
6
|
+
jin10McpUrl: "https://mcp.jin10.com/mcp",
|
|
7
|
+
jin10FlashPollInterval: 300,
|
|
8
|
+
jin10FlashRetentionDays: 7,
|
|
9
|
+
jin10FlashNightAlert: false,
|
|
6
10
|
llmBaseUrl: "https://api.openai.com/v1",
|
|
7
11
|
llmModel: "gpt-4o",
|
|
8
12
|
databasePath: "./data/lancedb",
|
|
@@ -6,11 +6,12 @@ async function main() {
|
|
|
6
6
|
const config = await loadLocalConfig();
|
|
7
7
|
const app = createAppContext(config, { configSource: "local_config" });
|
|
8
8
|
const worker = app.services.realtimeMonitorWorker;
|
|
9
|
+
const flashWorker = app.services.jin10FlashWorker;
|
|
9
10
|
const alertService = app.services.alertService;
|
|
10
11
|
const monitorService = app.services.monitorService;
|
|
11
12
|
await monitorService.recordHeartbeat("fallback_process");
|
|
12
13
|
await monitorService.setWorkerPid(process.pid);
|
|
13
|
-
process.stdout.write(`TickFlow
|
|
14
|
+
process.stdout.write(`TickFlow realtime loop started, price_interval=${config.requestInterval}s, jin10_interval=${config.jin10FlashPollInterval}s\n`);
|
|
14
15
|
const controller = new AbortController();
|
|
15
16
|
const shutdown = async (signal) => {
|
|
16
17
|
controller.abort();
|
|
@@ -29,7 +30,10 @@ async function main() {
|
|
|
29
30
|
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
30
31
|
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
31
32
|
try {
|
|
32
|
-
await
|
|
33
|
+
await Promise.all([
|
|
34
|
+
worker.runLoop(controller.signal, "fallback_process"),
|
|
35
|
+
flashWorker.runLoop(controller.signal, "fallback_process"),
|
|
36
|
+
]);
|
|
33
37
|
}
|
|
34
38
|
catch (error) {
|
|
35
39
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -13,6 +13,10 @@ const DEFAULTS = {
|
|
|
13
13
|
tickflowApiKeyLevel: "Free",
|
|
14
14
|
mxSearchApiUrl: "https://mkapi2.dfcfs.com/finskillshub/api/claw",
|
|
15
15
|
mxSearchApiKey: "",
|
|
16
|
+
jin10McpUrl: "https://mcp.jin10.com/mcp",
|
|
17
|
+
jin10FlashPollInterval: 300,
|
|
18
|
+
jin10FlashRetentionDays: 7,
|
|
19
|
+
jin10FlashNightAlert: false,
|
|
16
20
|
llmBaseUrl: "https://api.openai.com/v1",
|
|
17
21
|
llmModel: "gpt-4o",
|
|
18
22
|
requestInterval: 30,
|
|
@@ -43,6 +47,11 @@ Options:
|
|
|
43
47
|
--tickflow-api-key <key>
|
|
44
48
|
--tickflow-api-key-level <Free|Start|Pro|Expert>
|
|
45
49
|
--mx-search-api-key <key>
|
|
50
|
+
--jin10-mcp-url <url>
|
|
51
|
+
--jin10-api-token <token>
|
|
52
|
+
--jin10-flash-poll-interval <seconds>
|
|
53
|
+
--jin10-flash-retention-days <days>
|
|
54
|
+
--jin10-flash-night-alert <true|false>
|
|
46
55
|
--llm-base-url <url>
|
|
47
56
|
--llm-api-key <key>
|
|
48
57
|
--llm-model <name>
|
|
@@ -135,6 +144,21 @@ function parseArgs(argv) {
|
|
|
135
144
|
case "--mx-search-api-key":
|
|
136
145
|
options.overrides.mxSearchApiKey = requireValue(token);
|
|
137
146
|
break;
|
|
147
|
+
case "--jin10-mcp-url":
|
|
148
|
+
options.overrides.jin10McpUrl = requireValue(token);
|
|
149
|
+
break;
|
|
150
|
+
case "--jin10-api-token":
|
|
151
|
+
options.overrides.jin10ApiToken = requireValue(token);
|
|
152
|
+
break;
|
|
153
|
+
case "--jin10-flash-poll-interval":
|
|
154
|
+
options.overrides.jin10FlashPollInterval = Number(requireValue(token));
|
|
155
|
+
break;
|
|
156
|
+
case "--jin10-flash-retention-days":
|
|
157
|
+
options.overrides.jin10FlashRetentionDays = Number(requireValue(token));
|
|
158
|
+
break;
|
|
159
|
+
case "--jin10-flash-night-alert":
|
|
160
|
+
options.overrides.jin10FlashNightAlert = requireValue(token).toLowerCase() === "true";
|
|
161
|
+
break;
|
|
138
162
|
case "--llm-base-url":
|
|
139
163
|
options.overrides.llmBaseUrl = requireValue(token);
|
|
140
164
|
break;
|
|
@@ -240,6 +264,8 @@ function getExistingPluginConfig(root) {
|
|
|
240
264
|
? config.pythonArgs.map((value) => String(value))
|
|
241
265
|
: undefined;
|
|
242
266
|
const requestInterval = Number(config.requestInterval ?? DEFAULTS.requestInterval);
|
|
267
|
+
const jin10FlashPollInterval = Number(config.jin10FlashPollInterval ?? DEFAULTS.jin10FlashPollInterval);
|
|
268
|
+
const jin10FlashRetentionDays = Number(config.jin10FlashRetentionDays ?? DEFAULTS.jin10FlashRetentionDays);
|
|
243
269
|
const dailyUpdateNotify = typeof config.dailyUpdateNotify === "boolean"
|
|
244
270
|
? config.dailyUpdateNotify
|
|
245
271
|
: DEFAULTS.dailyUpdateNotify;
|
|
@@ -249,6 +275,17 @@ function getExistingPluginConfig(root) {
|
|
|
249
275
|
tickflowApiKeyLevel: normalizeApiKeyLevel(stringValue(config.tickflowApiKeyLevel, DEFAULTS.tickflowApiKeyLevel)),
|
|
250
276
|
mxSearchApiUrl: stringValue(config.mxSearchApiUrl, DEFAULTS.mxSearchApiUrl),
|
|
251
277
|
mxSearchApiKey: stringValue(config.mxSearchApiKey, DEFAULTS.mxSearchApiKey),
|
|
278
|
+
jin10McpUrl: stringValue(config.jin10McpUrl, DEFAULTS.jin10McpUrl),
|
|
279
|
+
jin10ApiToken: stringValue(config.jin10ApiToken),
|
|
280
|
+
jin10FlashPollInterval: Number.isFinite(jin10FlashPollInterval)
|
|
281
|
+
? Math.max(10, Math.trunc(jin10FlashPollInterval))
|
|
282
|
+
: DEFAULTS.jin10FlashPollInterval,
|
|
283
|
+
jin10FlashRetentionDays: Number.isFinite(jin10FlashRetentionDays)
|
|
284
|
+
? Math.max(1, Math.trunc(jin10FlashRetentionDays))
|
|
285
|
+
: DEFAULTS.jin10FlashRetentionDays,
|
|
286
|
+
jin10FlashNightAlert: typeof config.jin10FlashNightAlert === "boolean"
|
|
287
|
+
? config.jin10FlashNightAlert
|
|
288
|
+
: DEFAULTS.jin10FlashNightAlert,
|
|
252
289
|
llmBaseUrl: stringValue(config.llmBaseUrl, DEFAULTS.llmBaseUrl),
|
|
253
290
|
llmApiKey: stringValue(config.llmApiKey),
|
|
254
291
|
llmModel: stringValue(config.llmModel, DEFAULTS.llmModel),
|
|
@@ -391,6 +428,11 @@ async function promptForConfig(options, existing, pluginDir, configPath) {
|
|
|
391
428
|
tickflowApiKeyLevel: DEFAULTS.tickflowApiKeyLevel,
|
|
392
429
|
mxSearchApiUrl: DEFAULTS.mxSearchApiUrl,
|
|
393
430
|
mxSearchApiKey: DEFAULTS.mxSearchApiKey,
|
|
431
|
+
jin10McpUrl: DEFAULTS.jin10McpUrl,
|
|
432
|
+
jin10ApiToken: "",
|
|
433
|
+
jin10FlashPollInterval: DEFAULTS.jin10FlashPollInterval,
|
|
434
|
+
jin10FlashRetentionDays: DEFAULTS.jin10FlashRetentionDays,
|
|
435
|
+
jin10FlashNightAlert: DEFAULTS.jin10FlashNightAlert,
|
|
394
436
|
llmBaseUrl: DEFAULTS.llmBaseUrl,
|
|
395
437
|
llmApiKey: "",
|
|
396
438
|
llmModel: DEFAULTS.llmModel,
|
|
@@ -433,6 +475,14 @@ async function promptForConfig(options, existing, pluginDir, configPath) {
|
|
|
433
475
|
{ value: "Expert", label: "Expert" },
|
|
434
476
|
], seed.tickflowApiKeyLevel));
|
|
435
477
|
seed.mxSearchApiKey = await promptString(rl, "MX Search API Key (可留空)", seed.mxSearchApiKey, false);
|
|
478
|
+
seed.jin10ApiToken = await promptString(rl, "Jin10 API Token (可留空)", seed.jin10ApiToken, false);
|
|
479
|
+
seed.jin10FlashPollInterval = await promptInteger(rl, "Jin10 快讯轮询间隔(秒)", seed.jin10FlashPollInterval, 10);
|
|
480
|
+
seed.jin10FlashRetentionDays = await promptInteger(rl, "Jin10 快讯保留天数", seed.jin10FlashRetentionDays, 1);
|
|
481
|
+
const nightAlertChoice = await promptSelect(rl, "Jin10 夜间静默", [
|
|
482
|
+
{ value: "true", label: "关闭夜间静默(24小时告警)" },
|
|
483
|
+
{ value: "false", label: "开启夜间静默(22:00~06:00 不告警)" },
|
|
484
|
+
], seed.jin10FlashNightAlert ? "true" : "false");
|
|
485
|
+
seed.jin10FlashNightAlert = nightAlertChoice === "true";
|
|
436
486
|
seed.llmBaseUrl = await promptString(rl, "LLM Base URL", seed.llmBaseUrl, true);
|
|
437
487
|
seed.llmApiKey = await promptString(rl, "LLM API Key", seed.llmApiKey, true);
|
|
438
488
|
seed.llmModel = await promptString(rl, "LLM Model", seed.llmModel, true);
|
|
@@ -549,6 +599,11 @@ function applyPluginConfig(root, config, target) {
|
|
|
549
599
|
tickflowApiKeyLevel: config.tickflowApiKeyLevel,
|
|
550
600
|
mxSearchApiUrl: config.mxSearchApiUrl,
|
|
551
601
|
mxSearchApiKey: config.mxSearchApiKey,
|
|
602
|
+
jin10McpUrl: config.jin10McpUrl,
|
|
603
|
+
jin10ApiToken: config.jin10ApiToken,
|
|
604
|
+
jin10FlashPollInterval: config.jin10FlashPollInterval,
|
|
605
|
+
jin10FlashRetentionDays: config.jin10FlashRetentionDays,
|
|
606
|
+
jin10FlashNightAlert: config.jin10FlashNightAlert,
|
|
552
607
|
llmBaseUrl: config.llmBaseUrl,
|
|
553
608
|
llmApiKey: config.llmApiKey,
|
|
554
609
|
llmModel: config.llmModel,
|
|
@@ -679,11 +734,26 @@ function getManualMacosFontCommands() {
|
|
|
679
734
|
"fc-cache -fv",
|
|
680
735
|
];
|
|
681
736
|
}
|
|
737
|
+
function getManualUvInstallCommands() {
|
|
738
|
+
if (process.platform === "win32") {
|
|
739
|
+
return [
|
|
740
|
+
"powershell -ExecutionPolicy ByPass -c \"irm https://astral.sh/uv/install.ps1 | iex\"",
|
|
741
|
+
];
|
|
742
|
+
}
|
|
743
|
+
return [
|
|
744
|
+
"curl -LsSf https://astral.sh/uv/install.sh | sh",
|
|
745
|
+
];
|
|
746
|
+
}
|
|
682
747
|
function printNextSteps(options, config) {
|
|
683
748
|
console.log("");
|
|
684
749
|
console.log("接下来的命令需要你手动执行。");
|
|
685
750
|
let step = 1;
|
|
686
751
|
if (options.pythonSetup) {
|
|
752
|
+
console.log(`${step}. 如未安装 uv,请先安装 uv`);
|
|
753
|
+
for (const command of getManualUvInstallCommands()) {
|
|
754
|
+
console.log(` ${command}`);
|
|
755
|
+
}
|
|
756
|
+
step += 1;
|
|
687
757
|
console.log(`${step}. 安装 Python 依赖`);
|
|
688
758
|
console.log(` cd ${config.pythonWorkdir}`);
|
|
689
759
|
console.log(" uv sync");
|
package/dist/plugin-commands.js
CHANGED
|
@@ -81,6 +81,7 @@ export function registerPluginCommands(api, tools, app) {
|
|
|
81
81
|
const startMonitor = getTool(tools, "start_monitor");
|
|
82
82
|
const stopMonitor = getTool(tools, "stop_monitor");
|
|
83
83
|
const monitorStatus = getTool(tools, "monitor_status");
|
|
84
|
+
const flashMonitorStatus = getTool(tools, "flash_monitor_status");
|
|
84
85
|
const startDailyUpdate = getTool(tools, "start_daily_update");
|
|
85
86
|
const stopDailyUpdate = getTool(tools, "stop_daily_update");
|
|
86
87
|
const updateAll = getTool(tools, "update_all");
|
|
@@ -165,6 +166,12 @@ export function registerPluginCommands(api, tools, app) {
|
|
|
165
166
|
requireAuth: true,
|
|
166
167
|
handler: async () => runCommandText(() => runToolText(monitorStatus)),
|
|
167
168
|
},
|
|
169
|
+
{
|
|
170
|
+
name: "ta_flashstatus",
|
|
171
|
+
description: "查看 Jin10 快讯监控状态,不经过 AI 对话。",
|
|
172
|
+
requireAuth: true,
|
|
173
|
+
handler: async () => runCommandText(() => runToolText(flashMonitorStatus)),
|
|
174
|
+
},
|
|
168
175
|
{
|
|
169
176
|
name: "ta_startdailyupdate",
|
|
170
177
|
description: "启动定时日更任务,不经过 AI 对话。",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { WatchlistItem } from "../../types/domain.js";
|
|
2
|
+
import type { Jin10FlashRecord } from "../../types/jin10.js";
|
|
3
|
+
export declare const FLASH_MONITOR_ALERT_SYSTEM_PROMPT = "\n\u4F60\u662F\u4E00\u4F4DA\u80A1\u76D8\u4E2D/\u76D8\u540E\u5FEB\u8BAF\u544A\u8B66\u7B5B\u9009\u5668\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u5224\u65AD\u4E00\u6761\u91D1\u5341\u5FEB\u8BAF\uFF0C\u662F\u5426\u503C\u5F97\u5BF9\u5F53\u524D\u5173\u6CE8\u5217\u8868\u53D1\u9001\u4E00\u6B21\u544A\u8B66\u3002\n\n\u5224\u65AD\u89C4\u5219\uFF1A\n1. \u53EA\u6709\u5728\u5FEB\u8BAF\u4E0E\u5173\u6CE8\u80A1\u7968\u672C\u8EAB\uFF0C\u6216\u4E0E\u5176\u884C\u4E1A/\u9898\u6750\u5B58\u5728\u660E\u786E\u3001\u53EF\u6267\u884C\u3001\u77ED\u671F\u53EF\u80FD\u5F71\u54CD\u98CE\u9669\u504F\u597D\u7684\u5173\u8054\u65F6\uFF0C\u624D\u8FD4\u56DE alert=true\u3002\n2. \u76F4\u63A5\u70B9\u540D\u516C\u53F8/\u80A1\u7968\u4EE3\u7801\u7684\u516C\u544A\u3001\u8BA2\u5355\u3001\u4E2D\u6807\u3001\u51CF\u6301\u3001\u589E\u6301\u3001\u4E1A\u7EE9\u9884\u544A\u3001\u91CD\u7EC4\u3001\u76D1\u7BA1\u3001\u505C\u590D\u724C\u3001\u91CD\u5927\u4EA7\u54C1/\u9879\u76EE\u3001\u91CD\u8981\u884C\u4E1A\u653F\u7B56\uFF0C\u4F18\u5148\u7EA7\u6700\u9AD8\u3002\n3. \u7EAF\u6D77\u5916\u5B8F\u89C2\u3001\u5730\u7F18\u3001\u5546\u54C1\u62A5\u4EF7\u3001\u76F4\u64AD\u63A8\u5E7F\u3001\u56FE\u793A\u64AD\u62A5\u3001\u4E0EA\u80A1\u5019\u9009\u6807\u7684\u7F3A\u4E4F\u6E05\u6670\u4F20\u5BFC\u8DEF\u5F84\u7684\u5185\u5BB9\uFF0C\u4E0D\u8981\u89E6\u53D1\u544A\u8B66\u3002\n4. \u884C\u4E1A/\u9898\u6750\u7EA7\u5FEB\u8BAF\u53EA\u6709\u5728\u786E\u5B9E\u4F1A\u5F71\u54CD\u5019\u9009\u677F\u5757\u98CE\u9669\u504F\u597D\u65F6\uFF0C\u624D\u53EF\u89E6\u53D1\uFF1B\u6CDB\u6CDB\u800C\u8C08\u7684\u884C\u4E1A\u65B0\u95FB\u4E0D\u8981\u89E6\u53D1\u3002\n5. \u8F93\u51FA\u5FC5\u987B\u53EA\u6709\u4E00\u4E2A ```json \u4EE3\u7801\u5757\uFF0C\u7ED3\u6784\u5982\u4E0B\uFF1A\n{\n \"alert\": boolean,\n \"importance\": \"high\" | \"medium\" | \"low\",\n \"relevant_symbols\": [\"000001\", \"600519\"],\n \"headline\": \"\u7B80\u77ED\u544A\u8B66\u6807\u9898\",\n \"reason\": \"20-50\u5B57\u4E2D\u6587\u7406\u7531\"\n}\n";
|
|
4
|
+
export declare function buildFlashMonitorAlertUserPrompt(params: {
|
|
5
|
+
flash: Jin10FlashRecord;
|
|
6
|
+
candidates: Array<{
|
|
7
|
+
item: WatchlistItem;
|
|
8
|
+
directKeywords: string[];
|
|
9
|
+
boardKeywords: string[];
|
|
10
|
+
}>;
|
|
11
|
+
}): string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const FLASH_MONITOR_ALERT_SYSTEM_PROMPT = `
|
|
2
|
+
你是一位A股盘中/盘后快讯告警筛选器。你的任务是判断一条金十快讯,是否值得对当前关注列表发送一次告警。
|
|
3
|
+
|
|
4
|
+
判断规则:
|
|
5
|
+
1. 只有在快讯与关注股票本身,或与其行业/题材存在明确、可执行、短期可能影响风险偏好的关联时,才返回 alert=true。
|
|
6
|
+
2. 直接点名公司/股票代码的公告、订单、中标、减持、增持、业绩预告、重组、监管、停复牌、重大产品/项目、重要行业政策,优先级最高。
|
|
7
|
+
3. 纯海外宏观、地缘、商品报价、直播推广、图示播报、与A股候选标的缺乏清晰传导路径的内容,不要触发告警。
|
|
8
|
+
4. 行业/题材级快讯只有在确实会影响候选板块风险偏好时,才可触发;泛泛而谈的行业新闻不要触发。
|
|
9
|
+
5. 输出必须只有一个 \`\`\`json 代码块,结构如下:
|
|
10
|
+
{
|
|
11
|
+
"alert": boolean,
|
|
12
|
+
"importance": "high" | "medium" | "low",
|
|
13
|
+
"relevant_symbols": ["000001", "600519"],
|
|
14
|
+
"headline": "简短告警标题",
|
|
15
|
+
"reason": "20-50字中文理由"
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
export function buildFlashMonitorAlertUserPrompt(params) {
|
|
19
|
+
return [
|
|
20
|
+
"请判断以下金十快讯是否值得触发一次A股自选告警。",
|
|
21
|
+
"",
|
|
22
|
+
"## 快讯",
|
|
23
|
+
`时间: ${params.flash.published_at}`,
|
|
24
|
+
`链接: ${params.flash.url}`,
|
|
25
|
+
`正文: ${params.flash.content}`,
|
|
26
|
+
"",
|
|
27
|
+
"## 一阶段候选命中",
|
|
28
|
+
...params.candidates.map((candidate, index) => formatCandidate(index + 1, candidate.item, candidate.directKeywords, candidate.boardKeywords)),
|
|
29
|
+
"",
|
|
30
|
+
"请特别警惕误报:如果只是宽泛宏观信息、海外事件或商品行情,且没有明确传导到候选股票/行业,就不要发告警。",
|
|
31
|
+
].join("\n");
|
|
32
|
+
}
|
|
33
|
+
function formatCandidate(index, item, directKeywords, boardKeywords) {
|
|
34
|
+
return [
|
|
35
|
+
`${index}. ${item.name}(${item.symbol})`,
|
|
36
|
+
` 直接命中: ${formatKeywords(directKeywords)}`,
|
|
37
|
+
` 行业/题材命中: ${formatKeywords(boardKeywords)}`,
|
|
38
|
+
` 行业: ${item.sector ?? "未知"}`,
|
|
39
|
+
` 题材: ${item.themes.length > 0 ? item.themes.join("、") : "无"}`,
|
|
40
|
+
].join("\n");
|
|
41
|
+
}
|
|
42
|
+
function formatKeywords(items) {
|
|
43
|
+
return items.length > 0 ? items.join("、") : "无";
|
|
44
|
+
}
|
|
@@ -6,3 +6,4 @@ export { NEWS_ANALYSIS_SYSTEM_PROMPT, buildNewsAnalysisUserPrompt, } from "./new
|
|
|
6
6
|
export { COMPOSITE_ANALYSIS_SYSTEM_PROMPT, buildCompositeAnalysisUserPrompt, } from "./composite-analysis-user-prompt.js";
|
|
7
7
|
export { POST_CLOSE_REVIEW_SYSTEM_PROMPT, buildPostCloseReviewUserPrompt, } from "./post-close-review-user-prompt.js";
|
|
8
8
|
export { WATCHLIST_PROFILE_EXTRACTION_SYSTEM_PROMPT, buildWatchlistProfileExtractionUserPrompt, } from "./watchlist-profile-extraction-prompt.js";
|
|
9
|
+
export { FLASH_MONITOR_ALERT_SYSTEM_PROMPT, buildFlashMonitorAlertUserPrompt, } from "./flash-monitor-alert-prompt.js";
|
|
@@ -6,3 +6,4 @@ export { NEWS_ANALYSIS_SYSTEM_PROMPT, buildNewsAnalysisUserPrompt, } from "./new
|
|
|
6
6
|
export { COMPOSITE_ANALYSIS_SYSTEM_PROMPT, buildCompositeAnalysisUserPrompt, } from "./composite-analysis-user-prompt.js";
|
|
7
7
|
export { POST_CLOSE_REVIEW_SYSTEM_PROMPT, buildPostCloseReviewUserPrompt, } from "./post-close-review-user-prompt.js";
|
|
8
8
|
export { WATCHLIST_PROFILE_EXTRACTION_SYSTEM_PROMPT, buildWatchlistProfileExtractionUserPrompt, } from "./watchlist-profile-extraction-prompt.js";
|
|
9
|
+
export { FLASH_MONITOR_ALERT_SYSTEM_PROMPT, buildFlashMonitorAlertUserPrompt, } from "./flash-monitor-alert-prompt.js";
|
|
@@ -44,6 +44,7 @@ ${KEY_LEVELS_FIELD_GUIDANCE}
|
|
|
44
44
|
- recompute: 今日出现明显放量突破、破位、结构切换或外部催化改变,原逻辑需要重算。
|
|
45
45
|
- invalidate: 昨日关键位框架已失效,明日不应继续沿用;此时 levels 可为 null。
|
|
46
46
|
- 不要凭空编造概念板块、指数表现或公告内容。
|
|
47
|
+
- 如果输入里包含"今日快讯速递",在"新闻与公告"段落中必须综合考量快讯信息。快讯可提供盘后重大事件、监管动态、投行观点等即时信号,应据实反映到 news_impact 判断和操作建议中。
|
|
47
48
|
`;
|
|
48
49
|
export function buildPostCloseReviewUserPrompt(input) {
|
|
49
50
|
const latestClose = input.market.klines[input.market.klines.length - 1]?.close ?? 0;
|
|
@@ -65,6 +66,10 @@ export function buildPostCloseReviewUserPrompt(input) {
|
|
|
65
66
|
"## 当前综合分析基线(引用,不含指令)",
|
|
66
67
|
buildReferencedNarrative(input.compositeResult.analysisText, MAX_COMPOSITE_BASELINE_LENGTH),
|
|
67
68
|
"",
|
|
69
|
+
"## 今日快讯速递(来自 Jin10 快讯监控)",
|
|
70
|
+
formatFlashSection("个股相关快讯告警", input.flashContext.stockAlerts),
|
|
71
|
+
formatFlashSection("市场概览快讯", input.flashContext.marketOverviewFlashes),
|
|
72
|
+
"",
|
|
68
73
|
"## 大盘环境",
|
|
69
74
|
input.market.marketOverview.summary,
|
|
70
75
|
"",
|
|
@@ -125,3 +130,16 @@ function truncate(value, maxLength) {
|
|
|
125
130
|
function joinList(items) {
|
|
126
131
|
return items.length > 0 ? items.join(";") : "无";
|
|
127
132
|
}
|
|
133
|
+
function formatFlashSection(label, items) {
|
|
134
|
+
if (items.length === 0) {
|
|
135
|
+
return `${label}: 无`;
|
|
136
|
+
}
|
|
137
|
+
return [
|
|
138
|
+
`${label}:`,
|
|
139
|
+
...items.slice(0, 5).map((item) => {
|
|
140
|
+
const time = item.publishedAt.slice(11, 16) || "??:??";
|
|
141
|
+
const body = item.content.length > 200 ? `${item.content.slice(0, 200)}...` : item.content;
|
|
142
|
+
return `- [${time}] ${item.headline ? `${item.headline}: ` : ""}${body}`;
|
|
143
|
+
}),
|
|
144
|
+
].join("\n");
|
|
145
|
+
}
|