@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,465 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L4 — Skill-driven Tool Chain E2E
|
|
3
|
+
*
|
|
4
|
+
* Simulates the multi-step tool_use sequences that an LLM would execute
|
|
5
|
+
* when following a skill.md decision tree. Each scenario mirrors a real
|
|
6
|
+
* skill's analysis pattern with real DataHub data.
|
|
7
|
+
*
|
|
8
|
+
* Zero LLM cost — no API key needed. Calls tool.execute() directly
|
|
9
|
+
* in the exact order a skill prescribes.
|
|
10
|
+
*
|
|
11
|
+
* Scenarios:
|
|
12
|
+
* A: fin-a-share skill — individual A-share deep analysis (4 steps)
|
|
13
|
+
* B: fin-hk-hsi-pulse skill — HSI valuation pulse (3 steps)
|
|
14
|
+
* C: fin-us-equity skill — US stock earnings + options (3 steps)
|
|
15
|
+
* D: fin-crypto skill — crypto market overview (3 steps)
|
|
16
|
+
* E: fin-cross-asset skill — multi-asset correlation (4 steps)
|
|
17
|
+
* F: fin-a-quant-board skill — limit-up board + theme (3 steps)
|
|
18
|
+
*
|
|
19
|
+
* Run:
|
|
20
|
+
* npx vitest run extensions/findoo-datahub-plugin/test/e2e/l4-skill-tool-chain.test.ts
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
|
|
24
|
+
import { tmpdir } from "node:os";
|
|
25
|
+
import { join } from "node:path";
|
|
26
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
27
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
28
|
+
import findooDatahubPlugin from "../../index.js";
|
|
29
|
+
|
|
30
|
+
const SKIP = process.env.DATAHUB_SKIP_LIVE === "1";
|
|
31
|
+
const DEV_KEY = "98ffa5c5-1ec6-4735-8e0c-715a5eca1a8d";
|
|
32
|
+
|
|
33
|
+
/* ---------- helpers ---------- */
|
|
34
|
+
|
|
35
|
+
type ToolExecute = (id: string, params: Record<string, unknown>) => Promise<unknown>;
|
|
36
|
+
type ToolMap = Map<string, { execute: ToolExecute }>;
|
|
37
|
+
|
|
38
|
+
function createTestEnv(stateDir: string) {
|
|
39
|
+
const tools: ToolMap = new Map();
|
|
40
|
+
const services = new Map<string, unknown>();
|
|
41
|
+
const api = {
|
|
42
|
+
id: "findoo-datahub-plugin",
|
|
43
|
+
name: "findoo-datahub-plugin",
|
|
44
|
+
source: "test",
|
|
45
|
+
config: {},
|
|
46
|
+
pluginConfig: {
|
|
47
|
+
datahubApiKey: process.env.DATAHUB_API_KEY ?? process.env.DATAHUB_PASSWORD ?? DEV_KEY,
|
|
48
|
+
},
|
|
49
|
+
runtime: { version: "test", services },
|
|
50
|
+
logger: { info() {}, warn() {}, error() {}, debug() {} },
|
|
51
|
+
log() {},
|
|
52
|
+
registerTool(tool: { name: string; execute: ToolExecute }) {
|
|
53
|
+
tools.set(tool.name, tool);
|
|
54
|
+
},
|
|
55
|
+
registerHook() {},
|
|
56
|
+
registerHttpHandler() {},
|
|
57
|
+
registerHttpRoute() {},
|
|
58
|
+
registerChannel() {},
|
|
59
|
+
registerGatewayMethod() {},
|
|
60
|
+
registerCli() {},
|
|
61
|
+
registerService(svc: { id: string; instance: unknown }) {
|
|
62
|
+
services.set(svc.id, svc.instance);
|
|
63
|
+
},
|
|
64
|
+
registerProvider() {},
|
|
65
|
+
registerCommand() {},
|
|
66
|
+
resolvePath: (p: string) => {
|
|
67
|
+
const full = join(stateDir, p);
|
|
68
|
+
mkdirSync(join(full, ".."), { recursive: true });
|
|
69
|
+
return full;
|
|
70
|
+
},
|
|
71
|
+
on() {},
|
|
72
|
+
} as unknown as OpenClawPluginApi;
|
|
73
|
+
return { api, tools, services };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function parse(result: unknown): Record<string, unknown> {
|
|
77
|
+
const res = result as { content: Array<{ text: string }> };
|
|
78
|
+
return JSON.parse(res.content[0]!.text);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Asserts a tool call succeeded and returned data */
|
|
82
|
+
function assertSuccess(res: Record<string, unknown>, minCount = 0) {
|
|
83
|
+
expect(res.error, `Tool error: ${res.error}`).toBeUndefined();
|
|
84
|
+
if (res.success !== undefined) expect(res.success).toBe(true);
|
|
85
|
+
if (minCount > 0) expect(res.count as number).toBeGreaterThanOrEqual(minCount);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* ---------- tests ---------- */
|
|
89
|
+
|
|
90
|
+
describe.skipIf(SKIP)(
|
|
91
|
+
"L4 — Skill-driven Tool Chain E2E",
|
|
92
|
+
{ timeout: 180_000 },
|
|
93
|
+
() => {
|
|
94
|
+
let tempDir: string;
|
|
95
|
+
let tools: ToolMap;
|
|
96
|
+
|
|
97
|
+
beforeAll(async () => {
|
|
98
|
+
tempDir = mkdtempSync(join(tmpdir(), "l4-chain-"));
|
|
99
|
+
const env = createTestEnv(tempDir);
|
|
100
|
+
tools = env.tools;
|
|
101
|
+
await findooDatahubPlugin.register(env.api);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
afterAll(() => {
|
|
105
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ═══════════════════════════════════════════════════════════════
|
|
109
|
+
// Scenario A: fin-a-share — 个股全景分析 (600519.SH 茅台)
|
|
110
|
+
// Skill pattern: price → fundamentals → ownership → TA → regime
|
|
111
|
+
// ═══════════════════════════════════════════════════════════════
|
|
112
|
+
|
|
113
|
+
describe("A: fin-a-share — A-share deep analysis (茅台)", () => {
|
|
114
|
+
let priceData: Record<string, unknown>;
|
|
115
|
+
let fundamentals: Record<string, unknown>;
|
|
116
|
+
let regime: Record<string, unknown>;
|
|
117
|
+
|
|
118
|
+
it("A.1 fin_stock(price/historical) — get price data", async () => {
|
|
119
|
+
const res = parse(
|
|
120
|
+
await tools.get("fin_stock")!.execute("a1", {
|
|
121
|
+
symbol: "600519.SH",
|
|
122
|
+
endpoint: "price/historical",
|
|
123
|
+
limit: 30,
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
assertSuccess(res, 1);
|
|
127
|
+
priceData = res;
|
|
128
|
+
const rows = res.results as Array<Record<string, unknown>>;
|
|
129
|
+
expect(rows[0]).toHaveProperty("close");
|
|
130
|
+
expect(Number(rows[0]!.close)).toBeGreaterThan(100);
|
|
131
|
+
}, 30_000);
|
|
132
|
+
|
|
133
|
+
it("A.2 fin_stock(fundamental/income) — get financials", async () => {
|
|
134
|
+
const res = parse(
|
|
135
|
+
await tools.get("fin_stock")!.execute("a2", {
|
|
136
|
+
symbol: "600519.SH",
|
|
137
|
+
endpoint: "fundamental/income",
|
|
138
|
+
limit: 4,
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
assertSuccess(res, 1);
|
|
142
|
+
fundamentals = res;
|
|
143
|
+
}, 30_000);
|
|
144
|
+
|
|
145
|
+
it("A.3 fin_ta(rsi) — technical overlay", async () => {
|
|
146
|
+
const res = parse(
|
|
147
|
+
await tools.get("fin_ta")!.execute("a3", {
|
|
148
|
+
symbol: "600519.SH",
|
|
149
|
+
indicator: "rsi",
|
|
150
|
+
period: 14,
|
|
151
|
+
limit: 30,
|
|
152
|
+
}),
|
|
153
|
+
);
|
|
154
|
+
assertSuccess(res, 1);
|
|
155
|
+
expect(res.endpoint).toBe("ta/rsi");
|
|
156
|
+
}, 30_000);
|
|
157
|
+
|
|
158
|
+
it("A.4 fin_data_regime — trend context", async () => {
|
|
159
|
+
const res = parse(
|
|
160
|
+
await tools.get("fin_data_regime")!.execute("a4", {
|
|
161
|
+
symbol: "600519.SH",
|
|
162
|
+
market: "equity",
|
|
163
|
+
timeframe: "1d",
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
expect(res.error).toBeUndefined();
|
|
167
|
+
expect(["bull", "bear", "sideways", "volatile", "crisis"]).toContain(res.regime);
|
|
168
|
+
regime = res;
|
|
169
|
+
}, 30_000);
|
|
170
|
+
|
|
171
|
+
it("A.5 chain produces complete analysis inputs", () => {
|
|
172
|
+
// Verify all 4 steps produced data that an LLM could synthesize
|
|
173
|
+
expect(priceData.count as number).toBeGreaterThan(0);
|
|
174
|
+
expect(fundamentals.count as number).toBeGreaterThan(0);
|
|
175
|
+
expect(regime.regime).toBeDefined();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// ═══════════════════════════════════════════════════════════════
|
|
180
|
+
// Scenario B: fin-hk-hsi-pulse — 恒指估值脉搏
|
|
181
|
+
// Skill pattern: index valuation → HIBOR rate → regime
|
|
182
|
+
// ═══════════════════════════════════════════════════════════════
|
|
183
|
+
|
|
184
|
+
describe("B: fin-hk-hsi-pulse — HSI valuation pulse", () => {
|
|
185
|
+
let valuation: Record<string, unknown>;
|
|
186
|
+
let hibor: Record<string, unknown>;
|
|
187
|
+
let regime: Record<string, unknown>;
|
|
188
|
+
|
|
189
|
+
it("B.1 fin_index(daily_basic) — PE/PB percentile data", async () => {
|
|
190
|
+
const res = parse(
|
|
191
|
+
await tools.get("fin_index")!.execute("b1", {
|
|
192
|
+
symbol: "HSI",
|
|
193
|
+
endpoint: "daily_basic",
|
|
194
|
+
limit: 100,
|
|
195
|
+
}),
|
|
196
|
+
);
|
|
197
|
+
assertSuccess(res);
|
|
198
|
+
valuation = res;
|
|
199
|
+
}, 30_000);
|
|
200
|
+
|
|
201
|
+
it("B.2 fin_macro(hibor) — risk-free rate for ERP calc", async () => {
|
|
202
|
+
const res = parse(
|
|
203
|
+
await tools.get("fin_macro")!.execute("b2", {
|
|
204
|
+
endpoint: "hibor",
|
|
205
|
+
limit: 10,
|
|
206
|
+
}),
|
|
207
|
+
);
|
|
208
|
+
assertSuccess(res, 1);
|
|
209
|
+
hibor = res;
|
|
210
|
+
}, 30_000);
|
|
211
|
+
|
|
212
|
+
it("B.3 fin_data_regime(HSI) — trend overlay", async () => {
|
|
213
|
+
// HSI may not have OHLCV via the standard equity path, but we test the flow
|
|
214
|
+
try {
|
|
215
|
+
const res = parse(
|
|
216
|
+
await tools.get("fin_data_regime")!.execute("b3", {
|
|
217
|
+
symbol: "HSI",
|
|
218
|
+
market: "equity",
|
|
219
|
+
timeframe: "1d",
|
|
220
|
+
}),
|
|
221
|
+
);
|
|
222
|
+
if (!res.error) {
|
|
223
|
+
expect(["bull", "bear", "sideways", "volatile", "crisis"]).toContain(res.regime);
|
|
224
|
+
}
|
|
225
|
+
regime = res;
|
|
226
|
+
} catch {
|
|
227
|
+
// HSI index may not have OHLCV data in the same format
|
|
228
|
+
regime = { regime: "unavailable" };
|
|
229
|
+
}
|
|
230
|
+
}, 30_000);
|
|
231
|
+
|
|
232
|
+
it("B.4 chain produces ERP calculation inputs", () => {
|
|
233
|
+
// Verify we have the data needed for ERP = 1/PE - HIBOR
|
|
234
|
+
expect(hibor.count as number).toBeGreaterThan(0);
|
|
235
|
+
// Valuation may be empty for HSI if not covered by tushare
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// ═══════════════════════════════════════════════════════════════
|
|
240
|
+
// Scenario C: fin-us-equity — US stock analysis (AAPL)
|
|
241
|
+
// Skill pattern: earnings forecast → income → price → TA
|
|
242
|
+
// ═══════════════════════════════════════════════════════════════
|
|
243
|
+
|
|
244
|
+
describe("C: fin-us-equity — US stock analysis (AAPL)", () => {
|
|
245
|
+
it("C.1 fin_stock(fundamental/earnings_forecast) — consensus EPS", async () => {
|
|
246
|
+
const res = parse(
|
|
247
|
+
await tools.get("fin_stock")!.execute("c1", {
|
|
248
|
+
symbol: "AAPL",
|
|
249
|
+
endpoint: "fundamental/earnings_forecast",
|
|
250
|
+
limit: 5,
|
|
251
|
+
}),
|
|
252
|
+
);
|
|
253
|
+
// tushare may have limited US coverage
|
|
254
|
+
assertSuccess(res);
|
|
255
|
+
}, 30_000);
|
|
256
|
+
|
|
257
|
+
it("C.2 fin_stock(us/income) — GAAP financials", async () => {
|
|
258
|
+
const res = parse(
|
|
259
|
+
await tools.get("fin_stock")!.execute("c2", {
|
|
260
|
+
symbol: "AAPL",
|
|
261
|
+
endpoint: "us/income",
|
|
262
|
+
limit: 4,
|
|
263
|
+
}),
|
|
264
|
+
);
|
|
265
|
+
assertSuccess(res);
|
|
266
|
+
}, 30_000);
|
|
267
|
+
|
|
268
|
+
it("C.3 fin_macro(treasury_us) — risk-free rate for DCF", async () => {
|
|
269
|
+
const res = parse(
|
|
270
|
+
await tools.get("fin_macro")!.execute("c3", {
|
|
271
|
+
endpoint: "treasury_us",
|
|
272
|
+
limit: 5,
|
|
273
|
+
}),
|
|
274
|
+
);
|
|
275
|
+
assertSuccess(res, 1);
|
|
276
|
+
}, 30_000);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// ═══════════════════════════════════════════════════════════════
|
|
280
|
+
// Scenario D: fin-crypto — Crypto market overview
|
|
281
|
+
// Skill pattern: market overview → DeFi TVL → stablecoin flow
|
|
282
|
+
// ═══════════════════════════════════════════════════════════════
|
|
283
|
+
|
|
284
|
+
describe("D: fin-crypto — Crypto market overview", () => {
|
|
285
|
+
it("D.1 fin_crypto(coin/market) — top coins", async () => {
|
|
286
|
+
const res = parse(
|
|
287
|
+
await tools.get("fin_crypto")!.execute("d1", {
|
|
288
|
+
endpoint: "coin/market",
|
|
289
|
+
limit: 10,
|
|
290
|
+
}),
|
|
291
|
+
);
|
|
292
|
+
assertSuccess(res, 1);
|
|
293
|
+
}, 30_000);
|
|
294
|
+
|
|
295
|
+
it("D.2 fin_crypto(defi/protocols) — DeFi TVL ranking", async () => {
|
|
296
|
+
const res = parse(
|
|
297
|
+
await tools.get("fin_crypto")!.execute("d2", {
|
|
298
|
+
endpoint: "defi/protocols",
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
301
|
+
assertSuccess(res, 1);
|
|
302
|
+
}, 30_000);
|
|
303
|
+
|
|
304
|
+
it("D.3 fin_crypto(defi/stablecoins) — stablecoin supply", async () => {
|
|
305
|
+
const res = parse(
|
|
306
|
+
await tools.get("fin_crypto")!.execute("d3", {
|
|
307
|
+
endpoint: "defi/stablecoins",
|
|
308
|
+
}),
|
|
309
|
+
);
|
|
310
|
+
assertSuccess(res);
|
|
311
|
+
}, 30_000);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// ═══════════════════════════════════════════════════════════════
|
|
315
|
+
// Scenario E: fin-cross-asset — Cross-asset correlation
|
|
316
|
+
// Skill pattern: macro rates → A-share index → US treasury → crypto
|
|
317
|
+
// ═══════════════════════════════════════════════════════════════
|
|
318
|
+
|
|
319
|
+
describe("E: fin-cross-asset — Multi-asset analysis", () => {
|
|
320
|
+
it("E.1 fin_macro(shibor) — China rate environment", async () => {
|
|
321
|
+
const res = parse(
|
|
322
|
+
await tools.get("fin_macro")!.execute("e1", {
|
|
323
|
+
endpoint: "shibor",
|
|
324
|
+
limit: 10,
|
|
325
|
+
}),
|
|
326
|
+
);
|
|
327
|
+
assertSuccess(res, 1);
|
|
328
|
+
}, 30_000);
|
|
329
|
+
|
|
330
|
+
it("E.2 fin_index(price/historical) — CSI 300 trend", async () => {
|
|
331
|
+
const res = parse(
|
|
332
|
+
await tools.get("fin_index")!.execute("e2", {
|
|
333
|
+
symbol: "000300.SH",
|
|
334
|
+
endpoint: "price/historical",
|
|
335
|
+
limit: 30,
|
|
336
|
+
}),
|
|
337
|
+
);
|
|
338
|
+
assertSuccess(res, 1);
|
|
339
|
+
}, 30_000);
|
|
340
|
+
|
|
341
|
+
it("E.3 fin_macro(treasury_us) — US 10Y for global anchor", async () => {
|
|
342
|
+
const res = parse(
|
|
343
|
+
await tools.get("fin_macro")!.execute("e3", {
|
|
344
|
+
endpoint: "treasury_us",
|
|
345
|
+
limit: 10,
|
|
346
|
+
}),
|
|
347
|
+
);
|
|
348
|
+
assertSuccess(res, 1);
|
|
349
|
+
}, 30_000);
|
|
350
|
+
|
|
351
|
+
it("E.4 fin_derivatives(futures/historical) — commodity context", async () => {
|
|
352
|
+
const res = parse(
|
|
353
|
+
await tools.get("fin_derivatives")!.execute("e4", {
|
|
354
|
+
symbol: "RB2501.SHF",
|
|
355
|
+
endpoint: "futures/historical",
|
|
356
|
+
limit: 10,
|
|
357
|
+
}),
|
|
358
|
+
);
|
|
359
|
+
assertSuccess(res, 1);
|
|
360
|
+
}, 30_000);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// ═══════════════════════════════════════════════════════════════
|
|
364
|
+
// Scenario F: fin-a-quant-board — 涨停板量化 + 题材跟踪
|
|
365
|
+
// Skill pattern: limit_list → top_list (龙虎榜) → ths_index (题材)
|
|
366
|
+
// ═══════════════════════════════════════════════════════════════
|
|
367
|
+
|
|
368
|
+
describe("F: fin-a-quant-board — Limit-up board + theme tracking", () => {
|
|
369
|
+
it("F.1 fin_market(market/limit_list) — limit-up/down stats", async () => {
|
|
370
|
+
const res = parse(
|
|
371
|
+
await tools.get("fin_market")!.execute("f1", {
|
|
372
|
+
endpoint: "market/limit_list",
|
|
373
|
+
trade_date: "2026-02-27",
|
|
374
|
+
}),
|
|
375
|
+
);
|
|
376
|
+
// May return error for non-trading days
|
|
377
|
+
if (!res.error) {
|
|
378
|
+
assertSuccess(res);
|
|
379
|
+
}
|
|
380
|
+
}, 30_000);
|
|
381
|
+
|
|
382
|
+
it("F.2 fin_market(market/top_list) — dragon-tiger list", async () => {
|
|
383
|
+
const res = parse(
|
|
384
|
+
await tools.get("fin_market")!.execute("f2", {
|
|
385
|
+
endpoint: "market/top_list",
|
|
386
|
+
trade_date: "2026-02-27",
|
|
387
|
+
}),
|
|
388
|
+
);
|
|
389
|
+
if (!res.error) {
|
|
390
|
+
assertSuccess(res);
|
|
391
|
+
}
|
|
392
|
+
}, 30_000);
|
|
393
|
+
|
|
394
|
+
it("F.3 fin_index(thematic/ths_index) — theme index list", async () => {
|
|
395
|
+
const res = parse(
|
|
396
|
+
await tools.get("fin_index")!.execute("f3", {
|
|
397
|
+
endpoint: "thematic/ths_index",
|
|
398
|
+
limit: 20,
|
|
399
|
+
}),
|
|
400
|
+
);
|
|
401
|
+
assertSuccess(res, 1);
|
|
402
|
+
}, 30_000);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// ═══════════════════════════════════════════════════════════════
|
|
406
|
+
// Scenario G: Error resilience in multi-step chains
|
|
407
|
+
// ═══════════════════════════════════════════════════════════════
|
|
408
|
+
|
|
409
|
+
describe("G: Error resilience", () => {
|
|
410
|
+
it("G.1 tool error doesn't crash — chain can continue", async () => {
|
|
411
|
+
// Step 1: Intentionally fail
|
|
412
|
+
const bad = parse(
|
|
413
|
+
await tools.get("fin_stock")!.execute("g1-bad", {
|
|
414
|
+
symbol: "NONEXISTENT_999",
|
|
415
|
+
endpoint: "price/historical",
|
|
416
|
+
limit: 5,
|
|
417
|
+
}),
|
|
418
|
+
);
|
|
419
|
+
// Should gracefully return (error or empty results)
|
|
420
|
+
const hasSomeResponse = bad.error !== undefined || bad.success !== undefined;
|
|
421
|
+
expect(hasSomeResponse).toBe(true);
|
|
422
|
+
|
|
423
|
+
// Step 2: Next tool in chain still works
|
|
424
|
+
const good = parse(
|
|
425
|
+
await tools.get("fin_macro")!.execute("g1-good", {
|
|
426
|
+
endpoint: "cpi",
|
|
427
|
+
limit: 3,
|
|
428
|
+
}),
|
|
429
|
+
);
|
|
430
|
+
assertSuccess(good, 1);
|
|
431
|
+
}, 30_000);
|
|
432
|
+
|
|
433
|
+
it("G.2 parallel tool calls produce independent results", async () => {
|
|
434
|
+
// Simulate LLM calling 3 tools in parallel (common pattern)
|
|
435
|
+
const [r1, r2, r3] = await Promise.all([
|
|
436
|
+
tools.get("fin_stock")!.execute("g2-1", {
|
|
437
|
+
symbol: "600519.SH",
|
|
438
|
+
endpoint: "price/historical",
|
|
439
|
+
limit: 5,
|
|
440
|
+
}),
|
|
441
|
+
tools.get("fin_macro")!.execute("g2-2", {
|
|
442
|
+
endpoint: "cpi",
|
|
443
|
+
limit: 5,
|
|
444
|
+
}),
|
|
445
|
+
tools.get("fin_crypto")!.execute("g2-3", {
|
|
446
|
+
endpoint: "coin/market",
|
|
447
|
+
limit: 5,
|
|
448
|
+
}),
|
|
449
|
+
]);
|
|
450
|
+
|
|
451
|
+
const p1 = parse(r1);
|
|
452
|
+
const p2 = parse(r2);
|
|
453
|
+
const p3 = parse(r3);
|
|
454
|
+
|
|
455
|
+
assertSuccess(p1, 1);
|
|
456
|
+
assertSuccess(p2, 1);
|
|
457
|
+
assertSuccess(p3, 1);
|
|
458
|
+
|
|
459
|
+
// Results are independent
|
|
460
|
+
expect(p1.endpoint).not.toBe(p2.endpoint);
|
|
461
|
+
expect(p2.endpoint).not.toBe(p3.endpoint);
|
|
462
|
+
}, 30_000);
|
|
463
|
+
});
|
|
464
|
+
},
|
|
465
|
+
);
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: fin-crypto-defi
|
|
3
|
-
description: "Crypto & DeFi data — CEX market data (tickers/orderbook/funding), DeFi protocol TVL/yields/stablecoins/DEX volumes, CoinGecko market cap/trending. All via DataHub."
|
|
4
|
-
metadata: { "openclaw": { "emoji": "🪙", "requires": { "extensions": ["findoo-datahub-plugin"] } } }
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Crypto & DeFi
|
|
8
|
-
|
|
9
|
-
Use the **fin_crypto** tool for cryptocurrency and DeFi analysis via DataHub (works out of the box). For simple OHLCV data, use **fin_data_ohlcv** instead.
|
|
10
|
-
|
|
11
|
-
## When to Use
|
|
12
|
-
|
|
13
|
-
- "BTC最新价格" / "Bitcoin ticker"
|
|
14
|
-
- "ETH永续资金费率" / "funding rate"
|
|
15
|
-
- "DeFi TVL排行" / "DeFi protocol ranking"
|
|
16
|
-
- "Aave收益率" / "DeFi yield opportunities"
|
|
17
|
-
- "USDT发行量" / "stablecoin market cap"
|
|
18
|
-
- "币圈热搜" / "trending coins"
|
|
19
|
-
|
|
20
|
-
## CEX Market Data
|
|
21
|
-
|
|
22
|
-
| endpoint | Description | Example |
|
|
23
|
-
| --------------------- | ---------------------- | --------------------------------------------------------------- |
|
|
24
|
-
| `market/ticker` | Single ticker snapshot | `fin_crypto(endpoint="market/ticker", symbol="BTC/USDT")` |
|
|
25
|
-
| `market/tickers` | All tickers | `fin_crypto(endpoint="market/tickers")` |
|
|
26
|
-
| `market/orderbook` | Order book depth | `fin_crypto(endpoint="market/orderbook", symbol="BTC/USDT")` |
|
|
27
|
-
| `market/trades` | Recent trades | `fin_crypto(endpoint="market/trades", symbol="BTC/USDT")` |
|
|
28
|
-
| `market/funding_rate` | Perpetual funding rate | `fin_crypto(endpoint="market/funding_rate", symbol="BTC/USDT")` |
|
|
29
|
-
|
|
30
|
-
## CoinGecko Market Intelligence
|
|
31
|
-
|
|
32
|
-
| endpoint | Description | Example |
|
|
33
|
-
| ------------------- | ---------------------- | ---------------------------------------------------------- |
|
|
34
|
-
| `coin/market` | Market cap ranking | `fin_crypto(endpoint="coin/market", limit=20)` |
|
|
35
|
-
| `coin/historical` | Coin historical data | `fin_crypto(endpoint="coin/historical", symbol="bitcoin")` |
|
|
36
|
-
| `coin/info` | Coin detail info | `fin_crypto(endpoint="coin/info", symbol="ethereum")` |
|
|
37
|
-
| `coin/categories` | Category rankings | `fin_crypto(endpoint="coin/categories")` |
|
|
38
|
-
| `coin/trending` | Trending / hot coins | `fin_crypto(endpoint="coin/trending")` |
|
|
39
|
-
| `coin/global_stats` | Global market overview | `fin_crypto(endpoint="coin/global_stats")` |
|
|
40
|
-
|
|
41
|
-
## DeFi Protocol Data (DefiLlama)
|
|
42
|
-
|
|
43
|
-
| endpoint | Description | Example |
|
|
44
|
-
| --------------------- | --------------------------- | ------------------------------------------------------------ |
|
|
45
|
-
| `defi/protocols` | Protocol TVL ranking | `fin_crypto(endpoint="defi/protocols", limit=20)` |
|
|
46
|
-
| `defi/tvl_historical` | Full TVL history | `fin_crypto(endpoint="defi/tvl_historical")` |
|
|
47
|
-
| `defi/protocol_tvl` | Single protocol TVL history | `fin_crypto(endpoint="defi/protocol_tvl", symbol="aave")` |
|
|
48
|
-
| `defi/chains` | Blockchain TVL comparison | `fin_crypto(endpoint="defi/chains")` |
|
|
49
|
-
| `defi/yields` | Yield farming opportunities | `fin_crypto(endpoint="defi/yields")` |
|
|
50
|
-
| `defi/stablecoins` | Stablecoin market data | `fin_crypto(endpoint="defi/stablecoins")` |
|
|
51
|
-
| `defi/fees` | Protocol fees/revenue | `fin_crypto(endpoint="defi/fees")` |
|
|
52
|
-
| `defi/dex_volumes` | DEX trading volumes | `fin_crypto(endpoint="defi/dex_volumes")` |
|
|
53
|
-
| `defi/coin_prices` | DeFi token prices | `fin_crypto(endpoint="defi/coin_prices", symbol="ethereum")` |
|
|
54
|
-
|
|
55
|
-
## Simple OHLCV (via CCXT)
|
|
56
|
-
|
|
57
|
-
Use **fin_data_ohlcv** for simple crypto candlestick data via CCXT:
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
fin_data_ohlcv(symbol="BTC/USDT", market="crypto", timeframe="1d")
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Market Overview Pattern
|
|
64
|
-
|
|
65
|
-
1. `fin_crypto(coin/global_stats)` — total market cap, BTC dominance
|
|
66
|
-
2. `fin_crypto(coin/market)` — top coins by market cap
|
|
67
|
-
3. `fin_crypto(coin/trending)` — what's hot
|
|
68
|
-
4. `fin_crypto(defi/protocols)` — DeFi TVL leaders
|
|
69
|
-
5. `fin_crypto(defi/chains)` — which chains are growing
|
package/skills/equity/skill.md
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: fin-equity
|
|
3
|
-
description: "Equity research — A-share, HK, US stock analysis, financials, money flow, holders, dividends, index/ETF/fund, Stock Connect flows. All via DataHub."
|
|
4
|
-
metadata: { "openclaw": { "emoji": "📊", "requires": { "extensions": ["findoo-datahub-plugin"] } } }
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Equity Research
|
|
8
|
-
|
|
9
|
-
Use **fin_stock**, **fin_index**, and **fin_market** tools for equity analysis across A-share, HK, and US markets. All data routes through DataHub (works out of the box).
|
|
10
|
-
|
|
11
|
-
## When to Use
|
|
12
|
-
|
|
13
|
-
- "茅台行情" / "贵州茅台最新股价" / "AAPL earnings"
|
|
14
|
-
- "腾讯港股今天行情" / "00700.HK daily"
|
|
15
|
-
- "沪深300成分股" / "CSI 300 constituents"
|
|
16
|
-
- "50ETF净值" / "ETF NAV"
|
|
17
|
-
- "北向资金" / "Stock Connect flows"
|
|
18
|
-
|
|
19
|
-
## Stock Data (fin_stock)
|
|
20
|
-
|
|
21
|
-
| endpoint | Description | Example |
|
|
22
|
-
| ------------------------- | --------------------- | ------------------------------------------------------------------- |
|
|
23
|
-
| `price/historical` | Historical OHLCV | `fin_stock(symbol="600519.SH", endpoint="price/historical")` |
|
|
24
|
-
| `fundamental/income` | Income statement | `fin_stock(symbol="600519.SH", endpoint="fundamental/income")` |
|
|
25
|
-
| `fundamental/balance` | Balance sheet | `fin_stock(symbol="600519.SH", endpoint="fundamental/balance")` |
|
|
26
|
-
| `fundamental/cash` | Cash flow statement | `fin_stock(symbol="AAPL", endpoint="fundamental/cash")` |
|
|
27
|
-
| `fundamental/ratios` | Financial ratios | `fin_stock(symbol="00700.HK", endpoint="fundamental/ratios")` |
|
|
28
|
-
| `fundamental/dividends` | Dividend history | `fin_stock(symbol="600519.SH", endpoint="fundamental/dividends")` |
|
|
29
|
-
| `ownership/top10_holders` | Top 10 shareholders | `fin_stock(symbol="600519.SH", endpoint="ownership/top10_holders")` |
|
|
30
|
-
| `moneyflow/individual` | Capital flow tracking | `fin_stock(symbol="600519.SH", endpoint="moneyflow/individual")` |
|
|
31
|
-
| `discovery/gainers` | Top gainers | `fin_stock(endpoint="discovery/gainers")` |
|
|
32
|
-
|
|
33
|
-
## Index / ETF / Fund (fin_index)
|
|
34
|
-
|
|
35
|
-
| endpoint | Description | Example |
|
|
36
|
-
| -------------------- | ------------------------ | -------------------------------------------------------------- |
|
|
37
|
-
| `price/historical` | Index daily data | `fin_index(symbol="000300.SH", endpoint="price/historical")` |
|
|
38
|
-
| `constituents` | Index constituent stocks | `fin_index(symbol="000300.SH", endpoint="constituents")` |
|
|
39
|
-
| `daily_basic` | Index PE/PB valuation | `fin_index(symbol="000300.SH", endpoint="daily_basic")` |
|
|
40
|
-
| `thematic/ths_index` | THS concept index list | `fin_index(endpoint="thematic/ths_index")` |
|
|
41
|
-
| `thematic/ths_daily` | THS concept daily data | `fin_index(symbol="885760.TI", endpoint="thematic/ths_daily")` |
|
|
42
|
-
|
|
43
|
-
## Cross-Border Flows (fin_market)
|
|
44
|
-
|
|
45
|
-
| endpoint | Description | Example |
|
|
46
|
-
| ----------------- | --------------------------------- | ----------------------------------------------------------------- |
|
|
47
|
-
| `flow/hsgt_flow` | Northbound/Southbound daily flows | `fin_market(endpoint="flow/hsgt_flow", start_date="2025-02-01")` |
|
|
48
|
-
| `flow/hsgt_top10` | Top 10 HSGT holdings | `fin_market(endpoint="flow/hsgt_top10", trade_date="2025-02-28")` |
|
|
49
|
-
|
|
50
|
-
## Symbol Format
|
|
51
|
-
|
|
52
|
-
- A-shares: `600519.SH` (Shanghai), `000001.SZ` (Shenzhen)
|
|
53
|
-
- HK stocks: `00700.HK`
|
|
54
|
-
- US stocks: `AAPL`
|
|
55
|
-
- Index: `000300.SH`, ETF: `510050.SH`
|
|
56
|
-
|
|
57
|
-
## Deep Analysis Pattern
|
|
58
|
-
|
|
59
|
-
1. `fin_stock(price/historical)` — price trend
|
|
60
|
-
2. `fin_stock(fundamental/income)` — profitability
|
|
61
|
-
3. `fin_stock(fundamental/cash)` — cash quality
|
|
62
|
-
4. `fin_stock(moneyflow/individual)` — institutional flow
|
|
63
|
-
5. `fin_stock(ownership/top10_holders)` — ownership changes
|
|
64
|
-
6. `fin_market(flow/hsgt_flow)` — cross-border capital
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: fin-market-radar
|
|
3
|
-
description: "Market monitoring — dragon-tiger list, limit-up/down stats, block trades, sector money flow, margin trading, global index snapshots, IPO calendar. All via DataHub."
|
|
4
|
-
metadata: { "openclaw": { "emoji": "📡", "requires": { "extensions": ["findoo-datahub-plugin"] } } }
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Market Radar
|
|
8
|
-
|
|
9
|
-
Use the **fin_market** tool for market-wide monitoring and anomaly detection via DataHub (works out of the box).
|
|
10
|
-
|
|
11
|
-
## When to Use
|
|
12
|
-
|
|
13
|
-
- "今天龙虎榜" / "dragon-tiger list"
|
|
14
|
-
- "涨停板有哪些" / "limit up stocks"
|
|
15
|
-
- "大宗交易" / "block trades today"
|
|
16
|
-
- "板块资金流向" / "sector money flow"
|
|
17
|
-
- "融资融券余额" / "margin balance"
|
|
18
|
-
- "北向资金" / "northbound flow"
|
|
19
|
-
- "全球指数" / "global index snapshot"
|
|
20
|
-
|
|
21
|
-
## Available Endpoints
|
|
22
|
-
|
|
23
|
-
| endpoint | Description | Key Params |
|
|
24
|
-
| ----------------------- | ------------------------------------ | ------------------------- |
|
|
25
|
-
| `market/top_list` | Dragon-tiger list (top movers) | `trade_date="2025-02-28"` |
|
|
26
|
-
| `market/top_inst` | Institutional trades on dragon-tiger | `trade_date` |
|
|
27
|
-
| `market/limit_list` | Limit-up/down stocks | `trade_date` |
|
|
28
|
-
| `market/suspend` | Trading suspensions | `trade_date` |
|
|
29
|
-
| `market/trade_calendar` | Exchange calendar | — |
|
|
30
|
-
| `moneyflow/individual` | Per-stock capital flow | `symbol` |
|
|
31
|
-
| `moneyflow/industry` | Sector capital flow | `trade_date` |
|
|
32
|
-
| `moneyflow/block_trade` | Block trade records | `trade_date` |
|
|
33
|
-
| `margin/summary` | Market margin summary | `trade_date` |
|
|
34
|
-
| `margin/detail` | Per-stock margin detail | `symbol` |
|
|
35
|
-
| `flow/hsgt_flow` | Northbound/Southbound flows | `start_date`, `end_date` |
|
|
36
|
-
| `flow/hsgt_top10` | Top HSGT holdings | `trade_date` |
|
|
37
|
-
| `discovery/gainers` | Top gainers | — |
|
|
38
|
-
| `discovery/losers` | Top losers | — |
|
|
39
|
-
| `discovery/active` | Most active | — |
|
|
40
|
-
| `discovery/new_share` | IPO calendar | — |
|
|
41
|
-
|
|
42
|
-
## Post-market Review Pattern
|
|
43
|
-
|
|
44
|
-
1. `fin_market(market/top_list)` — who made the dragon-tiger list
|
|
45
|
-
2. `fin_market(market/limit_list)` — limit-up/down count
|
|
46
|
-
3. `fin_market(margin/summary)` — margin trading changes
|
|
47
|
-
4. `fin_market(flow/hsgt_flow)` — northbound capital trend
|