@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.
- 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 +332 -14
- package/openclaw.plugin.json +2 -2
- 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 +339 -0
- package/test/e2e/l4-skill-tool-chain.live.test.ts +465 -0
- package/skills/crypto-defi/skill.md +0 -69
- package/skills/equity/skill.md +0 -64
- package/skills/market-radar/skill.md +0 -47
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
3
|
+
import type { DataHubClient } from "./datahub-client.js";
|
|
4
|
+
import { buildParams, dateRangeParams, json, registerCategoryTool } from "./tool-helpers.js";
|
|
5
|
+
import type { MarketType } from "./types.js";
|
|
6
|
+
|
|
7
|
+
type DataProvider = {
|
|
8
|
+
getOHLCV: (p: {
|
|
9
|
+
symbol: string;
|
|
10
|
+
market: MarketType;
|
|
11
|
+
timeframe: string;
|
|
12
|
+
since?: number;
|
|
13
|
+
limit?: number;
|
|
14
|
+
}) => Promise<import("./types.js").OHLCV[]>;
|
|
15
|
+
detectRegime: (p: {
|
|
16
|
+
symbol: string;
|
|
17
|
+
market: MarketType;
|
|
18
|
+
timeframe: string;
|
|
19
|
+
}) => Promise<import("./types.js").MarketRegime>;
|
|
20
|
+
getSupportedMarkets: () => import("./types.js").MarketInfo[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function registerAllTools(
|
|
24
|
+
api: OpenClawPluginApi,
|
|
25
|
+
client: DataHubClient,
|
|
26
|
+
dataProvider: DataProvider,
|
|
27
|
+
datahubApiUrl: string,
|
|
28
|
+
) {
|
|
29
|
+
// ============================================================
|
|
30
|
+
// Tool 1: fin_stock — Individual equity data (A/HK/US)
|
|
31
|
+
// ============================================================
|
|
32
|
+
registerCategoryTool(api, client, {
|
|
33
|
+
name: "fin_stock",
|
|
34
|
+
label: "Stock Data (A/HK/US)",
|
|
35
|
+
description:
|
|
36
|
+
"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. 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.",
|
|
37
|
+
category: "equity",
|
|
38
|
+
defaultEndpoint: "price/historical",
|
|
39
|
+
clientMethod: (c, ep, qp) => c.equity(ep, qp),
|
|
40
|
+
parameters: Type.Object({
|
|
41
|
+
symbol: Type.String({
|
|
42
|
+
description: "Stock code. A-shares: 600519.SH; HK: 00700.HK; US: AAPL",
|
|
43
|
+
}),
|
|
44
|
+
endpoint: Type.Unsafe<string>({
|
|
45
|
+
type: "string",
|
|
46
|
+
enum: [
|
|
47
|
+
"price/historical",
|
|
48
|
+
"price/quote",
|
|
49
|
+
"profile",
|
|
50
|
+
"search",
|
|
51
|
+
"screener",
|
|
52
|
+
// fundamentals
|
|
53
|
+
"fundamental/income",
|
|
54
|
+
"fundamental/balance",
|
|
55
|
+
"fundamental/cash",
|
|
56
|
+
"fundamental/ratios",
|
|
57
|
+
"fundamental/metrics",
|
|
58
|
+
"fundamental/dividends",
|
|
59
|
+
"fundamental/adj_factor",
|
|
60
|
+
"fundamental/earnings_forecast",
|
|
61
|
+
"fundamental/financial_audit",
|
|
62
|
+
"fundamental/financial_express",
|
|
63
|
+
"fundamental/forecast_vip",
|
|
64
|
+
"fundamental/revenue_per_segment",
|
|
65
|
+
"fundamental/management",
|
|
66
|
+
// ownership & structure
|
|
67
|
+
"ownership/top10_holders",
|
|
68
|
+
"ownership/top10_float_holders",
|
|
69
|
+
"ownership/major_holders",
|
|
70
|
+
"ownership/shareholder_trade",
|
|
71
|
+
"ownership/repurchase",
|
|
72
|
+
"ownership/share_float",
|
|
73
|
+
"ownership/holder_number",
|
|
74
|
+
"ownership/share_statistics",
|
|
75
|
+
"pledge/stat",
|
|
76
|
+
"pledge/detail",
|
|
77
|
+
// money flow (individual stock level)
|
|
78
|
+
"moneyflow/individual",
|
|
79
|
+
// concept
|
|
80
|
+
"concept/concept_detail",
|
|
81
|
+
"concept/concept_list",
|
|
82
|
+
// estimates (yfinance only)
|
|
83
|
+
"estimates/consensus",
|
|
84
|
+
// VIP / tushare-only
|
|
85
|
+
"fundamental/backup_daily",
|
|
86
|
+
"fundamental/balance_vip",
|
|
87
|
+
"fundamental/cashflow_vip",
|
|
88
|
+
"fundamental/dividend_detail",
|
|
89
|
+
"fundamental/historical_splits",
|
|
90
|
+
"fundamental/income_vip",
|
|
91
|
+
"fundamental/revenue_segment_vip",
|
|
92
|
+
"fundamental/stock_factor",
|
|
93
|
+
// HK subset
|
|
94
|
+
"hk/income",
|
|
95
|
+
"hk/balancesheet",
|
|
96
|
+
"hk/cashflow",
|
|
97
|
+
"hk/fina_indicator",
|
|
98
|
+
"hk/basic",
|
|
99
|
+
"hk/hold",
|
|
100
|
+
"hk/adj_factor",
|
|
101
|
+
"hk/trade_cal",
|
|
102
|
+
// US subset
|
|
103
|
+
"us/income",
|
|
104
|
+
"us/balancesheet",
|
|
105
|
+
"us/cashflow",
|
|
106
|
+
"us/fina_indicator",
|
|
107
|
+
"us/basic",
|
|
108
|
+
"us/adj_factor",
|
|
109
|
+
"us/trade_cal",
|
|
110
|
+
],
|
|
111
|
+
description: "DataHub equity endpoint path",
|
|
112
|
+
}),
|
|
113
|
+
...dateRangeParams,
|
|
114
|
+
provider: Type.Optional(
|
|
115
|
+
Type.String({ description: "Data provider override (tushare, yfinance, massive)" }),
|
|
116
|
+
),
|
|
117
|
+
}),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// ============================================================
|
|
121
|
+
// Tool 2: fin_index — Index / ETF / Fund
|
|
122
|
+
// ============================================================
|
|
123
|
+
registerCategoryTool(api, client, {
|
|
124
|
+
name: "fin_index",
|
|
125
|
+
label: "Index / ETF / Fund",
|
|
126
|
+
description:
|
|
127
|
+
"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.",
|
|
128
|
+
category: "index",
|
|
129
|
+
defaultEndpoint: "price/historical",
|
|
130
|
+
clientMethod: (c, ep, qp) => c.index(ep, qp),
|
|
131
|
+
parameters: Type.Object({
|
|
132
|
+
symbol: Type.Optional(
|
|
133
|
+
Type.String({ description: "Index/ETF/fund code. Index: 000300.SH; ETF: 510050.SH" }),
|
|
134
|
+
),
|
|
135
|
+
endpoint: Type.Unsafe<string>({
|
|
136
|
+
type: "string",
|
|
137
|
+
enum: [
|
|
138
|
+
"price/historical",
|
|
139
|
+
"constituents",
|
|
140
|
+
"daily_basic",
|
|
141
|
+
"available",
|
|
142
|
+
"info",
|
|
143
|
+
"classify",
|
|
144
|
+
"global_index",
|
|
145
|
+
"thematic/ths_index",
|
|
146
|
+
"thematic/ths_daily",
|
|
147
|
+
"thematic/ths_member",
|
|
148
|
+
],
|
|
149
|
+
description: "DataHub index endpoint path",
|
|
150
|
+
}),
|
|
151
|
+
...dateRangeParams,
|
|
152
|
+
}),
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// ============================================================
|
|
156
|
+
// Tool 3: fin_macro — Economy / Rates / FX
|
|
157
|
+
// ============================================================
|
|
158
|
+
api.registerTool(
|
|
159
|
+
{
|
|
160
|
+
name: "fin_macro",
|
|
161
|
+
label: "Macro / Rates / FX",
|
|
162
|
+
description:
|
|
163
|
+
"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, company news. Notes: LIBOR terminated 2023 (data stops 2020-06).",
|
|
164
|
+
parameters: Type.Object({
|
|
165
|
+
endpoint: Type.Unsafe<string>({
|
|
166
|
+
type: "string",
|
|
167
|
+
enum: [
|
|
168
|
+
"gdp/real",
|
|
169
|
+
"cpi",
|
|
170
|
+
"ppi",
|
|
171
|
+
"pmi",
|
|
172
|
+
"money_supply",
|
|
173
|
+
"social_financing",
|
|
174
|
+
"shibor",
|
|
175
|
+
"shibor_lpr",
|
|
176
|
+
"shibor_quote",
|
|
177
|
+
"libor",
|
|
178
|
+
"hibor",
|
|
179
|
+
"treasury_cn",
|
|
180
|
+
"treasury_us",
|
|
181
|
+
"index_global",
|
|
182
|
+
"wz_index",
|
|
183
|
+
"calendar",
|
|
184
|
+
"currency/price/historical",
|
|
185
|
+
"currency/search",
|
|
186
|
+
"currency/snapshots",
|
|
187
|
+
"worldbank/country",
|
|
188
|
+
"worldbank/gdp",
|
|
189
|
+
"worldbank/population",
|
|
190
|
+
"worldbank/inflation",
|
|
191
|
+
"worldbank/indicator",
|
|
192
|
+
"fixedincome/rate/shibor",
|
|
193
|
+
"fixedincome/rate/shibor_lpr",
|
|
194
|
+
"fixedincome/rate/libor",
|
|
195
|
+
"fixedincome/rate/hibor",
|
|
196
|
+
"news/company",
|
|
197
|
+
],
|
|
198
|
+
description: "DataHub economy/currency/news endpoint path",
|
|
199
|
+
}),
|
|
200
|
+
symbol: Type.Optional(
|
|
201
|
+
Type.String({ description: "Currency pair or stock symbol for news" }),
|
|
202
|
+
),
|
|
203
|
+
country: Type.Optional(Type.String({ description: "Country code for WorldBank" })),
|
|
204
|
+
...dateRangeParams,
|
|
205
|
+
}),
|
|
206
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
207
|
+
try {
|
|
208
|
+
const endpoint = String(params.endpoint ?? "cpi");
|
|
209
|
+
const qp = buildParams(params);
|
|
210
|
+
let results: unknown[];
|
|
211
|
+
let category: string;
|
|
212
|
+
if (endpoint.startsWith("currency/")) {
|
|
213
|
+
results = await client.currency(endpoint.replace("currency/", ""), qp);
|
|
214
|
+
category = "currency";
|
|
215
|
+
} else if (endpoint.startsWith("fixedincome/")) {
|
|
216
|
+
results = await client.query(endpoint, qp);
|
|
217
|
+
category = "fixedincome";
|
|
218
|
+
} else if (endpoint.startsWith("news/")) {
|
|
219
|
+
results = await client.query(endpoint, qp);
|
|
220
|
+
category = "news";
|
|
221
|
+
} else {
|
|
222
|
+
results = await client.economy(endpoint, qp);
|
|
223
|
+
category = "economy";
|
|
224
|
+
}
|
|
225
|
+
return json({
|
|
226
|
+
success: true,
|
|
227
|
+
endpoint: `${category}/${endpoint.replace(/^(currency|fixedincome|news)\//, "")}`,
|
|
228
|
+
count: results.length,
|
|
229
|
+
results,
|
|
230
|
+
});
|
|
231
|
+
} catch (err) {
|
|
232
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
{ names: ["fin_macro"] },
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// ============================================================
|
|
240
|
+
// Tool 4: fin_derivatives — Futures / Options / CB
|
|
241
|
+
// ============================================================
|
|
242
|
+
registerCategoryTool(api, client, {
|
|
243
|
+
name: "fin_derivatives",
|
|
244
|
+
label: "Futures / Options / CB",
|
|
245
|
+
description:
|
|
246
|
+
"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.",
|
|
247
|
+
category: "derivatives",
|
|
248
|
+
defaultEndpoint: "futures/historical",
|
|
249
|
+
clientMethod: (c, ep, qp) => c.derivatives(ep, qp),
|
|
250
|
+
parameters: Type.Object({
|
|
251
|
+
symbol: Type.Optional(
|
|
252
|
+
Type.String({ description: "Contract code, e.g. IF2501.CFX, 113xxx.SH (CB)" }),
|
|
253
|
+
),
|
|
254
|
+
endpoint: Type.Unsafe<string>({
|
|
255
|
+
type: "string",
|
|
256
|
+
enum: [
|
|
257
|
+
"futures/historical",
|
|
258
|
+
"futures/info",
|
|
259
|
+
"futures/holding",
|
|
260
|
+
"futures/settle",
|
|
261
|
+
"futures/warehouse",
|
|
262
|
+
"futures/mapping",
|
|
263
|
+
"futures/curve",
|
|
264
|
+
"options/basic",
|
|
265
|
+
"options/daily",
|
|
266
|
+
"options/chains",
|
|
267
|
+
"convertible/basic",
|
|
268
|
+
"convertible/daily",
|
|
269
|
+
],
|
|
270
|
+
description: "DataHub derivatives endpoint path",
|
|
271
|
+
}),
|
|
272
|
+
trade_date: Type.Optional(Type.String({ description: "Trade date, e.g. 2025-02-28" })),
|
|
273
|
+
...dateRangeParams,
|
|
274
|
+
}),
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// ============================================================
|
|
278
|
+
// Tool 5: fin_crypto — Crypto & DeFi
|
|
279
|
+
// ============================================================
|
|
280
|
+
api.registerTool(
|
|
281
|
+
{
|
|
282
|
+
name: "fin_crypto",
|
|
283
|
+
label: "Crypto & DeFi",
|
|
284
|
+
description:
|
|
285
|
+
"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.",
|
|
286
|
+
parameters: Type.Object({
|
|
287
|
+
endpoint: Type.Unsafe<string>({
|
|
288
|
+
type: "string",
|
|
289
|
+
enum: [
|
|
290
|
+
"market/ticker",
|
|
291
|
+
"market/tickers",
|
|
292
|
+
"market/orderbook",
|
|
293
|
+
"market/trades",
|
|
294
|
+
"market/funding_rate",
|
|
295
|
+
"coin/market",
|
|
296
|
+
"coin/historical",
|
|
297
|
+
"coin/info",
|
|
298
|
+
"coin/categories",
|
|
299
|
+
"coin/trending",
|
|
300
|
+
"coin/global_stats",
|
|
301
|
+
"defi/protocols",
|
|
302
|
+
"defi/tvl_historical",
|
|
303
|
+
"defi/protocol_tvl",
|
|
304
|
+
"defi/chains",
|
|
305
|
+
"defi/yields",
|
|
306
|
+
"defi/stablecoins",
|
|
307
|
+
"defi/fees",
|
|
308
|
+
"defi/dex_volumes",
|
|
309
|
+
"defi/bridges",
|
|
310
|
+
"defi/coin_prices",
|
|
311
|
+
"price/historical",
|
|
312
|
+
"search",
|
|
313
|
+
],
|
|
314
|
+
description: "DataHub crypto endpoint path",
|
|
315
|
+
}),
|
|
316
|
+
symbol: Type.Optional(
|
|
317
|
+
Type.String({ description: "Coin ID, trading pair, or protocol slug" }),
|
|
318
|
+
),
|
|
319
|
+
interval: Type.Optional(
|
|
320
|
+
Type.String({
|
|
321
|
+
description:
|
|
322
|
+
"Time interval for OHLCV data. E.g. '1m','5m','15m','1h','1d'. Default: '1d'",
|
|
323
|
+
}),
|
|
324
|
+
),
|
|
325
|
+
exchange: Type.Optional(
|
|
326
|
+
Type.String({
|
|
327
|
+
description:
|
|
328
|
+
"Crypto exchange name for CEX data (ccxt provider). E.g. 'binance','okx','bybit'. Default: 'binance'",
|
|
329
|
+
}),
|
|
330
|
+
),
|
|
331
|
+
provider: Type.Optional(
|
|
332
|
+
Type.String({
|
|
333
|
+
description:
|
|
334
|
+
"Data provider override: ccxt (CEX), coingecko (coin/*), fmp, tiingo, yfinance, polygon",
|
|
335
|
+
}),
|
|
336
|
+
),
|
|
337
|
+
...dateRangeParams,
|
|
338
|
+
}),
|
|
339
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
340
|
+
try {
|
|
341
|
+
const endpoint = String(params.endpoint ?? "coin/market");
|
|
342
|
+
const qp = buildParams(params);
|
|
343
|
+
// Endpoint-specific param mapping for crypto APIs
|
|
344
|
+
if (qp.symbol) {
|
|
345
|
+
const coinIdEndpoints = ["coin/historical", "coin/info"];
|
|
346
|
+
if (coinIdEndpoints.includes(endpoint)) {
|
|
347
|
+
qp.coin_id = qp.symbol;
|
|
348
|
+
delete qp.symbol;
|
|
349
|
+
} else if (endpoint === "defi/protocol_tvl") {
|
|
350
|
+
qp.protocol = qp.symbol;
|
|
351
|
+
delete qp.symbol;
|
|
352
|
+
} else if (endpoint === "defi/coin_prices") {
|
|
353
|
+
qp.coins = qp.symbol;
|
|
354
|
+
delete qp.symbol;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const results = await client.crypto(endpoint, qp);
|
|
358
|
+
return json({
|
|
359
|
+
success: true,
|
|
360
|
+
endpoint: `crypto/${endpoint}`,
|
|
361
|
+
count: results.length,
|
|
362
|
+
results,
|
|
363
|
+
});
|
|
364
|
+
} catch (err) {
|
|
365
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
{ names: ["fin_crypto"] },
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
// ============================================================
|
|
373
|
+
// Tool 6: fin_market — Market Radar (market-wide, not individual stock)
|
|
374
|
+
// ============================================================
|
|
375
|
+
api.registerTool(
|
|
376
|
+
{
|
|
377
|
+
name: "fin_market",
|
|
378
|
+
label: "Market Radar",
|
|
379
|
+
description:
|
|
380
|
+
"A-share market-wide 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), trade calendar. 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'. market_snapshots returns 12000+ records.",
|
|
381
|
+
parameters: Type.Object({
|
|
382
|
+
endpoint: Type.Unsafe<string>({
|
|
383
|
+
type: "string",
|
|
384
|
+
enum: [
|
|
385
|
+
"market/top_list",
|
|
386
|
+
"market/top_inst",
|
|
387
|
+
"market/limit_list",
|
|
388
|
+
"market/suspend",
|
|
389
|
+
"market/trade_calendar",
|
|
390
|
+
"market/stock_limit",
|
|
391
|
+
"moneyflow/individual",
|
|
392
|
+
"moneyflow/industry",
|
|
393
|
+
"moneyflow/block_trade",
|
|
394
|
+
"margin/summary",
|
|
395
|
+
"margin/detail",
|
|
396
|
+
"margin/trading",
|
|
397
|
+
"flow/hsgt_flow",
|
|
398
|
+
"flow/hsgt_top10",
|
|
399
|
+
"flow/ggt_daily",
|
|
400
|
+
"flow/ggt_monthly",
|
|
401
|
+
"flow/hs_const",
|
|
402
|
+
"market_snapshots",
|
|
403
|
+
"discovery/gainers",
|
|
404
|
+
"discovery/losers",
|
|
405
|
+
"discovery/active",
|
|
406
|
+
"discovery/new_share",
|
|
407
|
+
],
|
|
408
|
+
description: "DataHub equity endpoint for market data",
|
|
409
|
+
}),
|
|
410
|
+
trade_date: Type.Optional(Type.String({ description: "Trade date, e.g. 2025-02-28" })),
|
|
411
|
+
date: Type.Optional(
|
|
412
|
+
Type.String({
|
|
413
|
+
description:
|
|
414
|
+
"Date param for hsgt_top10 (uses 'date' not 'trade_date'), e.g. 2025-02-28",
|
|
415
|
+
}),
|
|
416
|
+
),
|
|
417
|
+
symbol: Type.Optional(Type.String({ description: "Symbol for specific queries" })),
|
|
418
|
+
hs_type: Type.Optional(
|
|
419
|
+
Type.String({
|
|
420
|
+
description: "Stock Connect type for hs_const: SH or SZ",
|
|
421
|
+
}),
|
|
422
|
+
),
|
|
423
|
+
...dateRangeParams,
|
|
424
|
+
}),
|
|
425
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
426
|
+
try {
|
|
427
|
+
const endpoint = String(params.endpoint ?? "market/top_list");
|
|
428
|
+
const qp = buildParams(params);
|
|
429
|
+
// Auto-alias: trade_date -> date for endpoints that use 'date' param
|
|
430
|
+
const dateEndpoints = [
|
|
431
|
+
"market/top_list",
|
|
432
|
+
"market/top_inst",
|
|
433
|
+
"market/limit_list",
|
|
434
|
+
"flow/hsgt_top10",
|
|
435
|
+
];
|
|
436
|
+
if (qp.trade_date && !qp.date && dateEndpoints.includes(endpoint)) {
|
|
437
|
+
qp.date = qp.trade_date;
|
|
438
|
+
delete qp.trade_date;
|
|
439
|
+
}
|
|
440
|
+
const results = await client.equity(endpoint, qp);
|
|
441
|
+
return json({
|
|
442
|
+
success: true,
|
|
443
|
+
endpoint: `equity/${endpoint}`,
|
|
444
|
+
count: results.length,
|
|
445
|
+
results,
|
|
446
|
+
});
|
|
447
|
+
} catch (err) {
|
|
448
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
{ names: ["fin_market"] },
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
// ============================================================
|
|
456
|
+
// Tool 7: fin_query — Raw DataHub Query (fallback)
|
|
457
|
+
// ============================================================
|
|
458
|
+
api.registerTool(
|
|
459
|
+
{
|
|
460
|
+
name: "fin_query",
|
|
461
|
+
label: "Raw DataHub Query",
|
|
462
|
+
description:
|
|
463
|
+
"Direct passthrough to any of 172 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.",
|
|
464
|
+
parameters: Type.Object({
|
|
465
|
+
path: Type.String({
|
|
466
|
+
description:
|
|
467
|
+
"Full endpoint path after /api/v1/, e.g. equity/fundamental/income, crypto/defi/protocols",
|
|
468
|
+
}),
|
|
469
|
+
params: Type.Optional(
|
|
470
|
+
Type.Record(Type.String(), Type.String(), {
|
|
471
|
+
description: "Query parameters as key-value pairs",
|
|
472
|
+
}),
|
|
473
|
+
),
|
|
474
|
+
}),
|
|
475
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
476
|
+
try {
|
|
477
|
+
const path = String(params.path ?? "").trim();
|
|
478
|
+
if (!path) throw new Error("path is required");
|
|
479
|
+
const qp = (params.params ?? {}) as Record<string, string>;
|
|
480
|
+
const results = await client.query(path, qp);
|
|
481
|
+
return json({ success: true, endpoint: path, count: results.length, results });
|
|
482
|
+
} catch (err) {
|
|
483
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
{ names: ["fin_query"] },
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
// ============================================================
|
|
491
|
+
// Tool 8: fin_data_ohlcv — OHLCV with caching
|
|
492
|
+
// ============================================================
|
|
493
|
+
api.registerTool(
|
|
494
|
+
{
|
|
495
|
+
name: "fin_data_ohlcv",
|
|
496
|
+
label: "OHLCV Data",
|
|
497
|
+
description: "Fetch OHLCV candle data with local SQLite caching via DataHub.",
|
|
498
|
+
parameters: Type.Object({
|
|
499
|
+
symbol: Type.String({
|
|
500
|
+
description: "Trading pair (e.g. BTC/USDT, AAPL, 600519.SH)",
|
|
501
|
+
}),
|
|
502
|
+
market: Type.Optional(
|
|
503
|
+
Type.Unsafe<"crypto" | "equity" | "commodity">({
|
|
504
|
+
type: "string",
|
|
505
|
+
enum: ["crypto", "equity", "commodity"],
|
|
506
|
+
description: "Market type (default: crypto)",
|
|
507
|
+
}),
|
|
508
|
+
),
|
|
509
|
+
timeframe: Type.Optional(
|
|
510
|
+
Type.Unsafe<"1m" | "5m" | "1h" | "4h" | "1d">({
|
|
511
|
+
type: "string",
|
|
512
|
+
enum: ["1m", "5m", "1h", "4h", "1d"],
|
|
513
|
+
description: "Candle timeframe (default: 1h)",
|
|
514
|
+
}),
|
|
515
|
+
),
|
|
516
|
+
since: Type.Optional(Type.Number({ description: "Start timestamp in Unix ms" })),
|
|
517
|
+
limit: Type.Optional(Type.Number({ description: "Number of candles to return" })),
|
|
518
|
+
}),
|
|
519
|
+
async execute(_id: string, params: Record<string, unknown>) {
|
|
520
|
+
try {
|
|
521
|
+
const symbol = params.symbol as string;
|
|
522
|
+
const market = (params.market as MarketType | undefined) ?? "crypto";
|
|
523
|
+
const timeframe = (params.timeframe as string | undefined) ?? "1h";
|
|
524
|
+
const since = params.since as number | undefined;
|
|
525
|
+
const limit = params.limit as number | undefined;
|
|
526
|
+
|
|
527
|
+
const ohlcv = await dataProvider.getOHLCV({ symbol, market, timeframe, since, limit });
|
|
528
|
+
|
|
529
|
+
return json({
|
|
530
|
+
symbol,
|
|
531
|
+
market,
|
|
532
|
+
timeframe,
|
|
533
|
+
count: ohlcv.length,
|
|
534
|
+
candles: ohlcv.map((bar) => ({
|
|
535
|
+
timestamp: new Date(bar.timestamp).toISOString(),
|
|
536
|
+
open: bar.open,
|
|
537
|
+
high: bar.high,
|
|
538
|
+
low: bar.low,
|
|
539
|
+
close: bar.close,
|
|
540
|
+
volume: bar.volume,
|
|
541
|
+
})),
|
|
542
|
+
});
|
|
543
|
+
} catch (err) {
|
|
544
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
{ names: ["fin_data_ohlcv"] },
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
// ============================================================
|
|
552
|
+
// Tool 9: fin_data_regime — Market Regime Detection
|
|
553
|
+
// ============================================================
|
|
554
|
+
api.registerTool(
|
|
555
|
+
{
|
|
556
|
+
name: "fin_data_regime",
|
|
557
|
+
label: "Market Regime",
|
|
558
|
+
description:
|
|
559
|
+
"Detect market regime (bull/bear/sideways/volatile/crisis) using SMA/ATR analysis on DataHub OHLCV data.",
|
|
560
|
+
parameters: Type.Object({
|
|
561
|
+
symbol: Type.String({ description: "Trading pair (e.g. BTC/USDT, 600519.SH)" }),
|
|
562
|
+
market: Type.Optional(
|
|
563
|
+
Type.Unsafe<"crypto" | "equity" | "commodity">({
|
|
564
|
+
type: "string",
|
|
565
|
+
enum: ["crypto", "equity", "commodity"],
|
|
566
|
+
description: "Market type (default: crypto)",
|
|
567
|
+
}),
|
|
568
|
+
),
|
|
569
|
+
timeframe: Type.Optional(
|
|
570
|
+
Type.Unsafe<"1m" | "5m" | "1h" | "4h" | "1d">({
|
|
571
|
+
type: "string",
|
|
572
|
+
enum: ["1m", "5m", "1h", "4h", "1d"],
|
|
573
|
+
description: "Candle timeframe (default: 4h)",
|
|
574
|
+
}),
|
|
575
|
+
),
|
|
576
|
+
}),
|
|
577
|
+
async execute(_id: string, params: Record<string, unknown>) {
|
|
578
|
+
try {
|
|
579
|
+
const symbol = params.symbol as string;
|
|
580
|
+
const market = (params.market as MarketType | undefined) ?? "crypto";
|
|
581
|
+
const timeframe = (params.timeframe as string | undefined) ?? "4h";
|
|
582
|
+
const regime = await dataProvider.detectRegime({ symbol, market, timeframe });
|
|
583
|
+
return json({ symbol, market, timeframe, regime });
|
|
584
|
+
} catch (err) {
|
|
585
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
{ names: ["fin_data_regime"] },
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
// ============================================================
|
|
593
|
+
// Tool 10: fin_ta — Technical Analysis Indicators
|
|
594
|
+
// ============================================================
|
|
595
|
+
api.registerTool(
|
|
596
|
+
{
|
|
597
|
+
name: "fin_ta",
|
|
598
|
+
label: "Technical Analysis",
|
|
599
|
+
description:
|
|
600
|
+
"Calculate technical indicators (SMA, EMA, RSI, MACD, Bollinger Bands) for any symbol. DataHub fetches OHLCV and computes server-side.",
|
|
601
|
+
parameters: Type.Object({
|
|
602
|
+
symbol: Type.String({
|
|
603
|
+
description:
|
|
604
|
+
"Stock/crypto symbol. A: 600519.SH; HK: 00700.HK; US: AAPL; Crypto: BTC-USDT",
|
|
605
|
+
}),
|
|
606
|
+
indicator: Type.Unsafe<string>({
|
|
607
|
+
type: "string",
|
|
608
|
+
enum: ["sma", "ema", "rsi", "macd", "bbands"],
|
|
609
|
+
description: "Technical indicator to calculate",
|
|
610
|
+
}),
|
|
611
|
+
period: Type.Optional(
|
|
612
|
+
Type.Number({
|
|
613
|
+
description: "Indicator period (default: 20 for SMA/EMA/BBANDS, 14 for RSI)",
|
|
614
|
+
}),
|
|
615
|
+
),
|
|
616
|
+
limit: Type.Optional(
|
|
617
|
+
Type.Number({ description: "Number of OHLCV bars to fetch (default: 200)" }),
|
|
618
|
+
),
|
|
619
|
+
fast: Type.Optional(Type.Number({ description: "MACD fast period (default: 12)" })),
|
|
620
|
+
slow: Type.Optional(Type.Number({ description: "MACD slow period (default: 26)" })),
|
|
621
|
+
signal: Type.Optional(Type.Number({ description: "MACD signal period (default: 9)" })),
|
|
622
|
+
std: Type.Optional(Type.Number({ description: "Bollinger Bands std dev (default: 2.0)" })),
|
|
623
|
+
provider: Type.Optional(
|
|
624
|
+
Type.String({ description: "Data provider override (auto-detected if omitted)" }),
|
|
625
|
+
),
|
|
626
|
+
}),
|
|
627
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
628
|
+
try {
|
|
629
|
+
const indicator = String(params.indicator ?? "sma");
|
|
630
|
+
const qp: Record<string, string> = {};
|
|
631
|
+
if (params.symbol) qp.symbol = String(params.symbol);
|
|
632
|
+
if (params.period) qp.period = String(params.period);
|
|
633
|
+
if (params.limit) qp.limit = String(params.limit);
|
|
634
|
+
if (params.fast) qp.fast = String(params.fast);
|
|
635
|
+
if (params.slow) qp.slow = String(params.slow);
|
|
636
|
+
if (params.signal) qp.signal = String(params.signal);
|
|
637
|
+
if (params.std) qp.std = String(params.std);
|
|
638
|
+
if (params.provider) qp.provider = String(params.provider);
|
|
639
|
+
const results = await client.ta(indicator, qp);
|
|
640
|
+
return json({
|
|
641
|
+
success: true,
|
|
642
|
+
endpoint: `ta/${indicator}`,
|
|
643
|
+
count: results.length,
|
|
644
|
+
results,
|
|
645
|
+
});
|
|
646
|
+
} catch (err) {
|
|
647
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
648
|
+
}
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
{ names: ["fin_ta"] },
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
// ============================================================
|
|
655
|
+
// Tool 11: fin_etf — ETF & Fund Data
|
|
656
|
+
// ============================================================
|
|
657
|
+
registerCategoryTool(api, client, {
|
|
658
|
+
name: "fin_etf",
|
|
659
|
+
label: "ETF & Fund",
|
|
660
|
+
description:
|
|
661
|
+
"ETF and fund data -- NAV history, fund info (type/size/fees), historical prices, portfolio holdings (top 10, quarterly), manager track record, dividends, share changes, adjusted NAV, search. Use fund/manager with a known fund code to find manager info.",
|
|
662
|
+
category: "etf",
|
|
663
|
+
defaultEndpoint: "info",
|
|
664
|
+
clientMethod: (c, ep, qp) => c.etf(ep, qp),
|
|
665
|
+
parameters: Type.Object({
|
|
666
|
+
symbol: Type.Optional(
|
|
667
|
+
Type.String({ description: "ETF/Fund code. ETF: 510050.SH; Fund: 110011" }),
|
|
668
|
+
),
|
|
669
|
+
manager: Type.Optional(
|
|
670
|
+
Type.String({ description: "Fund manager name for search (e.g. Zhang Kun)" }),
|
|
671
|
+
),
|
|
672
|
+
endpoint: Type.Unsafe<string>({
|
|
673
|
+
type: "string",
|
|
674
|
+
enum: [
|
|
675
|
+
"nav",
|
|
676
|
+
"info",
|
|
677
|
+
"historical",
|
|
678
|
+
"fund/portfolio",
|
|
679
|
+
"fund/manager",
|
|
680
|
+
"fund/dividends",
|
|
681
|
+
"fund/share",
|
|
682
|
+
"fund/adj_nav",
|
|
683
|
+
"search",
|
|
684
|
+
],
|
|
685
|
+
description: "DataHub ETF/fund endpoint path",
|
|
686
|
+
}),
|
|
687
|
+
...dateRangeParams,
|
|
688
|
+
}),
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// ============================================================
|
|
692
|
+
// Tool 12: fin_data_markets — Supported Markets
|
|
693
|
+
// ============================================================
|
|
694
|
+
api.registerTool(
|
|
695
|
+
{
|
|
696
|
+
name: "fin_data_markets",
|
|
697
|
+
label: "Supported Markets",
|
|
698
|
+
description: "List all supported market types and data categories",
|
|
699
|
+
parameters: Type.Object({}),
|
|
700
|
+
async execute() {
|
|
701
|
+
return json({
|
|
702
|
+
datahub: datahubApiUrl,
|
|
703
|
+
markets: dataProvider.getSupportedMarkets(),
|
|
704
|
+
categories: [
|
|
705
|
+
"equity",
|
|
706
|
+
"crypto",
|
|
707
|
+
"economy",
|
|
708
|
+
"derivatives",
|
|
709
|
+
"index",
|
|
710
|
+
"etf",
|
|
711
|
+
"currency",
|
|
712
|
+
"coverage",
|
|
713
|
+
],
|
|
714
|
+
endpoints: 172,
|
|
715
|
+
});
|
|
716
|
+
},
|
|
717
|
+
},
|
|
718
|
+
{ names: ["fin_data_markets"] },
|
|
719
|
+
);
|
|
720
|
+
}
|