@openfinclaw/findoo-datahub-plugin 2026.3.2 → 2026.3.12
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/DESIGN.md +492 -151
- package/_vendor/claude-skills-finance/SKILL.md +192 -0
- package/_vendor/claude-skills-finance/assets/dcf_analysis_template.md +184 -0
- package/_vendor/claude-skills-finance/assets/expected_output.json +161 -0
- package/_vendor/claude-skills-finance/assets/forecast_report_template.md +177 -0
- package/_vendor/claude-skills-finance/assets/sample_financial_data.json +219 -0
- package/_vendor/claude-skills-finance/assets/variance_report_template.md +122 -0
- package/_vendor/claude-skills-finance/references/financial-ratios-guide.md +396 -0
- package/_vendor/claude-skills-finance/references/forecasting-best-practices.md +294 -0
- package/_vendor/claude-skills-finance/references/valuation-methodology.md +255 -0
- package/_vendor/claude-skills-finance/scripts/budget_variance_analyzer.py +406 -0
- package/_vendor/claude-skills-finance/scripts/dcf_valuation.py +449 -0
- package/_vendor/claude-skills-finance/scripts/forecast_builder.py +494 -0
- package/_vendor/claude-skills-finance/scripts/ratio_calculator.py +432 -0
- package/index.ts +356 -20
- package/openclaw.plugin.json +12 -19
- package/package.json +1 -1
- package/references/cn-market-specifics.md +165 -0
- package/references/crypto-analysis.md +635 -0
- package/references/financial-ratios-cn.md +452 -0
- package/references/hk-market-specifics.md +166 -0
- package/references/macro-cycle-cn.md +409 -0
- package/references/valuation-cn.md +427 -0
- package/skills/README.md +294 -0
- package/skills/a-concept-cycle/skill.md +200 -0
- package/skills/a-convertible-arb/skill.md +294 -0
- package/skills/a-dividend-king/skill.md +187 -0
- package/skills/a-earnings-season/skill.md +221 -0
- package/skills/a-index-timer/skill.md +192 -0
- package/skills/a-ipo-new/skill.md +297 -0
- package/skills/a-northbound-decoder/skill.md +185 -0
- package/skills/a-quant-board/skill.md +286 -0
- package/skills/a-share/skill.md +347 -0
- package/skills/a-share-radar/skill.md +185 -0
- package/skills/cross-asset/skill.md +202 -0
- package/skills/crypto/skill.md +269 -0
- package/skills/crypto-altseason/skill.md +208 -0
- package/skills/crypto-btc-cycle/skill.md +231 -0
- package/skills/crypto-defi-yield/skill.md +181 -0
- package/skills/crypto-funding-arb/skill.md +158 -0
- package/skills/crypto-stablecoin-flow/skill.md +149 -0
- package/skills/data-query/skill.md +124 -30
- package/skills/derivatives/skill.md +188 -35
- package/skills/etf-fund/skill.md +216 -0
- package/skills/factor-screen/skill.md +186 -0
- package/skills/hk-china-internet/skill.md +190 -0
- package/skills/hk-dividend-harvest/skill.md +192 -0
- package/skills/hk-hsi-pulse/skill.md +154 -0
- package/skills/hk-southbound-alpha/skill.md +163 -0
- package/skills/hk-stock/skill.md +295 -0
- package/skills/macro/skill.md +244 -53
- package/skills/risk-monitor/skill.md +171 -0
- package/skills/us-dividend/skill.md +162 -0
- package/skills/us-earnings/skill.md +149 -0
- package/skills/us-equity/skill.md +235 -0
- package/skills/us-etf/skill.md +261 -0
- package/skills/us-sector-rotation/skill.md +223 -0
- package/src/config.ts +4 -5
- package/src/datahub-client.test.ts +4 -7
- package/src/datahub-client.ts +6 -1
- package/src/register-tools.ts +720 -0
- package/src/tool-helpers.ts +89 -0
- package/test/e2e/l3-gateway-bootstrap.live.test.ts +354 -0
- package/test/e2e/l4-skill-tool-chain.live.test.ts +461 -0
- package/test/e2e/l5-browser/data-freshness.live.test.ts +379 -0
- package/test/e2e/l5-browser/market-data-chat.live.test.ts +259 -0
- package/test/e2e/l5-browser/skills-registry.test.ts +282 -0
- package/skills/crypto-defi/skill.md +0 -69
- package/skills/equity/skill.md +0 -64
- package/skills/market-radar/skill.md +0 -47
package/index.ts
CHANGED
|
@@ -20,10 +20,13 @@ function buildParams(params: Record<string, unknown>): Record<string, string> {
|
|
|
20
20
|
if (params.start_date) out.start_date = String(params.start_date);
|
|
21
21
|
if (params.end_date) out.end_date = String(params.end_date);
|
|
22
22
|
if (params.trade_date) out.trade_date = String(params.trade_date);
|
|
23
|
+
if (params.date) out.date = String(params.date);
|
|
23
24
|
if (params.limit) out.limit = String(params.limit);
|
|
24
25
|
if (params.provider) out.provider = String(params.provider);
|
|
25
26
|
if (params.country) out.country = String(params.country);
|
|
26
27
|
if (params.indicator) out.indicator = String(params.indicator);
|
|
28
|
+
if (params.manager) out.manager = String(params.manager);
|
|
29
|
+
if (params.hs_type) out.hs_type = String(params.hs_type);
|
|
27
30
|
return out;
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -37,22 +40,34 @@ const findooDatahubPlugin = {
|
|
|
37
40
|
"172 endpoints covering equity (A/HK/US), crypto, macro, derivatives, index, ETF.",
|
|
38
41
|
kind: "financial" as const,
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
register(api: OpenClawPluginApi) {
|
|
41
44
|
const config = resolveConfig(api);
|
|
42
45
|
|
|
43
46
|
// --- DataHub client ---
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
if (!config.datahubApiKey) {
|
|
48
|
+
api.log?.(
|
|
49
|
+
"error",
|
|
50
|
+
"findoo-datahub-plugin: API key is required. Set DATAHUB_API_KEY env var or configure in Control UI → Plugins → Findoo DataHub.",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const client = config.datahubApiKey
|
|
55
|
+
? new DataHubClient(
|
|
56
|
+
config.datahubApiUrl,
|
|
57
|
+
config.datahubUsername,
|
|
58
|
+
config.datahubApiKey,
|
|
59
|
+
config.requestTimeoutMs,
|
|
60
|
+
)
|
|
61
|
+
: null;
|
|
50
62
|
|
|
51
63
|
// --- Local cache + regime detector ---
|
|
52
64
|
const dbPath = api.resolvePath("state/findoo-ohlcv-cache.sqlite");
|
|
53
65
|
const cache = new OHLCVCache(dbPath);
|
|
54
66
|
const regimeDetector = new RegimeDetector();
|
|
55
67
|
|
|
68
|
+
const NO_KEY_ERROR =
|
|
69
|
+
"DataHub API key not configured. Set DATAHUB_API_KEY env var or configure in Control UI → Plugins → Findoo DataHub.";
|
|
70
|
+
|
|
56
71
|
// --- Data provider service (exposed to other extensions) ---
|
|
57
72
|
const dataProvider = {
|
|
58
73
|
async getOHLCV(params: {
|
|
@@ -62,6 +77,8 @@ const findooDatahubPlugin = {
|
|
|
62
77
|
since?: number;
|
|
63
78
|
limit?: number;
|
|
64
79
|
}) {
|
|
80
|
+
if (!client) throw new Error(NO_KEY_ERROR);
|
|
81
|
+
|
|
65
82
|
// Check cache first
|
|
66
83
|
const range = cache.getRange(params.symbol, params.market, params.timeframe);
|
|
67
84
|
if (range && params.since != null && params.limit != null) {
|
|
@@ -82,6 +99,7 @@ const findooDatahubPlugin = {
|
|
|
82
99
|
},
|
|
83
100
|
|
|
84
101
|
async getTicker(symbol: string, market: MarketType) {
|
|
102
|
+
if (!client) throw new Error(NO_KEY_ERROR);
|
|
85
103
|
return client.getTicker(symbol, market);
|
|
86
104
|
},
|
|
87
105
|
|
|
@@ -118,7 +136,7 @@ const findooDatahubPlugin = {
|
|
|
118
136
|
} as Parameters<typeof api.registerService>[0]);
|
|
119
137
|
|
|
120
138
|
// ============================================================
|
|
121
|
-
// AI Tools (
|
|
139
|
+
// AI Tools (13 total)
|
|
122
140
|
// ============================================================
|
|
123
141
|
|
|
124
142
|
// === Tool 1: fin_stock — Equity data ===
|
|
@@ -127,7 +145,7 @@ const findooDatahubPlugin = {
|
|
|
127
145
|
name: "fin_stock",
|
|
128
146
|
label: "Stock Data (A/HK/US)",
|
|
129
147
|
description:
|
|
130
|
-
"Fetch A-share, HK, or US equity data — quotes, historical prices, income
|
|
148
|
+
"Fetch A-share, HK, or US equity data — quotes, historical prices, financials (income/balance/cash/ratios/metrics + VIP variants), ownership (top10/shareholder_trade/repurchase/pledge), money flow, concept lists, calendar (earnings/ipo), discovery (gainers/losers), HK subset (hk/*), US subset (us/*). Notes: estimates/consensus is yfinance-only (US/HK, rate-limited, NOT for A-share — use fundamental/earnings_forecast instead). fundamental/stock_factor returns pre-computed MACD/KDJ/RSI/BOLL/CCI. market/stock_limit requires symbol param (no batch scan by date).",
|
|
131
149
|
parameters: Type.Object({
|
|
132
150
|
symbol: Type.String({
|
|
133
151
|
description: "Stock code. A-shares: 600519.SH; HK: 00700.HK; US: AAPL",
|
|
@@ -136,17 +154,81 @@ const findooDatahubPlugin = {
|
|
|
136
154
|
type: "string",
|
|
137
155
|
enum: [
|
|
138
156
|
"price/historical",
|
|
157
|
+
"price/quote",
|
|
158
|
+
"profile",
|
|
159
|
+
"search",
|
|
160
|
+
"screener",
|
|
161
|
+
// fundamentals — A-share
|
|
139
162
|
"fundamental/income",
|
|
140
163
|
"fundamental/balance",
|
|
141
164
|
"fundamental/cash",
|
|
142
165
|
"fundamental/ratios",
|
|
143
166
|
"fundamental/metrics",
|
|
144
167
|
"fundamental/dividends",
|
|
168
|
+
"fundamental/adj_factor",
|
|
169
|
+
"fundamental/earnings_forecast",
|
|
170
|
+
"fundamental/financial_audit",
|
|
171
|
+
"fundamental/financial_express",
|
|
172
|
+
"fundamental/forecast_vip",
|
|
173
|
+
"fundamental/revenue_per_segment",
|
|
174
|
+
"fundamental/management",
|
|
175
|
+
// ownership & structure
|
|
145
176
|
"ownership/top10_holders",
|
|
177
|
+
"ownership/top10_float_holders",
|
|
178
|
+
"ownership/major_holders",
|
|
179
|
+
"ownership/shareholder_trade",
|
|
180
|
+
"ownership/repurchase",
|
|
181
|
+
"ownership/share_float",
|
|
182
|
+
"ownership/holder_number",
|
|
183
|
+
"ownership/share_statistics",
|
|
184
|
+
"pledge/stat",
|
|
185
|
+
"pledge/detail",
|
|
186
|
+
// money flow & market
|
|
146
187
|
"moneyflow/individual",
|
|
147
188
|
"market/top_list",
|
|
189
|
+
// discovery & calendar
|
|
148
190
|
"discovery/gainers",
|
|
149
191
|
"discovery/losers",
|
|
192
|
+
"discovery/active",
|
|
193
|
+
"discovery/undervalued_growth",
|
|
194
|
+
"discovery/undervalued_large_caps",
|
|
195
|
+
"discovery/growth_tech",
|
|
196
|
+
"discovery/aggressive_small_caps",
|
|
197
|
+
"discovery/name_change",
|
|
198
|
+
"calendar/earnings",
|
|
199
|
+
"calendar/ipo",
|
|
200
|
+
"compare/peers",
|
|
201
|
+
"shorts/short_volume",
|
|
202
|
+
"concept/concept_detail",
|
|
203
|
+
"concept/concept_list",
|
|
204
|
+
// estimates (yfinance only — US/HK stocks, not A-share; may hit rate limits)
|
|
205
|
+
"estimates/consensus",
|
|
206
|
+
// additional fundamentals (VIP / tushare-only)
|
|
207
|
+
"fundamental/backup_daily",
|
|
208
|
+
"fundamental/balance_vip",
|
|
209
|
+
"fundamental/cashflow_vip",
|
|
210
|
+
"fundamental/dividend_detail",
|
|
211
|
+
"fundamental/historical_splits",
|
|
212
|
+
"fundamental/income_vip",
|
|
213
|
+
"fundamental/revenue_segment_vip",
|
|
214
|
+
"fundamental/stock_factor",
|
|
215
|
+
// HK
|
|
216
|
+
"hk/income",
|
|
217
|
+
"hk/balancesheet",
|
|
218
|
+
"hk/cashflow",
|
|
219
|
+
"hk/fina_indicator",
|
|
220
|
+
"hk/basic",
|
|
221
|
+
"hk/hold",
|
|
222
|
+
"hk/adj_factor",
|
|
223
|
+
"hk/trade_cal",
|
|
224
|
+
// US
|
|
225
|
+
"us/income",
|
|
226
|
+
"us/balancesheet",
|
|
227
|
+
"us/cashflow",
|
|
228
|
+
"us/fina_indicator",
|
|
229
|
+
"us/basic",
|
|
230
|
+
"us/adj_factor",
|
|
231
|
+
"us/trade_cal",
|
|
150
232
|
],
|
|
151
233
|
description: "DataHub equity endpoint path",
|
|
152
234
|
}),
|
|
@@ -159,6 +241,7 @@ const findooDatahubPlugin = {
|
|
|
159
241
|
}),
|
|
160
242
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
161
243
|
try {
|
|
244
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
162
245
|
const endpoint = String(params.endpoint ?? "price/historical");
|
|
163
246
|
const qp = buildParams(params);
|
|
164
247
|
const results = await client.equity(endpoint, qp);
|
|
@@ -182,7 +265,7 @@ const findooDatahubPlugin = {
|
|
|
182
265
|
name: "fin_index",
|
|
183
266
|
label: "Index / ETF / Fund",
|
|
184
267
|
description:
|
|
185
|
-
"Query index constituents, valuations,
|
|
268
|
+
"Query index data — constituents, daily valuations (PE/PB via daily_basic), thematic/concept indices (ths_index/ths_daily/ths_member), classification, global indices. Use constituents (not members) for index composition.",
|
|
186
269
|
parameters: Type.Object({
|
|
187
270
|
symbol: Type.Optional(
|
|
188
271
|
Type.String({ description: "Index/ETF/fund code. Index: 000300.SH; ETF: 510050.SH" }),
|
|
@@ -193,6 +276,10 @@ const findooDatahubPlugin = {
|
|
|
193
276
|
"price/historical",
|
|
194
277
|
"constituents",
|
|
195
278
|
"daily_basic",
|
|
279
|
+
"available",
|
|
280
|
+
"info",
|
|
281
|
+
"classify",
|
|
282
|
+
"global_index",
|
|
196
283
|
"thematic/ths_index",
|
|
197
284
|
"thematic/ths_daily",
|
|
198
285
|
"thematic/ths_member",
|
|
@@ -205,6 +292,7 @@ const findooDatahubPlugin = {
|
|
|
205
292
|
}),
|
|
206
293
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
207
294
|
try {
|
|
295
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
208
296
|
const endpoint = String(params.endpoint ?? "price/historical");
|
|
209
297
|
const qp = buildParams(params);
|
|
210
298
|
const results = await client.index(endpoint, qp);
|
|
@@ -228,7 +316,7 @@ const findooDatahubPlugin = {
|
|
|
228
316
|
name: "fin_macro",
|
|
229
317
|
label: "Macro / Rates / FX",
|
|
230
318
|
description:
|
|
231
|
-
"China macro (GDP/CPI/PPI/PMI/M2), interest rates (Shibor/LPR/Libor/Hibor), treasury yields, FX, WorldBank data.",
|
|
319
|
+
"China macro (GDP/CPI/PPI/PMI/M2/social financing), interest rates (Shibor/LPR/Libor/Hibor), CN/US treasury yields, FX rates (currency/*), WorldBank data (worldbank/*), economic calendar, fixedincome rate aliases (fixedincome/rate/*). Notes: LIBOR terminated 2023 (data stops 2020-06). fixedincome/rate/* are aliases for shibor/libor/hibor. worldbank/country provides country classification for EM vs DM analysis.",
|
|
232
320
|
parameters: Type.Object({
|
|
233
321
|
endpoint: Type.Unsafe<string>({
|
|
234
322
|
type: "string",
|
|
@@ -241,18 +329,29 @@ const findooDatahubPlugin = {
|
|
|
241
329
|
"social_financing",
|
|
242
330
|
"shibor",
|
|
243
331
|
"shibor_lpr",
|
|
332
|
+
"shibor_quote",
|
|
244
333
|
"libor",
|
|
245
334
|
"hibor",
|
|
246
335
|
"treasury_cn",
|
|
247
336
|
"treasury_us",
|
|
248
337
|
"index_global",
|
|
338
|
+
"wz_index",
|
|
249
339
|
"calendar",
|
|
340
|
+
"currency/price/historical",
|
|
341
|
+
"currency/search",
|
|
342
|
+
"currency/snapshots",
|
|
343
|
+
"worldbank/country",
|
|
250
344
|
"worldbank/gdp",
|
|
251
345
|
"worldbank/population",
|
|
252
346
|
"worldbank/inflation",
|
|
253
347
|
"worldbank/indicator",
|
|
348
|
+
// fixedincome (dedicated DataHub paths, same data as shibor/libor/hibor)
|
|
349
|
+
"fixedincome/rate/shibor",
|
|
350
|
+
"fixedincome/rate/shibor_lpr",
|
|
351
|
+
"fixedincome/rate/libor",
|
|
352
|
+
"fixedincome/rate/hibor",
|
|
254
353
|
],
|
|
255
|
-
description: "DataHub economy endpoint path",
|
|
354
|
+
description: "DataHub economy/currency endpoint path",
|
|
256
355
|
}),
|
|
257
356
|
symbol: Type.Optional(Type.String({ description: "Currency pair or indicator code" })),
|
|
258
357
|
country: Type.Optional(Type.String({ description: "Country code for WorldBank" })),
|
|
@@ -262,12 +361,25 @@ const findooDatahubPlugin = {
|
|
|
262
361
|
}),
|
|
263
362
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
264
363
|
try {
|
|
364
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
265
365
|
const endpoint = String(params.endpoint ?? "cpi");
|
|
266
366
|
const qp = buildParams(params);
|
|
267
|
-
|
|
367
|
+
// Route currency/* and fixedincome/* endpoints to their categories
|
|
368
|
+
let results: unknown[];
|
|
369
|
+
let category: string;
|
|
370
|
+
if (endpoint.startsWith("currency/")) {
|
|
371
|
+
results = await client.currency(endpoint.replace("currency/", ""), qp);
|
|
372
|
+
category = "currency";
|
|
373
|
+
} else if (endpoint.startsWith("fixedincome/")) {
|
|
374
|
+
results = await client.query(endpoint, qp);
|
|
375
|
+
category = "fixedincome";
|
|
376
|
+
} else {
|
|
377
|
+
results = await client.economy(endpoint, qp);
|
|
378
|
+
category = "economy";
|
|
379
|
+
}
|
|
268
380
|
return json({
|
|
269
381
|
success: true,
|
|
270
|
-
endpoint:
|
|
382
|
+
endpoint: `${category}/${endpoint.startsWith("currency/") ? endpoint.replace("currency/", "") : endpoint}`,
|
|
271
383
|
count: results.length,
|
|
272
384
|
results,
|
|
273
385
|
});
|
|
@@ -285,7 +397,7 @@ const findooDatahubPlugin = {
|
|
|
285
397
|
name: "fin_derivatives",
|
|
286
398
|
label: "Futures / Options / CB",
|
|
287
399
|
description:
|
|
288
|
-
"Futures (daily, holdings, settlement, warehouse, mapping), options (basic, daily, chains), convertible bonds.",
|
|
400
|
+
"Futures (daily OHLCV, holdings/OI, settlement, warehouse receipts, contract mapping, term structure curve), options (basic info, daily, chains with Greeks/IV), convertible bonds (basic, daily). Use futures/curve for contango/backwardation analysis.",
|
|
289
401
|
parameters: Type.Object({
|
|
290
402
|
symbol: Type.Optional(
|
|
291
403
|
Type.String({ description: "Contract code, e.g. IF2501.CFX, 113xxx.SH (CB)" }),
|
|
@@ -299,6 +411,7 @@ const findooDatahubPlugin = {
|
|
|
299
411
|
"futures/settle",
|
|
300
412
|
"futures/warehouse",
|
|
301
413
|
"futures/mapping",
|
|
414
|
+
"futures/curve",
|
|
302
415
|
"options/basic",
|
|
303
416
|
"options/daily",
|
|
304
417
|
"options/chains",
|
|
@@ -314,6 +427,7 @@ const findooDatahubPlugin = {
|
|
|
314
427
|
}),
|
|
315
428
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
316
429
|
try {
|
|
430
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
317
431
|
const endpoint = String(params.endpoint ?? "futures/historical");
|
|
318
432
|
const qp = buildParams(params);
|
|
319
433
|
const results = await client.derivatives(endpoint, qp);
|
|
@@ -337,7 +451,7 @@ const findooDatahubPlugin = {
|
|
|
337
451
|
name: "fin_crypto",
|
|
338
452
|
label: "Crypto & DeFi",
|
|
339
453
|
description:
|
|
340
|
-
"Crypto market data (
|
|
454
|
+
"Crypto market data (ticker/orderbook/trades/funding_rate) via CEX, DeFi (protocols/TVL/yields/stablecoins/fees/dex_volumes/bridges/chains) via DefiLlama, market metrics (coin/market/info/categories/trending/global_stats) via CoinGecko. price/historical for crypto OHLCV, search for symbol lookup.",
|
|
341
455
|
parameters: Type.Object({
|
|
342
456
|
endpoint: Type.Unsafe<string>({
|
|
343
457
|
type: "string",
|
|
@@ -361,19 +475,43 @@ const findooDatahubPlugin = {
|
|
|
361
475
|
"defi/stablecoins",
|
|
362
476
|
"defi/fees",
|
|
363
477
|
"defi/dex_volumes",
|
|
478
|
+
"defi/bridges",
|
|
364
479
|
"defi/coin_prices",
|
|
480
|
+
"price/historical",
|
|
481
|
+
"search",
|
|
365
482
|
],
|
|
366
483
|
description: "DataHub crypto endpoint path",
|
|
367
484
|
}),
|
|
368
485
|
symbol: Type.Optional(
|
|
369
486
|
Type.String({ description: "Coin ID, trading pair, or protocol slug" }),
|
|
370
487
|
),
|
|
488
|
+
start_date: Type.Optional(
|
|
489
|
+
Type.String({ description: "Start date for coin/historical (YYYY-MM-DD)" }),
|
|
490
|
+
),
|
|
491
|
+
end_date: Type.Optional(
|
|
492
|
+
Type.String({ description: "End date for coin/historical (YYYY-MM-DD)" }),
|
|
493
|
+
),
|
|
371
494
|
limit: Type.Optional(Type.Number()),
|
|
372
495
|
}),
|
|
373
496
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
374
497
|
try {
|
|
498
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
375
499
|
const endpoint = String(params.endpoint ?? "coin/market");
|
|
376
500
|
const qp = buildParams(params);
|
|
501
|
+
// Endpoint-specific param mapping for crypto APIs
|
|
502
|
+
if (qp.symbol) {
|
|
503
|
+
const coinIdEndpoints = ["coin/historical", "coin/info"];
|
|
504
|
+
if (coinIdEndpoints.includes(endpoint)) {
|
|
505
|
+
qp.coin_id = qp.symbol;
|
|
506
|
+
delete qp.symbol;
|
|
507
|
+
} else if (endpoint === "defi/protocol_tvl") {
|
|
508
|
+
qp.protocol = qp.symbol;
|
|
509
|
+
delete qp.symbol;
|
|
510
|
+
} else if (endpoint === "defi/coin_prices") {
|
|
511
|
+
qp.coins = qp.symbol;
|
|
512
|
+
delete qp.symbol;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
377
515
|
const results = await client.crypto(endpoint, qp);
|
|
378
516
|
return json({
|
|
379
517
|
success: true,
|
|
@@ -395,7 +533,7 @@ const findooDatahubPlugin = {
|
|
|
395
533
|
name: "fin_market",
|
|
396
534
|
label: "Market Radar",
|
|
397
535
|
description:
|
|
398
|
-
"
|
|
536
|
+
"A-share market monitoring — dragon-tiger list (top_list/top_inst), limit-up/down stats (limit_list), block trades, sector money flow (moneyflow/industry), margin (summary/detail/trading), Stock Connect northbound (hsgt_flow/hsgt_top10) and southbound (ggt_daily/ggt_monthly), market snapshots, discovery (gainers/losers/active/new_share). Notes: flow/hsgt_top10 requires 'date' param (NOT trade_date). flow/hs_const requires 'hs_type' param (SH or SZ). market/stock_limit requires 'symbol' (no date-only scan). market_snapshots returns full market snapshot (12000+ records, no params needed).",
|
|
399
537
|
parameters: Type.Object({
|
|
400
538
|
endpoint: Type.Unsafe<string>({
|
|
401
539
|
type: "string",
|
|
@@ -410,8 +548,14 @@ const findooDatahubPlugin = {
|
|
|
410
548
|
"moneyflow/block_trade",
|
|
411
549
|
"margin/summary",
|
|
412
550
|
"margin/detail",
|
|
551
|
+
"margin/trading",
|
|
413
552
|
"flow/hsgt_flow",
|
|
414
553
|
"flow/hsgt_top10",
|
|
554
|
+
"flow/ggt_daily",
|
|
555
|
+
"flow/ggt_monthly",
|
|
556
|
+
"flow/hs_const",
|
|
557
|
+
"market/stock_limit",
|
|
558
|
+
"market_snapshots",
|
|
415
559
|
"discovery/gainers",
|
|
416
560
|
"discovery/losers",
|
|
417
561
|
"discovery/active",
|
|
@@ -420,15 +564,39 @@ const findooDatahubPlugin = {
|
|
|
420
564
|
description: "DataHub equity endpoint for market data",
|
|
421
565
|
}),
|
|
422
566
|
trade_date: Type.Optional(Type.String({ description: "Trade date, e.g. 2025-02-28" })),
|
|
567
|
+
date: Type.Optional(
|
|
568
|
+
Type.String({
|
|
569
|
+
description:
|
|
570
|
+
"Date param for hsgt_top10 (uses 'date' not 'trade_date'), e.g. 2025-02-28",
|
|
571
|
+
}),
|
|
572
|
+
),
|
|
423
573
|
symbol: Type.Optional(Type.String({ description: "Symbol for specific queries" })),
|
|
574
|
+
hs_type: Type.Optional(
|
|
575
|
+
Type.String({
|
|
576
|
+
description:
|
|
577
|
+
"Stock Connect type for hs_const: SH (northbound) or SZ (northbound Shenzhen)",
|
|
578
|
+
}),
|
|
579
|
+
),
|
|
424
580
|
start_date: Type.Optional(Type.String()),
|
|
425
581
|
end_date: Type.Optional(Type.String()),
|
|
426
582
|
limit: Type.Optional(Type.Number()),
|
|
427
583
|
}),
|
|
428
584
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
429
585
|
try {
|
|
586
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
430
587
|
const endpoint = String(params.endpoint ?? "market/top_list");
|
|
431
588
|
const qp = buildParams(params);
|
|
589
|
+
// Auto-alias: trade_date→date for endpoints that use 'date' param
|
|
590
|
+
const dateEndpoints = [
|
|
591
|
+
"market/top_list",
|
|
592
|
+
"market/top_inst",
|
|
593
|
+
"market/limit_list",
|
|
594
|
+
"flow/hsgt_top10",
|
|
595
|
+
];
|
|
596
|
+
if (qp.trade_date && !qp.date && dateEndpoints.includes(endpoint)) {
|
|
597
|
+
qp.date = qp.trade_date;
|
|
598
|
+
delete qp.trade_date;
|
|
599
|
+
}
|
|
432
600
|
const results = await client.equity(endpoint, qp);
|
|
433
601
|
return json({
|
|
434
602
|
success: true,
|
|
@@ -450,7 +618,7 @@ const findooDatahubPlugin = {
|
|
|
450
618
|
name: "fin_query",
|
|
451
619
|
label: "Raw DataHub Query",
|
|
452
620
|
description:
|
|
453
|
-
"Direct passthrough to any of
|
|
621
|
+
"Direct passthrough to any of 168 DataHub endpoints by path. Use when other tools don't cover the specific data. Accepts arbitrary query params. Example paths: equity/fundamental/income, crypto/defi/protocols, economy/cpi, fixedincome/rate/shibor.",
|
|
454
622
|
parameters: Type.Object({
|
|
455
623
|
path: Type.String({
|
|
456
624
|
description:
|
|
@@ -464,6 +632,7 @@ const findooDatahubPlugin = {
|
|
|
464
632
|
}),
|
|
465
633
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
466
634
|
try {
|
|
635
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
467
636
|
const path = String(params.path ?? "").trim();
|
|
468
637
|
if (!path) throw new Error("path is required");
|
|
469
638
|
const qp = (params.params ?? {}) as Record<string, string>;
|
|
@@ -575,7 +744,174 @@ const findooDatahubPlugin = {
|
|
|
575
744
|
{ names: ["fin_data_regime"] },
|
|
576
745
|
);
|
|
577
746
|
|
|
578
|
-
// === Tool 10:
|
|
747
|
+
// === Tool 10: fin_ta — Technical Analysis Indicators ===
|
|
748
|
+
api.registerTool(
|
|
749
|
+
{
|
|
750
|
+
name: "fin_ta",
|
|
751
|
+
label: "Technical Analysis",
|
|
752
|
+
description:
|
|
753
|
+
"Calculate technical indicators (SMA, EMA, RSI, MACD, Bollinger Bands) for any symbol. DataHub fetches OHLCV and computes server-side.",
|
|
754
|
+
parameters: Type.Object({
|
|
755
|
+
symbol: Type.String({
|
|
756
|
+
description:
|
|
757
|
+
"Stock/crypto symbol. A: 600519.SH; HK: 00700.HK; US: AAPL; Crypto: BTC-USDT",
|
|
758
|
+
}),
|
|
759
|
+
indicator: Type.Unsafe<string>({
|
|
760
|
+
type: "string",
|
|
761
|
+
enum: ["sma", "ema", "rsi", "macd", "bbands"],
|
|
762
|
+
description: "Technical indicator to calculate",
|
|
763
|
+
}),
|
|
764
|
+
period: Type.Optional(
|
|
765
|
+
Type.Number({
|
|
766
|
+
description: "Indicator period (default: 20 for SMA/EMA/BBANDS, 14 for RSI)",
|
|
767
|
+
}),
|
|
768
|
+
),
|
|
769
|
+
limit: Type.Optional(
|
|
770
|
+
Type.Number({ description: "Number of OHLCV bars to fetch (default: 200)" }),
|
|
771
|
+
),
|
|
772
|
+
fast: Type.Optional(Type.Number({ description: "MACD fast period (default: 12)" })),
|
|
773
|
+
slow: Type.Optional(Type.Number({ description: "MACD slow period (default: 26)" })),
|
|
774
|
+
signal: Type.Optional(Type.Number({ description: "MACD signal period (default: 9)" })),
|
|
775
|
+
std: Type.Optional(
|
|
776
|
+
Type.Number({ description: "Bollinger Bands std dev (default: 2.0)" }),
|
|
777
|
+
),
|
|
778
|
+
provider: Type.Optional(
|
|
779
|
+
Type.String({ description: "Data provider override (auto-detected if omitted)" }),
|
|
780
|
+
),
|
|
781
|
+
}),
|
|
782
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
783
|
+
try {
|
|
784
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
785
|
+
const indicator = String(params.indicator ?? "sma");
|
|
786
|
+
const qp: Record<string, string> = {};
|
|
787
|
+
if (params.symbol) qp.symbol = String(params.symbol);
|
|
788
|
+
if (params.period) qp.period = String(params.period);
|
|
789
|
+
if (params.limit) qp.limit = String(params.limit);
|
|
790
|
+
if (params.fast) qp.fast = String(params.fast);
|
|
791
|
+
if (params.slow) qp.slow = String(params.slow);
|
|
792
|
+
if (params.signal) qp.signal = String(params.signal);
|
|
793
|
+
if (params.std) qp.std = String(params.std);
|
|
794
|
+
if (params.provider) qp.provider = String(params.provider);
|
|
795
|
+
const results = await client.ta(indicator, qp);
|
|
796
|
+
return json({
|
|
797
|
+
success: true,
|
|
798
|
+
endpoint: `ta/${indicator}`,
|
|
799
|
+
count: results.length,
|
|
800
|
+
results,
|
|
801
|
+
});
|
|
802
|
+
} catch (err) {
|
|
803
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
},
|
|
807
|
+
{ names: ["fin_ta"] },
|
|
808
|
+
);
|
|
809
|
+
|
|
810
|
+
// === Tool 11: fin_etf — ETF & Fund Data ===
|
|
811
|
+
api.registerTool(
|
|
812
|
+
{
|
|
813
|
+
name: "fin_etf",
|
|
814
|
+
label: "ETF & Fund",
|
|
815
|
+
description:
|
|
816
|
+
"ETF and fund data — NAV history, fund info (type/size/fees), historical prices, portfolio holdings (top 10, quarterly), manager track record, dividends, share changes (subscription/redemption trends), adjusted NAV (dividend-reinvested), search. Use fund/manager with a known fund code to find manager info; manager param available for search.",
|
|
817
|
+
parameters: Type.Object({
|
|
818
|
+
symbol: Type.Optional(
|
|
819
|
+
Type.String({
|
|
820
|
+
description: "ETF/Fund code. ETF: 510050.SH; Fund: 110011",
|
|
821
|
+
}),
|
|
822
|
+
),
|
|
823
|
+
manager: Type.Optional(
|
|
824
|
+
Type.String({
|
|
825
|
+
description: "Fund manager name for search endpoint (e.g. 张坤)",
|
|
826
|
+
}),
|
|
827
|
+
),
|
|
828
|
+
endpoint: Type.Unsafe<string>({
|
|
829
|
+
type: "string",
|
|
830
|
+
enum: [
|
|
831
|
+
"nav",
|
|
832
|
+
"info",
|
|
833
|
+
"historical",
|
|
834
|
+
"fund/portfolio",
|
|
835
|
+
"fund/manager",
|
|
836
|
+
"fund/dividends",
|
|
837
|
+
"fund/share",
|
|
838
|
+
"fund/adj_nav",
|
|
839
|
+
"search",
|
|
840
|
+
],
|
|
841
|
+
description: "DataHub ETF/fund endpoint path",
|
|
842
|
+
}),
|
|
843
|
+
start_date: Type.Optional(Type.String({ description: "Start date, e.g. 2025-01-01" })),
|
|
844
|
+
end_date: Type.Optional(Type.String({ description: "End date, e.g. 2025-12-31" })),
|
|
845
|
+
limit: Type.Optional(Type.Number({ description: "Max records to return" })),
|
|
846
|
+
}),
|
|
847
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
848
|
+
try {
|
|
849
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
850
|
+
const endpoint = String(params.endpoint ?? "info");
|
|
851
|
+
const qp = buildParams(params);
|
|
852
|
+
const results = await client.etf(endpoint, qp);
|
|
853
|
+
return json({
|
|
854
|
+
success: true,
|
|
855
|
+
endpoint: `etf/${endpoint}`,
|
|
856
|
+
count: results.length,
|
|
857
|
+
results,
|
|
858
|
+
});
|
|
859
|
+
} catch (err) {
|
|
860
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
},
|
|
864
|
+
{ names: ["fin_etf"] },
|
|
865
|
+
);
|
|
866
|
+
|
|
867
|
+
// === Tool 12: fin_currency — FX & News ===
|
|
868
|
+
api.registerTool(
|
|
869
|
+
{
|
|
870
|
+
name: "fin_currency",
|
|
871
|
+
label: "FX & News",
|
|
872
|
+
description:
|
|
873
|
+
"Foreign exchange rates (price/historical, search, snapshots) and company news (news/company). Major pairs: USDCNH, EURUSD, USDJPY. Use search to find available currency pair symbols.",
|
|
874
|
+
parameters: Type.Object({
|
|
875
|
+
endpoint: Type.Unsafe<string>({
|
|
876
|
+
type: "string",
|
|
877
|
+
enum: ["price/historical", "search", "snapshots", "news/company"],
|
|
878
|
+
description: "DataHub currency/news endpoint path",
|
|
879
|
+
}),
|
|
880
|
+
symbol: Type.Optional(
|
|
881
|
+
Type.String({
|
|
882
|
+
description: "Currency pair (USDCNH, EURUSD) or stock symbol for news (AAPL)",
|
|
883
|
+
}),
|
|
884
|
+
),
|
|
885
|
+
start_date: Type.Optional(Type.String({ description: "Start date, e.g. 2025-01-01" })),
|
|
886
|
+
end_date: Type.Optional(Type.String({ description: "End date, e.g. 2025-12-31" })),
|
|
887
|
+
limit: Type.Optional(Type.Number({ description: "Max records to return" })),
|
|
888
|
+
}),
|
|
889
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
890
|
+
try {
|
|
891
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
892
|
+
const endpoint = String(params.endpoint ?? "price/historical");
|
|
893
|
+
const qp = buildParams(params);
|
|
894
|
+
// Route news/* to the generic query path
|
|
895
|
+
const results =
|
|
896
|
+
endpoint === "news/company"
|
|
897
|
+
? await client.query(`news/company`, qp)
|
|
898
|
+
: await client.currency(endpoint, qp);
|
|
899
|
+
const category = endpoint === "news/company" ? "news" : "currency";
|
|
900
|
+
return json({
|
|
901
|
+
success: true,
|
|
902
|
+
endpoint: `${category}/${endpoint === "news/company" ? "company" : endpoint}`,
|
|
903
|
+
count: results.length,
|
|
904
|
+
results,
|
|
905
|
+
});
|
|
906
|
+
} catch (err) {
|
|
907
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
},
|
|
911
|
+
{ names: ["fin_currency"] },
|
|
912
|
+
);
|
|
913
|
+
|
|
914
|
+
// === Tool 13: fin_data_markets — Supported Markets ===
|
|
579
915
|
api.registerTool(
|
|
580
916
|
{
|
|
581
917
|
name: "fin_data_markets",
|
|
@@ -584,7 +920,7 @@ const findooDatahubPlugin = {
|
|
|
584
920
|
parameters: Type.Object({}),
|
|
585
921
|
async execute() {
|
|
586
922
|
return json({
|
|
587
|
-
|
|
923
|
+
connected: !!client,
|
|
588
924
|
markets: dataProvider.getSupportedMarkets(),
|
|
589
925
|
categories: [
|
|
590
926
|
"equity",
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,34 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "findoo-datahub-plugin",
|
|
3
3
|
"name": "Findoo DataHub",
|
|
4
|
-
"description": "Unified financial data source — 172 endpoints covering equity (A/HK/US), crypto, macro, derivatives, index, ETF
|
|
4
|
+
"description": "Unified financial data source — 172 endpoints covering equity (A/HK/US), crypto, macro, derivatives, index, ETF. Works out of the box.",
|
|
5
5
|
"kind": "financial",
|
|
6
6
|
"version": "2026.3.2",
|
|
7
7
|
"skills": ["./skills"],
|
|
8
8
|
"configSchema": {
|
|
9
9
|
"type": "object",
|
|
10
10
|
"properties": {
|
|
11
|
-
"
|
|
11
|
+
"datahubApiKey": {
|
|
12
12
|
"type": "string",
|
|
13
|
-
"
|
|
14
|
-
"description": "DataHub
|
|
15
|
-
},
|
|
16
|
-
"datahubUsername": {
|
|
17
|
-
"type": "string",
|
|
18
|
-
"default": "admin",
|
|
19
|
-
"description": "DataHub HTTP Basic Auth username"
|
|
20
|
-
},
|
|
21
|
-
"datahubPassword": {
|
|
22
|
-
"type": "string",
|
|
23
|
-
"description": "DataHub HTTP Basic Auth password",
|
|
13
|
+
"minLength": 1,
|
|
14
|
+
"description": "DataHub API key (required). Also accepted via DATAHUB_API_KEY env var.",
|
|
24
15
|
"sensitive": true
|
|
25
|
-
},
|
|
26
|
-
"requestTimeoutMs": {
|
|
27
|
-
"type": "number",
|
|
28
|
-
"default": 30000,
|
|
29
|
-
"minimum": 1000,
|
|
30
|
-
"maximum": 120000
|
|
31
16
|
}
|
|
32
17
|
}
|
|
18
|
+
},
|
|
19
|
+
"uiHints": {
|
|
20
|
+
"datahubApiKey": {
|
|
21
|
+
"label": "API Key",
|
|
22
|
+
"help": "DataHub API 密钥(必填)。",
|
|
23
|
+
"sensitive": true,
|
|
24
|
+
"placeholder": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
25
|
+
}
|
|
33
26
|
}
|
|
34
27
|
}
|
package/package.json
CHANGED