@openfinclaw/findoo-datahub-plugin 2026.3.2 → 2026.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/DESIGN.md +492 -151
  2. package/_vendor/claude-skills-finance/SKILL.md +192 -0
  3. package/_vendor/claude-skills-finance/assets/dcf_analysis_template.md +184 -0
  4. package/_vendor/claude-skills-finance/assets/expected_output.json +161 -0
  5. package/_vendor/claude-skills-finance/assets/forecast_report_template.md +177 -0
  6. package/_vendor/claude-skills-finance/assets/sample_financial_data.json +219 -0
  7. package/_vendor/claude-skills-finance/assets/variance_report_template.md +122 -0
  8. package/_vendor/claude-skills-finance/references/financial-ratios-guide.md +396 -0
  9. package/_vendor/claude-skills-finance/references/forecasting-best-practices.md +294 -0
  10. package/_vendor/claude-skills-finance/references/valuation-methodology.md +255 -0
  11. package/_vendor/claude-skills-finance/scripts/budget_variance_analyzer.py +406 -0
  12. package/_vendor/claude-skills-finance/scripts/dcf_valuation.py +449 -0
  13. package/_vendor/claude-skills-finance/scripts/forecast_builder.py +494 -0
  14. package/_vendor/claude-skills-finance/scripts/ratio_calculator.py +432 -0
  15. package/index.ts +332 -14
  16. package/openclaw.plugin.json +2 -2
  17. package/package.json +1 -1
  18. package/references/cn-market-specifics.md +165 -0
  19. package/references/crypto-analysis.md +635 -0
  20. package/references/financial-ratios-cn.md +452 -0
  21. package/references/hk-market-specifics.md +166 -0
  22. package/references/macro-cycle-cn.md +409 -0
  23. package/references/valuation-cn.md +427 -0
  24. package/skills/README.md +294 -0
  25. package/skills/a-concept-cycle/skill.md +200 -0
  26. package/skills/a-convertible-arb/skill.md +294 -0
  27. package/skills/a-dividend-king/skill.md +187 -0
  28. package/skills/a-earnings-season/skill.md +221 -0
  29. package/skills/a-index-timer/skill.md +192 -0
  30. package/skills/a-ipo-new/skill.md +297 -0
  31. package/skills/a-northbound-decoder/skill.md +185 -0
  32. package/skills/a-quant-board/skill.md +286 -0
  33. package/skills/a-share/skill.md +347 -0
  34. package/skills/a-share-radar/skill.md +185 -0
  35. package/skills/cross-asset/skill.md +202 -0
  36. package/skills/crypto/skill.md +269 -0
  37. package/skills/crypto-altseason/skill.md +208 -0
  38. package/skills/crypto-btc-cycle/skill.md +231 -0
  39. package/skills/crypto-defi-yield/skill.md +181 -0
  40. package/skills/crypto-funding-arb/skill.md +158 -0
  41. package/skills/crypto-stablecoin-flow/skill.md +149 -0
  42. package/skills/data-query/skill.md +124 -30
  43. package/skills/derivatives/skill.md +188 -35
  44. package/skills/etf-fund/skill.md +216 -0
  45. package/skills/factor-screen/skill.md +186 -0
  46. package/skills/hk-china-internet/skill.md +190 -0
  47. package/skills/hk-dividend-harvest/skill.md +192 -0
  48. package/skills/hk-hsi-pulse/skill.md +154 -0
  49. package/skills/hk-southbound-alpha/skill.md +163 -0
  50. package/skills/hk-stock/skill.md +295 -0
  51. package/skills/macro/skill.md +244 -53
  52. package/skills/risk-monitor/skill.md +171 -0
  53. package/skills/us-dividend/skill.md +162 -0
  54. package/skills/us-earnings/skill.md +149 -0
  55. package/skills/us-equity/skill.md +235 -0
  56. package/skills/us-etf/skill.md +261 -0
  57. package/skills/us-sector-rotation/skill.md +223 -0
  58. package/src/config.ts +4 -5
  59. package/src/datahub-client.test.ts +4 -7
  60. package/src/datahub-client.ts +6 -1
  61. package/src/register-tools.ts +720 -0
  62. package/src/tool-helpers.ts +89 -0
  63. package/test/e2e/l3-gateway-bootstrap.live.test.ts +339 -0
  64. package/test/e2e/l4-skill-tool-chain.live.test.ts +465 -0
  65. package/skills/crypto-defi/skill.md +0 -69
  66. package/skills/equity/skill.md +0 -64
  67. 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,14 +40,21 @@ 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
- async register(api: OpenClawPluginApi) {
43
+ register(api: OpenClawPluginApi) {
41
44
  const config = resolveConfig(api);
42
45
 
43
46
  // --- DataHub client ---
47
+ if (!config.datahubApiKey) {
48
+ api.log?.(
49
+ "warn",
50
+ "findoo-datahub-plugin: no API key configured. Set DATAHUB_API_KEY env var or plugins.findoo-datahub-plugin.datahubApiKey in config. Using built-in dev key.",
51
+ );
52
+ }
53
+
44
54
  const client = new DataHubClient(
45
55
  config.datahubApiUrl,
46
56
  config.datahubUsername,
47
- config.datahubPassword,
57
+ config.datahubApiKey ?? "98ffa5c5-1ec6-4735-8e0c-715a5eca1a8d",
48
58
  config.requestTimeoutMs,
49
59
  );
50
60
 
@@ -118,7 +128,7 @@ const findooDatahubPlugin = {
118
128
  } as Parameters<typeof api.registerService>[0]);
119
129
 
120
130
  // ============================================================
121
- // AI Tools (10 total)
131
+ // AI Tools (13 total)
122
132
  // ============================================================
123
133
 
124
134
  // === Tool 1: fin_stock — Equity data ===
@@ -127,7 +137,7 @@ const findooDatahubPlugin = {
127
137
  name: "fin_stock",
128
138
  label: "Stock Data (A/HK/US)",
129
139
  description:
130
- "Fetch A-share, HK, or US equity data — quotes, historical prices, income, balance sheet, cashflow, ratios, money flow, holders, dividends, news.",
140
+ "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
141
  parameters: Type.Object({
132
142
  symbol: Type.String({
133
143
  description: "Stock code. A-shares: 600519.SH; HK: 00700.HK; US: AAPL",
@@ -136,17 +146,81 @@ const findooDatahubPlugin = {
136
146
  type: "string",
137
147
  enum: [
138
148
  "price/historical",
149
+ "price/quote",
150
+ "profile",
151
+ "search",
152
+ "screener",
153
+ // fundamentals — A-share
139
154
  "fundamental/income",
140
155
  "fundamental/balance",
141
156
  "fundamental/cash",
142
157
  "fundamental/ratios",
143
158
  "fundamental/metrics",
144
159
  "fundamental/dividends",
160
+ "fundamental/adj_factor",
161
+ "fundamental/earnings_forecast",
162
+ "fundamental/financial_audit",
163
+ "fundamental/financial_express",
164
+ "fundamental/forecast_vip",
165
+ "fundamental/revenue_per_segment",
166
+ "fundamental/management",
167
+ // ownership & structure
145
168
  "ownership/top10_holders",
169
+ "ownership/top10_float_holders",
170
+ "ownership/major_holders",
171
+ "ownership/shareholder_trade",
172
+ "ownership/repurchase",
173
+ "ownership/share_float",
174
+ "ownership/holder_number",
175
+ "ownership/share_statistics",
176
+ "pledge/stat",
177
+ "pledge/detail",
178
+ // money flow & market
146
179
  "moneyflow/individual",
147
180
  "market/top_list",
181
+ // discovery & calendar
148
182
  "discovery/gainers",
149
183
  "discovery/losers",
184
+ "discovery/active",
185
+ "discovery/undervalued_growth",
186
+ "discovery/undervalued_large_caps",
187
+ "discovery/growth_tech",
188
+ "discovery/aggressive_small_caps",
189
+ "discovery/name_change",
190
+ "calendar/earnings",
191
+ "calendar/ipo",
192
+ "compare/peers",
193
+ "shorts/short_volume",
194
+ "concept/concept_detail",
195
+ "concept/concept_list",
196
+ // estimates (yfinance only — US/HK stocks, not A-share; may hit rate limits)
197
+ "estimates/consensus",
198
+ // additional fundamentals (VIP / tushare-only)
199
+ "fundamental/backup_daily",
200
+ "fundamental/balance_vip",
201
+ "fundamental/cashflow_vip",
202
+ "fundamental/dividend_detail",
203
+ "fundamental/historical_splits",
204
+ "fundamental/income_vip",
205
+ "fundamental/revenue_segment_vip",
206
+ "fundamental/stock_factor",
207
+ // HK
208
+ "hk/income",
209
+ "hk/balancesheet",
210
+ "hk/cashflow",
211
+ "hk/fina_indicator",
212
+ "hk/basic",
213
+ "hk/hold",
214
+ "hk/adj_factor",
215
+ "hk/trade_cal",
216
+ // US
217
+ "us/income",
218
+ "us/balancesheet",
219
+ "us/cashflow",
220
+ "us/fina_indicator",
221
+ "us/basic",
222
+ "us/adj_factor",
223
+ "us/trade_cal",
150
224
  ],
151
225
  description: "DataHub equity endpoint path",
152
226
  }),
@@ -182,7 +256,7 @@ const findooDatahubPlugin = {
182
256
  name: "fin_index",
183
257
  label: "Index / ETF / Fund",
184
258
  description:
185
- "Query index constituents, valuations, ETF prices/NAV, fund manager/portfolio.",
259
+ "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
260
  parameters: Type.Object({
187
261
  symbol: Type.Optional(
188
262
  Type.String({ description: "Index/ETF/fund code. Index: 000300.SH; ETF: 510050.SH" }),
@@ -193,6 +267,10 @@ const findooDatahubPlugin = {
193
267
  "price/historical",
194
268
  "constituents",
195
269
  "daily_basic",
270
+ "available",
271
+ "info",
272
+ "classify",
273
+ "global_index",
196
274
  "thematic/ths_index",
197
275
  "thematic/ths_daily",
198
276
  "thematic/ths_member",
@@ -228,7 +306,7 @@ const findooDatahubPlugin = {
228
306
  name: "fin_macro",
229
307
  label: "Macro / Rates / FX",
230
308
  description:
231
- "China macro (GDP/CPI/PPI/PMI/M2), interest rates (Shibor/LPR/Libor/Hibor), treasury yields, FX, WorldBank data.",
309
+ "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
310
  parameters: Type.Object({
233
311
  endpoint: Type.Unsafe<string>({
234
312
  type: "string",
@@ -241,18 +319,29 @@ const findooDatahubPlugin = {
241
319
  "social_financing",
242
320
  "shibor",
243
321
  "shibor_lpr",
322
+ "shibor_quote",
244
323
  "libor",
245
324
  "hibor",
246
325
  "treasury_cn",
247
326
  "treasury_us",
248
327
  "index_global",
328
+ "wz_index",
249
329
  "calendar",
330
+ "currency/price/historical",
331
+ "currency/search",
332
+ "currency/snapshots",
333
+ "worldbank/country",
250
334
  "worldbank/gdp",
251
335
  "worldbank/population",
252
336
  "worldbank/inflation",
253
337
  "worldbank/indicator",
338
+ // fixedincome (dedicated DataHub paths, same data as shibor/libor/hibor)
339
+ "fixedincome/rate/shibor",
340
+ "fixedincome/rate/shibor_lpr",
341
+ "fixedincome/rate/libor",
342
+ "fixedincome/rate/hibor",
254
343
  ],
255
- description: "DataHub economy endpoint path",
344
+ description: "DataHub economy/currency endpoint path",
256
345
  }),
257
346
  symbol: Type.Optional(Type.String({ description: "Currency pair or indicator code" })),
258
347
  country: Type.Optional(Type.String({ description: "Country code for WorldBank" })),
@@ -264,10 +353,22 @@ const findooDatahubPlugin = {
264
353
  try {
265
354
  const endpoint = String(params.endpoint ?? "cpi");
266
355
  const qp = buildParams(params);
267
- const results = await client.economy(endpoint, qp);
356
+ // Route currency/* and fixedincome/* endpoints to their categories
357
+ let results: unknown[];
358
+ let category: string;
359
+ if (endpoint.startsWith("currency/")) {
360
+ results = await client.currency(endpoint.replace("currency/", ""), qp);
361
+ category = "currency";
362
+ } else if (endpoint.startsWith("fixedincome/")) {
363
+ results = await client.query(endpoint, qp);
364
+ category = "fixedincome";
365
+ } else {
366
+ results = await client.economy(endpoint, qp);
367
+ category = "economy";
368
+ }
268
369
  return json({
269
370
  success: true,
270
- endpoint: `economy/${endpoint}`,
371
+ endpoint: `${category}/${endpoint.startsWith("currency/") ? endpoint.replace("currency/", "") : endpoint}`,
271
372
  count: results.length,
272
373
  results,
273
374
  });
@@ -285,7 +386,7 @@ const findooDatahubPlugin = {
285
386
  name: "fin_derivatives",
286
387
  label: "Futures / Options / CB",
287
388
  description:
288
- "Futures (daily, holdings, settlement, warehouse, mapping), options (basic, daily, chains), convertible bonds.",
389
+ "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
390
  parameters: Type.Object({
290
391
  symbol: Type.Optional(
291
392
  Type.String({ description: "Contract code, e.g. IF2501.CFX, 113xxx.SH (CB)" }),
@@ -299,6 +400,7 @@ const findooDatahubPlugin = {
299
400
  "futures/settle",
300
401
  "futures/warehouse",
301
402
  "futures/mapping",
403
+ "futures/curve",
302
404
  "options/basic",
303
405
  "options/daily",
304
406
  "options/chains",
@@ -337,7 +439,7 @@ const findooDatahubPlugin = {
337
439
  name: "fin_crypto",
338
440
  label: "Crypto & DeFi",
339
441
  description:
340
- "Crypto market data (tickers, orderbook, funding rates) via CEX, DeFi (TVL, yields, stablecoins, fees, DEX volumes) via DefiLlama, market cap rankings via CoinGecko.",
442
+ "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
443
  parameters: Type.Object({
342
444
  endpoint: Type.Unsafe<string>({
343
445
  type: "string",
@@ -361,19 +463,42 @@ const findooDatahubPlugin = {
361
463
  "defi/stablecoins",
362
464
  "defi/fees",
363
465
  "defi/dex_volumes",
466
+ "defi/bridges",
364
467
  "defi/coin_prices",
468
+ "price/historical",
469
+ "search",
365
470
  ],
366
471
  description: "DataHub crypto endpoint path",
367
472
  }),
368
473
  symbol: Type.Optional(
369
474
  Type.String({ description: "Coin ID, trading pair, or protocol slug" }),
370
475
  ),
476
+ start_date: Type.Optional(
477
+ Type.String({ description: "Start date for coin/historical (YYYY-MM-DD)" }),
478
+ ),
479
+ end_date: Type.Optional(
480
+ Type.String({ description: "End date for coin/historical (YYYY-MM-DD)" }),
481
+ ),
371
482
  limit: Type.Optional(Type.Number()),
372
483
  }),
373
484
  async execute(_toolCallId: string, params: Record<string, unknown>) {
374
485
  try {
375
486
  const endpoint = String(params.endpoint ?? "coin/market");
376
487
  const qp = buildParams(params);
488
+ // Endpoint-specific param mapping for crypto APIs
489
+ if (qp.symbol) {
490
+ const coinIdEndpoints = ["coin/historical", "coin/info"];
491
+ if (coinIdEndpoints.includes(endpoint)) {
492
+ qp.coin_id = qp.symbol;
493
+ delete qp.symbol;
494
+ } else if (endpoint === "defi/protocol_tvl") {
495
+ qp.protocol = qp.symbol;
496
+ delete qp.symbol;
497
+ } else if (endpoint === "defi/coin_prices") {
498
+ qp.coins = qp.symbol;
499
+ delete qp.symbol;
500
+ }
501
+ }
377
502
  const results = await client.crypto(endpoint, qp);
378
503
  return json({
379
504
  success: true,
@@ -395,7 +520,7 @@ const findooDatahubPlugin = {
395
520
  name: "fin_market",
396
521
  label: "Market Radar",
397
522
  description:
398
- "Market monitoring — dragon-tiger list, limit-up/down stats, block trades, sector money flow, margin, Stock Connect flows, global index, IPO calendar.",
523
+ "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
524
  parameters: Type.Object({
400
525
  endpoint: Type.Unsafe<string>({
401
526
  type: "string",
@@ -410,8 +535,14 @@ const findooDatahubPlugin = {
410
535
  "moneyflow/block_trade",
411
536
  "margin/summary",
412
537
  "margin/detail",
538
+ "margin/trading",
413
539
  "flow/hsgt_flow",
414
540
  "flow/hsgt_top10",
541
+ "flow/ggt_daily",
542
+ "flow/ggt_monthly",
543
+ "flow/hs_const",
544
+ "market/stock_limit",
545
+ "market_snapshots",
415
546
  "discovery/gainers",
416
547
  "discovery/losers",
417
548
  "discovery/active",
@@ -420,7 +551,19 @@ const findooDatahubPlugin = {
420
551
  description: "DataHub equity endpoint for market data",
421
552
  }),
422
553
  trade_date: Type.Optional(Type.String({ description: "Trade date, e.g. 2025-02-28" })),
554
+ date: Type.Optional(
555
+ Type.String({
556
+ description:
557
+ "Date param for hsgt_top10 (uses 'date' not 'trade_date'), e.g. 2025-02-28",
558
+ }),
559
+ ),
423
560
  symbol: Type.Optional(Type.String({ description: "Symbol for specific queries" })),
561
+ hs_type: Type.Optional(
562
+ Type.String({
563
+ description:
564
+ "Stock Connect type for hs_const: SH (northbound) or SZ (northbound Shenzhen)",
565
+ }),
566
+ ),
424
567
  start_date: Type.Optional(Type.String()),
425
568
  end_date: Type.Optional(Type.String()),
426
569
  limit: Type.Optional(Type.Number()),
@@ -429,6 +572,17 @@ const findooDatahubPlugin = {
429
572
  try {
430
573
  const endpoint = String(params.endpoint ?? "market/top_list");
431
574
  const qp = buildParams(params);
575
+ // Auto-alias: trade_date→date for endpoints that use 'date' param
576
+ const dateEndpoints = [
577
+ "market/top_list",
578
+ "market/top_inst",
579
+ "market/limit_list",
580
+ "flow/hsgt_top10",
581
+ ];
582
+ if (qp.trade_date && !qp.date && dateEndpoints.includes(endpoint)) {
583
+ qp.date = qp.trade_date;
584
+ delete qp.trade_date;
585
+ }
432
586
  const results = await client.equity(endpoint, qp);
433
587
  return json({
434
588
  success: true,
@@ -450,7 +604,7 @@ const findooDatahubPlugin = {
450
604
  name: "fin_query",
451
605
  label: "Raw DataHub Query",
452
606
  description:
453
- "Direct passthrough to any of 172 DataHub endpoints by path. Use when other tools don't cover the specific data.",
607
+ "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
608
  parameters: Type.Object({
455
609
  path: Type.String({
456
610
  description:
@@ -575,7 +729,171 @@ const findooDatahubPlugin = {
575
729
  { names: ["fin_data_regime"] },
576
730
  );
577
731
 
578
- // === Tool 10: fin_data_marketsSupported Markets ===
732
+ // === Tool 10: fin_taTechnical Analysis Indicators ===
733
+ api.registerTool(
734
+ {
735
+ name: "fin_ta",
736
+ label: "Technical Analysis",
737
+ description:
738
+ "Calculate technical indicators (SMA, EMA, RSI, MACD, Bollinger Bands) for any symbol. DataHub fetches OHLCV and computes server-side.",
739
+ parameters: Type.Object({
740
+ symbol: Type.String({
741
+ description:
742
+ "Stock/crypto symbol. A: 600519.SH; HK: 00700.HK; US: AAPL; Crypto: BTC-USDT",
743
+ }),
744
+ indicator: Type.Unsafe<string>({
745
+ type: "string",
746
+ enum: ["sma", "ema", "rsi", "macd", "bbands"],
747
+ description: "Technical indicator to calculate",
748
+ }),
749
+ period: Type.Optional(
750
+ Type.Number({
751
+ description: "Indicator period (default: 20 for SMA/EMA/BBANDS, 14 for RSI)",
752
+ }),
753
+ ),
754
+ limit: Type.Optional(
755
+ Type.Number({ description: "Number of OHLCV bars to fetch (default: 200)" }),
756
+ ),
757
+ fast: Type.Optional(Type.Number({ description: "MACD fast period (default: 12)" })),
758
+ slow: Type.Optional(Type.Number({ description: "MACD slow period (default: 26)" })),
759
+ signal: Type.Optional(Type.Number({ description: "MACD signal period (default: 9)" })),
760
+ std: Type.Optional(
761
+ Type.Number({ description: "Bollinger Bands std dev (default: 2.0)" }),
762
+ ),
763
+ provider: Type.Optional(
764
+ Type.String({ description: "Data provider override (auto-detected if omitted)" }),
765
+ ),
766
+ }),
767
+ async execute(_toolCallId: string, params: Record<string, unknown>) {
768
+ try {
769
+ const indicator = String(params.indicator ?? "sma");
770
+ const qp: Record<string, string> = {};
771
+ if (params.symbol) qp.symbol = String(params.symbol);
772
+ if (params.period) qp.period = String(params.period);
773
+ if (params.limit) qp.limit = String(params.limit);
774
+ if (params.fast) qp.fast = String(params.fast);
775
+ if (params.slow) qp.slow = String(params.slow);
776
+ if (params.signal) qp.signal = String(params.signal);
777
+ if (params.std) qp.std = String(params.std);
778
+ if (params.provider) qp.provider = String(params.provider);
779
+ const results = await client.ta(indicator, qp);
780
+ return json({
781
+ success: true,
782
+ endpoint: `ta/${indicator}`,
783
+ count: results.length,
784
+ results,
785
+ });
786
+ } catch (err) {
787
+ return json({ error: err instanceof Error ? err.message : String(err) });
788
+ }
789
+ },
790
+ },
791
+ { names: ["fin_ta"] },
792
+ );
793
+
794
+ // === Tool 11: fin_etf — ETF & Fund Data ===
795
+ api.registerTool(
796
+ {
797
+ name: "fin_etf",
798
+ label: "ETF & Fund",
799
+ description:
800
+ "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.",
801
+ parameters: Type.Object({
802
+ symbol: Type.Optional(
803
+ Type.String({
804
+ description: "ETF/Fund code. ETF: 510050.SH; Fund: 110011",
805
+ }),
806
+ ),
807
+ manager: Type.Optional(
808
+ Type.String({
809
+ description: "Fund manager name for search endpoint (e.g. 张坤)",
810
+ }),
811
+ ),
812
+ endpoint: Type.Unsafe<string>({
813
+ type: "string",
814
+ enum: [
815
+ "nav",
816
+ "info",
817
+ "historical",
818
+ "fund/portfolio",
819
+ "fund/manager",
820
+ "fund/dividends",
821
+ "fund/share",
822
+ "fund/adj_nav",
823
+ "search",
824
+ ],
825
+ description: "DataHub ETF/fund endpoint path",
826
+ }),
827
+ start_date: Type.Optional(Type.String({ description: "Start date, e.g. 2025-01-01" })),
828
+ end_date: Type.Optional(Type.String({ description: "End date, e.g. 2025-12-31" })),
829
+ limit: Type.Optional(Type.Number({ description: "Max records to return" })),
830
+ }),
831
+ async execute(_toolCallId: string, params: Record<string, unknown>) {
832
+ try {
833
+ const endpoint = String(params.endpoint ?? "info");
834
+ const qp = buildParams(params);
835
+ const results = await client.etf(endpoint, qp);
836
+ return json({
837
+ success: true,
838
+ endpoint: `etf/${endpoint}`,
839
+ count: results.length,
840
+ results,
841
+ });
842
+ } catch (err) {
843
+ return json({ error: err instanceof Error ? err.message : String(err) });
844
+ }
845
+ },
846
+ },
847
+ { names: ["fin_etf"] },
848
+ );
849
+
850
+ // === Tool 12: fin_currency — FX & News ===
851
+ api.registerTool(
852
+ {
853
+ name: "fin_currency",
854
+ label: "FX & News",
855
+ description:
856
+ "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.",
857
+ parameters: Type.Object({
858
+ endpoint: Type.Unsafe<string>({
859
+ type: "string",
860
+ enum: ["price/historical", "search", "snapshots", "news/company"],
861
+ description: "DataHub currency/news endpoint path",
862
+ }),
863
+ symbol: Type.Optional(
864
+ Type.String({
865
+ description: "Currency pair (USDCNH, EURUSD) or stock symbol for news (AAPL)",
866
+ }),
867
+ ),
868
+ start_date: Type.Optional(Type.String({ description: "Start date, e.g. 2025-01-01" })),
869
+ end_date: Type.Optional(Type.String({ description: "End date, e.g. 2025-12-31" })),
870
+ limit: Type.Optional(Type.Number({ description: "Max records to return" })),
871
+ }),
872
+ async execute(_toolCallId: string, params: Record<string, unknown>) {
873
+ try {
874
+ const endpoint = String(params.endpoint ?? "price/historical");
875
+ const qp = buildParams(params);
876
+ // Route news/* to the generic query path
877
+ const results =
878
+ endpoint === "news/company"
879
+ ? await client.query(`news/company`, qp)
880
+ : await client.currency(endpoint, qp);
881
+ const category = endpoint === "news/company" ? "news" : "currency";
882
+ return json({
883
+ success: true,
884
+ endpoint: `${category}/${endpoint === "news/company" ? "company" : endpoint}`,
885
+ count: results.length,
886
+ results,
887
+ });
888
+ } catch (err) {
889
+ return json({ error: err instanceof Error ? err.message : String(err) });
890
+ }
891
+ },
892
+ },
893
+ { names: ["fin_currency"] },
894
+ );
895
+
896
+ // === Tool 13: fin_data_markets — Supported Markets ===
579
897
  api.registerTool(
580
898
  {
581
899
  name: "fin_data_markets",
@@ -18,9 +18,9 @@
18
18
  "default": "admin",
19
19
  "description": "DataHub HTTP Basic Auth username"
20
20
  },
21
- "datahubPassword": {
21
+ "datahubApiKey": {
22
22
  "type": "string",
23
- "description": "DataHub HTTP Basic Auth password",
23
+ "description": "DataHub API key (required). Set via config or DATAHUB_API_KEY env var.",
24
24
  "sensitive": true
25
25
  },
26
26
  "requestTimeoutMs": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfinclaw/findoo-datahub-plugin",
3
- "version": "2026.3.2",
3
+ "version": "2026.3.10",
4
4
  "description": "Unified financial data source — free mode (CCXT/CoinGecko/DefiLlama/Yahoo) + full mode (172 DataHub endpoints)",
5
5
  "keywords": [
6
6
  "crypto",