@openfinclaw/findoo-datahub-plugin 2026.3.2
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 +234 -0
- package/index.ts +608 -0
- package/openclaw.plugin.json +34 -0
- package/package.json +32 -0
- package/skills/crypto-defi/skill.md +69 -0
- package/skills/data-query/skill.md +56 -0
- package/skills/derivatives/skill.md +53 -0
- package/skills/equity/skill.md +64 -0
- package/skills/macro/skill.md +60 -0
- package/skills/market-radar/skill.md +47 -0
- package/src/adapters/crypto-adapter.ts +103 -0
- package/src/adapters/equity-adapter.ts +11 -0
- package/src/adapters/yahoo-adapter.ts +110 -0
- package/src/config.ts +51 -0
- package/src/datahub-client.test.ts +544 -0
- package/src/datahub-client.ts +207 -0
- package/src/integration.live.test.ts +589 -0
- package/src/ohlcv-cache.ts +118 -0
- package/src/regime-detector.ts +73 -0
- package/src/types.ts +31 -0
- package/src/unified-provider.ts +123 -0
|
@@ -0,0 +1,69 @@
|
|
|
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
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fin-data-query
|
|
3
|
+
description: "Generic DataHub query — directly call any of 172 financial data endpoints by path. Fallback when specialized tools don't cover the data need."
|
|
4
|
+
metadata: { "openclaw": { "emoji": "🔍", "requires": { "extensions": ["findoo-datahub-plugin"] } } }
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Data Query (Fallback)
|
|
8
|
+
|
|
9
|
+
Use the **fin_query** tool as a generic fallback to access any of the 172 DataHub endpoints directly (works out of the box).
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- When specialized tools (fin_stock, fin_index, fin_macro, fin_derivatives, fin_crypto, fin_market) don't cover the specific data need
|
|
14
|
+
- When querying less common endpoints
|
|
15
|
+
|
|
16
|
+
## DataHub Categories
|
|
17
|
+
|
|
18
|
+
| Category | Endpoints | Coverage |
|
|
19
|
+
| --------------- | --------- | ------------------------------------------------------------- |
|
|
20
|
+
| `equity/*` | 83 | A-share, HK, US — prices, fundamentals, ownership, money flow |
|
|
21
|
+
| `crypto/*` | 23 | CEX market data, CoinGecko, DeFi via DefiLlama |
|
|
22
|
+
| `economy/*` | 21 | China macro, rates, FX, World Bank |
|
|
23
|
+
| `derivatives/*` | 13 | Futures, options, convertible bonds |
|
|
24
|
+
| `index/*` | 12 | Index data, thematic indices |
|
|
25
|
+
| `etf/*` | 9 | ETF prices, NAV, fund data |
|
|
26
|
+
| `currency/*` | 3 | FX historical, search, snapshots |
|
|
27
|
+
| `news/*` | 1 | Company news |
|
|
28
|
+
|
|
29
|
+
## Example Calls
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
# ETF fund manager info
|
|
33
|
+
fin_query(path="etf/fund/manager", params={"symbol": "110011"})
|
|
34
|
+
|
|
35
|
+
# Currency historical
|
|
36
|
+
fin_query(path="currency/price/historical", params={"symbol": "USDCNH"})
|
|
37
|
+
|
|
38
|
+
# Company news
|
|
39
|
+
fin_query(path="news/company", params={"symbol": "AAPL"})
|
|
40
|
+
|
|
41
|
+
# Coverage metadata — see all available endpoints
|
|
42
|
+
fin_query(path="coverage/providers")
|
|
43
|
+
fin_query(path="coverage/commands")
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## When to Prefer Specialized Tools
|
|
47
|
+
|
|
48
|
+
| Data Need | Use Instead |
|
|
49
|
+
| --------------------------- | ----------------- |
|
|
50
|
+
| Stock quote / financials | `fin_stock` |
|
|
51
|
+
| Index / ETF / Fund | `fin_index` |
|
|
52
|
+
| GDP / CPI / interest rates | `fin_macro` |
|
|
53
|
+
| Futures / options / CB | `fin_derivatives` |
|
|
54
|
+
| Crypto / DeFi | `fin_crypto` |
|
|
55
|
+
| Dragon-tiger / market radar | `fin_market` |
|
|
56
|
+
| Simple OHLCV (CCXT/Yahoo) | `fin_data_ohlcv` |
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fin-derivatives
|
|
3
|
+
description: "Derivatives analysis — futures (holdings/settlement/warehouse/term structure), options (chains/Greeks/IV), convertible bonds. All via DataHub."
|
|
4
|
+
metadata: { "openclaw": { "emoji": "📉", "requires": { "extensions": ["findoo-datahub-plugin"] } } }
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Derivatives Analysis
|
|
8
|
+
|
|
9
|
+
Use the **fin_derivatives** tool for futures, options, and convertible bond analysis via DataHub (works out of the box).
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- "螺纹钢期货持仓" / "rebar futures holding"
|
|
14
|
+
- "IF2501 结算价" / "futures settlement"
|
|
15
|
+
- "铜仓单变化" / "warehouse receipts"
|
|
16
|
+
- "AAPL期权链" / "option chains with Greeks"
|
|
17
|
+
- "可转债转股溢价率" / "CB conversion premium"
|
|
18
|
+
|
|
19
|
+
## Available Endpoints
|
|
20
|
+
|
|
21
|
+
### Futures
|
|
22
|
+
|
|
23
|
+
| endpoint | Description | Example |
|
|
24
|
+
| -------------------- | ------------------------ | ------------------------------------------------------------------------------------------- |
|
|
25
|
+
| `futures/historical` | Futures historical OHLCV | `fin_derivatives(symbol="RB2501.SHF", endpoint="futures/historical")` |
|
|
26
|
+
| `futures/info` | Contract specification | `fin_derivatives(symbol="RB2501.SHF", endpoint="futures/info")` |
|
|
27
|
+
| `futures/holding` | Position ranking | `fin_derivatives(symbol="RB2501.SHF", endpoint="futures/holding", trade_date="2025-02-28")` |
|
|
28
|
+
| `futures/settle` | Daily settlement | `fin_derivatives(symbol="RB2501.SHF", endpoint="futures/settle")` |
|
|
29
|
+
| `futures/warehouse` | Warehouse receipts | `fin_derivatives(symbol="RB.SHF", endpoint="futures/warehouse")` |
|
|
30
|
+
| `futures/mapping` | Active contract mapping | `fin_derivatives(symbol="RB.SHF", endpoint="futures/mapping")` |
|
|
31
|
+
|
|
32
|
+
### Options
|
|
33
|
+
|
|
34
|
+
| endpoint | Description | Example |
|
|
35
|
+
| ---------------- | ------------------------- | ----------------------------------------------------------------- |
|
|
36
|
+
| `options/basic` | Option contract list | `fin_derivatives(symbol="510050.SH", endpoint="options/basic")` |
|
|
37
|
+
| `options/daily` | Option daily prices | `fin_derivatives(symbol="10004537.SH", endpoint="options/daily")` |
|
|
38
|
+
| `options/chains` | Option chains with Greeks | `fin_derivatives(symbol="AAPL", endpoint="options/chains")` |
|
|
39
|
+
|
|
40
|
+
### Convertible Bonds
|
|
41
|
+
|
|
42
|
+
| endpoint | Description | Example |
|
|
43
|
+
| ------------------- | --------------- | ------------------------------------------------------------------- |
|
|
44
|
+
| `convertible/basic` | CB basic info | `fin_derivatives(symbol="113xxx.SH", endpoint="convertible/basic")` |
|
|
45
|
+
| `convertible/daily` | CB daily prices | `fin_derivatives(symbol="113xxx.SH", endpoint="convertible/daily")` |
|
|
46
|
+
|
|
47
|
+
## Futures Analysis Pattern
|
|
48
|
+
|
|
49
|
+
1. `fin_derivatives(futures/info)` — contract specification
|
|
50
|
+
2. `fin_derivatives(futures/historical)` — price trend
|
|
51
|
+
3. `fin_derivatives(futures/holding)` — major institution positions
|
|
52
|
+
4. `fin_derivatives(futures/settle)` — settlement and open interest
|
|
53
|
+
5. `fin_derivatives(futures/warehouse)` — warehouse receipts (supply signal)
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fin-macro
|
|
3
|
+
description: "Macro economics and interest rates — China GDP/CPI/PPI/PMI/M2, global rates (Shibor/LPR/Libor/Treasury), World Bank data, FX rates. All via DataHub."
|
|
4
|
+
metadata: { "openclaw": { "emoji": "🏛️", "requires": { "extensions": ["findoo-datahub-plugin"] } } }
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Macro & Rates
|
|
8
|
+
|
|
9
|
+
Use the **fin_macro** tool for macroeconomic indicators and interest rate data via DataHub (works out of the box).
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- "中国最新GDP" / "China GDP growth"
|
|
14
|
+
- "CPI数据" / "latest CPI"
|
|
15
|
+
- "Shibor利率" / "interbank rate"
|
|
16
|
+
- "LPR是多少" / "loan prime rate"
|
|
17
|
+
- "美国国债收益率" / "US Treasury yield"
|
|
18
|
+
- "世界银行GDP对比" / "World Bank comparison"
|
|
19
|
+
|
|
20
|
+
## Available Endpoints
|
|
21
|
+
|
|
22
|
+
### China Macro
|
|
23
|
+
|
|
24
|
+
| endpoint | Description | Example |
|
|
25
|
+
| ------------------ | --------------------- | ---------------------------------------- |
|
|
26
|
+
| `gdp/real` | China GDP | `fin_macro(endpoint="gdp/real")` |
|
|
27
|
+
| `cpi` | Consumer Price Index | `fin_macro(endpoint="cpi")` |
|
|
28
|
+
| `ppi` | Producer Price Index | `fin_macro(endpoint="ppi")` |
|
|
29
|
+
| `pmi` | Purchasing Managers | `fin_macro(endpoint="pmi")` |
|
|
30
|
+
| `money_supply` | Money supply M0/M1/M2 | `fin_macro(endpoint="money_supply")` |
|
|
31
|
+
| `social_financing` | Social financing | `fin_macro(endpoint="social_financing")` |
|
|
32
|
+
|
|
33
|
+
### Interest Rates
|
|
34
|
+
|
|
35
|
+
| endpoint | Description | Example |
|
|
36
|
+
| ------------- | ------------------------ | ----------------------------------- |
|
|
37
|
+
| `shibor` | Shanghai Interbank Rate | `fin_macro(endpoint="shibor")` |
|
|
38
|
+
| `shibor_lpr` | Loan Prime Rate | `fin_macro(endpoint="shibor_lpr")` |
|
|
39
|
+
| `libor` | London Interbank Rate | `fin_macro(endpoint="libor")` |
|
|
40
|
+
| `hibor` | Hong Kong Interbank Rate | `fin_macro(endpoint="hibor")` |
|
|
41
|
+
| `treasury_cn` | China treasury yields | `fin_macro(endpoint="treasury_cn")` |
|
|
42
|
+
| `treasury_us` | US treasury yields | `fin_macro(endpoint="treasury_us")` |
|
|
43
|
+
|
|
44
|
+
### Global (World Bank)
|
|
45
|
+
|
|
46
|
+
| endpoint | Description | Example |
|
|
47
|
+
| ---------------------- | --------------------- | ---------------------------------------------------------- |
|
|
48
|
+
| `worldbank/gdp` | World Bank GDP | `fin_macro(endpoint="worldbank/gdp", country="CN")` |
|
|
49
|
+
| `worldbank/population` | World Bank population | `fin_macro(endpoint="worldbank/population", country="US")` |
|
|
50
|
+
| `worldbank/inflation` | World Bank inflation | `fin_macro(endpoint="worldbank/inflation", country="CN")` |
|
|
51
|
+
| `worldbank/indicator` | Custom WB indicator | `fin_macro(endpoint="worldbank/indicator", country="CN")` |
|
|
52
|
+
|
|
53
|
+
## Macro Cycle Analysis Pattern
|
|
54
|
+
|
|
55
|
+
1. `fin_macro(gdp/real)` — growth trend
|
|
56
|
+
2. `fin_macro(cpi)` — inflation
|
|
57
|
+
3. `fin_macro(pmi)` — manufacturing activity
|
|
58
|
+
4. `fin_macro(shibor)` — liquidity conditions
|
|
59
|
+
5. `fin_macro(shibor_lpr)` — policy rate direction
|
|
60
|
+
6. `fin_macro(treasury_cn)` — bond market signal
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { OHLCVCache } from "../ohlcv-cache.js";
|
|
2
|
+
import type { OHLCV, Ticker } from "../types.js";
|
|
3
|
+
|
|
4
|
+
export type CcxtExchange = {
|
|
5
|
+
fetchTicker: (symbol: string) => Promise<Record<string, unknown>>;
|
|
6
|
+
fetchOHLCV: (
|
|
7
|
+
symbol: string,
|
|
8
|
+
timeframe: string,
|
|
9
|
+
since?: number,
|
|
10
|
+
limit?: number,
|
|
11
|
+
) => Promise<Array<[number, number, number, number, number, number]>>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export interface CryptoAdapter {
|
|
15
|
+
getOHLCV(params: {
|
|
16
|
+
symbol: string;
|
|
17
|
+
timeframe: string;
|
|
18
|
+
since?: number;
|
|
19
|
+
limit?: number;
|
|
20
|
+
exchangeId?: string;
|
|
21
|
+
}): Promise<OHLCV[]>;
|
|
22
|
+
getTicker(symbol: string, exchangeId?: string): Promise<Ticker>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createCryptoAdapter(
|
|
26
|
+
cache: OHLCVCache,
|
|
27
|
+
getExchangeInstance: (id?: string) => Promise<CcxtExchange>,
|
|
28
|
+
defaultExchangeId?: string,
|
|
29
|
+
): CryptoAdapter {
|
|
30
|
+
async function resolveExchange(exchangeId?: string): Promise<CcxtExchange> {
|
|
31
|
+
return getExchangeInstance(exchangeId ?? defaultExchangeId);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ccxtToOHLCV(raw: [number, number, number, number, number, number]): OHLCV {
|
|
35
|
+
return {
|
|
36
|
+
timestamp: raw[0],
|
|
37
|
+
open: raw[1],
|
|
38
|
+
high: raw[2],
|
|
39
|
+
low: raw[3],
|
|
40
|
+
close: raw[4],
|
|
41
|
+
volume: raw[5],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
async getOHLCV(params) {
|
|
47
|
+
const { symbol, timeframe, since, limit, exchangeId } = params;
|
|
48
|
+
const market = "crypto";
|
|
49
|
+
|
|
50
|
+
// Check cache for existing data
|
|
51
|
+
const range = cache.getRange(symbol, market, timeframe);
|
|
52
|
+
|
|
53
|
+
if (range) {
|
|
54
|
+
// If since + limit are specified and we have enough data in cache, return cached
|
|
55
|
+
if (since != null && limit != null) {
|
|
56
|
+
const cached = cache.query(symbol, market, timeframe, since);
|
|
57
|
+
if (cached.length >= limit) {
|
|
58
|
+
return cached.slice(0, limit);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Fetch data after the latest cached timestamp
|
|
63
|
+
const exchange = await resolveExchange(exchangeId);
|
|
64
|
+
const fetchSince = range.latest + 1;
|
|
65
|
+
const raw = await exchange.fetchOHLCV(symbol, timeframe, fetchSince, limit);
|
|
66
|
+
if (raw.length > 0) {
|
|
67
|
+
const newRows = raw.map(ccxtToOHLCV);
|
|
68
|
+
cache.upsertBatch(symbol, market, timeframe, newRows);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Return all cached data (including newly stored)
|
|
72
|
+
return cache.query(symbol, market, timeframe, since);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Full cache miss — fetch from exchange
|
|
76
|
+
const exchange = await resolveExchange(exchangeId);
|
|
77
|
+
const raw = await exchange.fetchOHLCV(symbol, timeframe, since, limit);
|
|
78
|
+
const rows = raw.map(ccxtToOHLCV);
|
|
79
|
+
|
|
80
|
+
if (rows.length > 0) {
|
|
81
|
+
cache.upsertBatch(symbol, market, timeframe, rows);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return rows;
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
async getTicker(symbol, exchangeId) {
|
|
88
|
+
const exchange = await resolveExchange(exchangeId);
|
|
89
|
+
const raw = await exchange.fetchTicker(symbol);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
symbol,
|
|
93
|
+
market: "crypto",
|
|
94
|
+
last: raw.last as number,
|
|
95
|
+
bid: raw.bid as number | undefined,
|
|
96
|
+
ask: raw.ask as number | undefined,
|
|
97
|
+
volume24h: raw.quoteVolume as number | undefined,
|
|
98
|
+
changePct24h: raw.percentage as number | undefined,
|
|
99
|
+
timestamp: (raw.timestamp as number) ?? Date.now(),
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { OHLCVCache } from "../ohlcv-cache.js";
|
|
2
|
+
import type { OHLCV, Ticker } from "../types.js";
|
|
3
|
+
import type { EquityAdapter } from "./equity-adapter.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Duck-typed client interface matching yahoo-finance2's API surface.
|
|
7
|
+
* Avoids hard dependency on the yahoo-finance2 package at type level.
|
|
8
|
+
*/
|
|
9
|
+
export type YahooFinanceClient = {
|
|
10
|
+
chart: (
|
|
11
|
+
symbol: string,
|
|
12
|
+
options: { period1: string | number; period2?: string | number; interval?: string },
|
|
13
|
+
) => Promise<{ quotes: Array<Record<string, unknown>> }>;
|
|
14
|
+
quote: (symbol: string) => Promise<Record<string, unknown>>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Map our canonical timeframes to Yahoo Finance interval strings. */
|
|
18
|
+
const TIMEFRAME_MAP: Record<string, string> = {
|
|
19
|
+
"1m": "1m",
|
|
20
|
+
"5m": "5m",
|
|
21
|
+
"15m": "15m",
|
|
22
|
+
"1h": "60m",
|
|
23
|
+
"4h": "60m", // Yahoo has no 4h; use 1h as fallback
|
|
24
|
+
"1d": "1d",
|
|
25
|
+
"1W": "1wk",
|
|
26
|
+
"1M": "1mo",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Default lookback in ms (~1 year). */
|
|
30
|
+
const DEFAULT_LOOKBACK_MS = 365 * 24 * 60 * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
/** Convert a Yahoo chart quote row to canonical OHLCV, or null if data is missing. */
|
|
33
|
+
function rowToOHLCV(row: Record<string, unknown>): OHLCV | null {
|
|
34
|
+
const date = row.date as Date | undefined;
|
|
35
|
+
if (!date) return null;
|
|
36
|
+
|
|
37
|
+
const open = row.open as number | null | undefined;
|
|
38
|
+
const high = row.high as number | null | undefined;
|
|
39
|
+
const low = row.low as number | null | undefined;
|
|
40
|
+
const close = row.close as number | null | undefined;
|
|
41
|
+
const volume = row.volume as number | null | undefined;
|
|
42
|
+
|
|
43
|
+
// Yahoo returns null values for non-trading days
|
|
44
|
+
if (open == null || high == null || low == null || close == null) return null;
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
timestamp: date.getTime(),
|
|
48
|
+
open,
|
|
49
|
+
high,
|
|
50
|
+
low,
|
|
51
|
+
close,
|
|
52
|
+
volume: volume ?? 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createYahooAdapter(cache: OHLCVCache, client: YahooFinanceClient): EquityAdapter {
|
|
57
|
+
return {
|
|
58
|
+
async getOHLCV(params) {
|
|
59
|
+
const { symbol, timeframe, since, limit } = params;
|
|
60
|
+
const market = "equity";
|
|
61
|
+
const interval = TIMEFRAME_MAP[timeframe] ?? "1d";
|
|
62
|
+
|
|
63
|
+
// Check cache first
|
|
64
|
+
const range = cache.getRange(symbol, market, timeframe);
|
|
65
|
+
if (range) {
|
|
66
|
+
if (since != null && limit != null) {
|
|
67
|
+
const cached = cache.query(symbol, market, timeframe, since);
|
|
68
|
+
if (cached.length >= limit) {
|
|
69
|
+
return cached.slice(0, limit);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Determine fetch window
|
|
75
|
+
const period1 = since ?? Date.now() - DEFAULT_LOOKBACK_MS;
|
|
76
|
+
const result = await client.chart(symbol, { period1, interval });
|
|
77
|
+
|
|
78
|
+
const rows = result.quotes
|
|
79
|
+
.map(rowToOHLCV)
|
|
80
|
+
.filter((r): r is OHLCV => r !== null)
|
|
81
|
+
.sort((a, b) => a.timestamp - b.timestamp);
|
|
82
|
+
|
|
83
|
+
if (rows.length > 0) {
|
|
84
|
+
cache.upsertBatch(symbol, market, timeframe, rows);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Return from cache for consistency (merges with any existing data)
|
|
88
|
+
if (range || rows.length > 0) {
|
|
89
|
+
const all = cache.query(symbol, market, timeframe, since);
|
|
90
|
+
return limit ? all.slice(0, limit) : all;
|
|
91
|
+
}
|
|
92
|
+
return rows;
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
async getTicker(symbol) {
|
|
96
|
+
const raw = await client.quote(symbol);
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
symbol,
|
|
100
|
+
market: "equity" as const,
|
|
101
|
+
last: (raw.regularMarketPrice as number) ?? 0,
|
|
102
|
+
bid: raw.bid as number | undefined,
|
|
103
|
+
ask: raw.ask as number | undefined,
|
|
104
|
+
volume24h: raw.regularMarketVolume as number | undefined,
|
|
105
|
+
changePct24h: raw.regularMarketChangePercent as number | undefined,
|
|
106
|
+
timestamp: (raw.regularMarketTime as Date)?.getTime?.() ?? Date.now(),
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
|
|
3
|
+
export type PluginConfig = {
|
|
4
|
+
datahubApiUrl: string;
|
|
5
|
+
datahubUsername: string;
|
|
6
|
+
datahubPassword: string;
|
|
7
|
+
requestTimeoutMs: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
function readEnv(keys: string[]): string | undefined {
|
|
11
|
+
for (const key of keys) {
|
|
12
|
+
const value = process.env[key]?.trim();
|
|
13
|
+
if (value) return value;
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Public DataHub defaults — works out of the box
|
|
19
|
+
const DEFAULT_DATAHUB_URL = "http://43.134.61.136:8088";
|
|
20
|
+
const DEFAULT_DATAHUB_USERNAME = "admin";
|
|
21
|
+
const DEFAULT_DATAHUB_PASSWORD = "98ffa5c5-1ec6-4735-8e0c-715a5eca1a8d";
|
|
22
|
+
|
|
23
|
+
export function resolveConfig(api: OpenClawPluginApi): PluginConfig {
|
|
24
|
+
const raw = api.pluginConfig as Record<string, unknown> | undefined;
|
|
25
|
+
|
|
26
|
+
const datahubApiUrl =
|
|
27
|
+
(typeof raw?.datahubApiUrl === "string" ? raw.datahubApiUrl : undefined) ??
|
|
28
|
+
readEnv(["DATAHUB_API_URL", "OPENFINCLAW_DATAHUB_API_URL"]) ??
|
|
29
|
+
DEFAULT_DATAHUB_URL;
|
|
30
|
+
|
|
31
|
+
const datahubUsername =
|
|
32
|
+
(typeof raw?.datahubUsername === "string" ? raw.datahubUsername : undefined) ??
|
|
33
|
+
readEnv(["DATAHUB_USERNAME", "OPENFINCLAW_DATAHUB_USERNAME"]) ??
|
|
34
|
+
DEFAULT_DATAHUB_USERNAME;
|
|
35
|
+
|
|
36
|
+
const datahubPassword =
|
|
37
|
+
(typeof raw?.datahubPassword === "string" ? raw.datahubPassword : undefined) ??
|
|
38
|
+
(typeof raw?.datahubApiKey === "string" ? raw.datahubApiKey : undefined) ??
|
|
39
|
+
readEnv(["DATAHUB_PASSWORD", "OPENFINCLAW_DATAHUB_PASSWORD", "DATAHUB_API_KEY"]) ??
|
|
40
|
+
DEFAULT_DATAHUB_PASSWORD;
|
|
41
|
+
|
|
42
|
+
const timeoutRaw = raw?.requestTimeoutMs ?? readEnv(["OPENFINCLAW_DATAHUB_TIMEOUT_MS"]);
|
|
43
|
+
const timeout = Number(timeoutRaw);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
datahubApiUrl: datahubApiUrl.replace(/\/+$/, ""),
|
|
47
|
+
datahubUsername,
|
|
48
|
+
datahubPassword,
|
|
49
|
+
requestTimeoutMs: Number.isFinite(timeout) && timeout >= 1000 ? Math.floor(timeout) : 30_000,
|
|
50
|
+
};
|
|
51
|
+
}
|