@openfinclaw/findoo-datahub-plugin 2026.3.10 → 2026.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +27 -9
- package/openclaw.plugin.json +11 -18
- package/package.json +1 -1
- package/skills/derivatives/skill.md +4 -4
- package/skills/risk-monitor/skill.md +11 -11
- package/test/e2e/l3-gateway-bootstrap.live.test.ts +246 -231
- package/test/e2e/l4-skill-tool-chain.live.test.ts +362 -366
- package/test/e2e/l5-browser/data-freshness.live.test.ts +379 -0
- package/test/e2e/l5-browser/market-data-chat.live.test.ts +259 -0
- package/test/e2e/l5-browser/skills-registry.test.ts +282 -0
package/index.ts
CHANGED
|
@@ -46,23 +46,28 @@ const findooDatahubPlugin = {
|
|
|
46
46
|
// --- DataHub client ---
|
|
47
47
|
if (!config.datahubApiKey) {
|
|
48
48
|
api.log?.(
|
|
49
|
-
"
|
|
50
|
-
"findoo-datahub-plugin:
|
|
49
|
+
"error",
|
|
50
|
+
"findoo-datahub-plugin: API key is required. Set DATAHUB_API_KEY env var or configure in Control UI → Plugins → Findoo DataHub.",
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const client =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
const client = config.datahubApiKey
|
|
55
|
+
? new DataHubClient(
|
|
56
|
+
config.datahubApiUrl,
|
|
57
|
+
config.datahubUsername,
|
|
58
|
+
config.datahubApiKey,
|
|
59
|
+
config.requestTimeoutMs,
|
|
60
|
+
)
|
|
61
|
+
: null;
|
|
60
62
|
|
|
61
63
|
// --- Local cache + regime detector ---
|
|
62
64
|
const dbPath = api.resolvePath("state/findoo-ohlcv-cache.sqlite");
|
|
63
65
|
const cache = new OHLCVCache(dbPath);
|
|
64
66
|
const regimeDetector = new RegimeDetector();
|
|
65
67
|
|
|
68
|
+
const NO_KEY_ERROR =
|
|
69
|
+
"DataHub API key not configured. Set DATAHUB_API_KEY env var or configure in Control UI → Plugins → Findoo DataHub.";
|
|
70
|
+
|
|
66
71
|
// --- Data provider service (exposed to other extensions) ---
|
|
67
72
|
const dataProvider = {
|
|
68
73
|
async getOHLCV(params: {
|
|
@@ -72,6 +77,8 @@ const findooDatahubPlugin = {
|
|
|
72
77
|
since?: number;
|
|
73
78
|
limit?: number;
|
|
74
79
|
}) {
|
|
80
|
+
if (!client) throw new Error(NO_KEY_ERROR);
|
|
81
|
+
|
|
75
82
|
// Check cache first
|
|
76
83
|
const range = cache.getRange(params.symbol, params.market, params.timeframe);
|
|
77
84
|
if (range && params.since != null && params.limit != null) {
|
|
@@ -92,6 +99,7 @@ const findooDatahubPlugin = {
|
|
|
92
99
|
},
|
|
93
100
|
|
|
94
101
|
async getTicker(symbol: string, market: MarketType) {
|
|
102
|
+
if (!client) throw new Error(NO_KEY_ERROR);
|
|
95
103
|
return client.getTicker(symbol, market);
|
|
96
104
|
},
|
|
97
105
|
|
|
@@ -233,6 +241,7 @@ const findooDatahubPlugin = {
|
|
|
233
241
|
}),
|
|
234
242
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
235
243
|
try {
|
|
244
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
236
245
|
const endpoint = String(params.endpoint ?? "price/historical");
|
|
237
246
|
const qp = buildParams(params);
|
|
238
247
|
const results = await client.equity(endpoint, qp);
|
|
@@ -283,6 +292,7 @@ const findooDatahubPlugin = {
|
|
|
283
292
|
}),
|
|
284
293
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
285
294
|
try {
|
|
295
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
286
296
|
const endpoint = String(params.endpoint ?? "price/historical");
|
|
287
297
|
const qp = buildParams(params);
|
|
288
298
|
const results = await client.index(endpoint, qp);
|
|
@@ -351,6 +361,7 @@ const findooDatahubPlugin = {
|
|
|
351
361
|
}),
|
|
352
362
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
353
363
|
try {
|
|
364
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
354
365
|
const endpoint = String(params.endpoint ?? "cpi");
|
|
355
366
|
const qp = buildParams(params);
|
|
356
367
|
// Route currency/* and fixedincome/* endpoints to their categories
|
|
@@ -416,6 +427,7 @@ const findooDatahubPlugin = {
|
|
|
416
427
|
}),
|
|
417
428
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
418
429
|
try {
|
|
430
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
419
431
|
const endpoint = String(params.endpoint ?? "futures/historical");
|
|
420
432
|
const qp = buildParams(params);
|
|
421
433
|
const results = await client.derivatives(endpoint, qp);
|
|
@@ -483,6 +495,7 @@ const findooDatahubPlugin = {
|
|
|
483
495
|
}),
|
|
484
496
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
485
497
|
try {
|
|
498
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
486
499
|
const endpoint = String(params.endpoint ?? "coin/market");
|
|
487
500
|
const qp = buildParams(params);
|
|
488
501
|
// Endpoint-specific param mapping for crypto APIs
|
|
@@ -570,6 +583,7 @@ const findooDatahubPlugin = {
|
|
|
570
583
|
}),
|
|
571
584
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
572
585
|
try {
|
|
586
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
573
587
|
const endpoint = String(params.endpoint ?? "market/top_list");
|
|
574
588
|
const qp = buildParams(params);
|
|
575
589
|
// Auto-alias: trade_date→date for endpoints that use 'date' param
|
|
@@ -618,6 +632,7 @@ const findooDatahubPlugin = {
|
|
|
618
632
|
}),
|
|
619
633
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
620
634
|
try {
|
|
635
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
621
636
|
const path = String(params.path ?? "").trim();
|
|
622
637
|
if (!path) throw new Error("path is required");
|
|
623
638
|
const qp = (params.params ?? {}) as Record<string, string>;
|
|
@@ -766,6 +781,7 @@ const findooDatahubPlugin = {
|
|
|
766
781
|
}),
|
|
767
782
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
768
783
|
try {
|
|
784
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
769
785
|
const indicator = String(params.indicator ?? "sma");
|
|
770
786
|
const qp: Record<string, string> = {};
|
|
771
787
|
if (params.symbol) qp.symbol = String(params.symbol);
|
|
@@ -830,6 +846,7 @@ const findooDatahubPlugin = {
|
|
|
830
846
|
}),
|
|
831
847
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
832
848
|
try {
|
|
849
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
833
850
|
const endpoint = String(params.endpoint ?? "info");
|
|
834
851
|
const qp = buildParams(params);
|
|
835
852
|
const results = await client.etf(endpoint, qp);
|
|
@@ -871,6 +888,7 @@ const findooDatahubPlugin = {
|
|
|
871
888
|
}),
|
|
872
889
|
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
873
890
|
try {
|
|
891
|
+
if (!client) return json({ error: NO_KEY_ERROR });
|
|
874
892
|
const endpoint = String(params.endpoint ?? "price/historical");
|
|
875
893
|
const qp = buildParams(params);
|
|
876
894
|
// Route news/* to the generic query path
|
|
@@ -902,7 +920,7 @@ const findooDatahubPlugin = {
|
|
|
902
920
|
parameters: Type.Object({}),
|
|
903
921
|
async execute() {
|
|
904
922
|
return json({
|
|
905
|
-
|
|
923
|
+
connected: !!client,
|
|
906
924
|
markets: dataProvider.getSupportedMarkets(),
|
|
907
925
|
categories: [
|
|
908
926
|
"equity",
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,34 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "findoo-datahub-plugin",
|
|
3
3
|
"name": "Findoo DataHub",
|
|
4
|
-
"description": "Unified financial data source — 172 endpoints covering equity (A/HK/US), crypto, macro, derivatives, index, ETF
|
|
4
|
+
"description": "Unified financial data source — 172 endpoints covering equity (A/HK/US), crypto, macro, derivatives, index, ETF. Works out of the box.",
|
|
5
5
|
"kind": "financial",
|
|
6
6
|
"version": "2026.3.2",
|
|
7
7
|
"skills": ["./skills"],
|
|
8
8
|
"configSchema": {
|
|
9
9
|
"type": "object",
|
|
10
10
|
"properties": {
|
|
11
|
-
"datahubApiUrl": {
|
|
12
|
-
"type": "string",
|
|
13
|
-
"default": "http://43.134.61.136:8088",
|
|
14
|
-
"description": "DataHub REST API base URL. Change only for self-hosted OpenBB instances"
|
|
15
|
-
},
|
|
16
|
-
"datahubUsername": {
|
|
17
|
-
"type": "string",
|
|
18
|
-
"default": "admin",
|
|
19
|
-
"description": "DataHub HTTP Basic Auth username"
|
|
20
|
-
},
|
|
21
11
|
"datahubApiKey": {
|
|
22
12
|
"type": "string",
|
|
23
|
-
"
|
|
13
|
+
"minLength": 1,
|
|
14
|
+
"description": "DataHub API key (required). Also accepted via DATAHUB_API_KEY env var.",
|
|
24
15
|
"sensitive": true
|
|
25
|
-
},
|
|
26
|
-
"requestTimeoutMs": {
|
|
27
|
-
"type": "number",
|
|
28
|
-
"default": 30000,
|
|
29
|
-
"minimum": 1000,
|
|
30
|
-
"maximum": 120000
|
|
31
16
|
}
|
|
32
17
|
}
|
|
18
|
+
},
|
|
19
|
+
"uiHints": {
|
|
20
|
+
"datahubApiKey": {
|
|
21
|
+
"label": "API Key",
|
|
22
|
+
"help": "DataHub API 密钥(必填)。",
|
|
23
|
+
"sensitive": true,
|
|
24
|
+
"placeholder": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
25
|
+
}
|
|
33
26
|
}
|
|
34
27
|
}
|
package/package.json
CHANGED
|
@@ -101,10 +101,10 @@ fin_derivatives(futures/holding) → 主力持仓方向
|
|
|
101
101
|
- US: `fin_derivatives(endpoint="options/chains", symbol="SPY")` → 直接取返回的 IV 字段 → 与 VIX 历史对比
|
|
102
102
|
- ⚠️ A 股标的有限 (50ETF/300ETF/科创50ETF 等),个股期权几乎不存在
|
|
103
103
|
|
|
104
|
-
| IV 分位 | 看涨 | 看跌 | 无方向
|
|
105
|
-
| ----------- | -------------------------- | -------------------------- |
|
|
106
|
-
| < 30th (低) | 买 Call / Bull Call Spread | 买 Put / Bear Put Spread | Long Straddle/Strangle
|
|
107
|
-
| 30-70th | Bull Call Spread | Bear Put Spread | Iron Condor
|
|
104
|
+
| IV 分位 | 看涨 | 看跌 | 无方向 |
|
|
105
|
+
| ----------- | -------------------------- | -------------------------- | ------------------------------------- |
|
|
106
|
+
| < 30th (低) | 买 Call / Bull Call Spread | 买 Put / Bear Put Spread | Long Straddle/Strangle |
|
|
107
|
+
| 30-70th | Bull Call Spread | Bear Put Spread | Iron Condor |
|
|
108
108
|
| > 70th (高) | 卖 Put / Bull Put Spread | 卖 Call / Bear Call Spread | Short Straddle/Strangle ⚠️ 必须设止损 |
|
|
109
109
|
|
|
110
110
|
### 常用策略参数
|
|
@@ -112,20 +112,20 @@ Credit_stress = 0.4*shibor_score + 0.3*curve_score + 0.3*margin_score
|
|
|
112
112
|
|
|
113
113
|
## 风险传导链模型
|
|
114
114
|
|
|
115
|
-
| 路径
|
|
116
|
-
|
|
117
|
-
| **A 外部冲击**
|
|
118
|
-
| **B 去杠杆**
|
|
119
|
-
| **C 流动性危机** | Shibor飙升(`shibor`) → 债券抛售(`treasury_cn`) → 利差异常 → 股债双杀
|
|
115
|
+
| 路径 | 链路 | 频率 | 升级触发 |
|
|
116
|
+
| ---------------- | ----------------------------------------------------------------------------------------------------- | ---- | ------------ |
|
|
117
|
+
| **A 外部冲击** | 美债↑(`treasury_us`) → CNH贬(`USDCNH`) → 北向流出(`hsgt_flow`) → 大宗联动(AU/SC) → A股承压 | 日频 | 10Y日变>10bp |
|
|
118
|
+
| **B 去杠杆** | 监管收紧 → 融资降(`margin/summary`) → 杠杆平仓(`margin/trading`) → 质押风险(`pledge/stat`) → 连锁跌停 | 周频 | 融资连降3日 |
|
|
119
|
+
| **C 流动性危机** | Shibor飙升(`shibor`) → 债券抛售(`treasury_cn`) → 利差异常 → 股债双杀 | 日频 | O/N>3% |
|
|
120
120
|
|
|
121
121
|
## 风险情景预案
|
|
122
122
|
|
|
123
|
-
| 情景
|
|
124
|
-
|
|
125
|
-
| **A 美联储冲击** | 10Y 5日升>25bp + USDCNH周升>0.8% + 北向3日出>150亿 | 减成长至30%,增配高股息,IF空头对冲
|
|
126
|
-
| **B 信用事件**
|
|
127
|
-
| **C 地缘冲突**
|
|
128
|
-
| **D 钱荒**
|
|
123
|
+
| 情景 | 触发条件 | 行动 |
|
|
124
|
+
| ---------------- | -------------------------------------------------- | ---------------------------------------------- |
|
|
125
|
+
| **A 美联储冲击** | 10Y 5日升>25bp + USDCNH周升>0.8% + 北向3日出>150亿 | 减成长至30%,增配高股息,IF空头对冲 |
|
|
126
|
+
| **B 信用事件** | 融资10日降>8% + O/N>3.5% + 银行地产跌停>15 | 清仓金融地产,仓位<30%,增配国债ETF(511010.SH) |
|
|
127
|
+
| **C 地缘冲突** | AU日涨>2% + SC日涨>5% + USDCNH 3日升>1% | 仓位30%,加配黄金/现金/短期国债 |
|
|
128
|
+
| **D 钱荒** | O/N>5% + 大面积强平 + 跌停>200 + 10Y-1Y<10bp | 全清仓转现金。恢复: O/N<2.5% + 跌停<50 |
|
|
129
129
|
|
|
130
130
|
## 尾部风险预警 (Perfect Storm)
|
|
131
131
|
|