finhay-mcp-server 1.0.7 → 1.0.9
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/README.en.md +133 -0
- package/README.md +69 -73
- package/dist/client/AccountContext.d.ts +8 -3
- package/dist/client/AccountContext.js +26 -18
- package/dist/tools/index.js +2 -2
- package/dist/tools/market.js +41 -0
- package/dist/tools/portfolio.js +8 -25
- package/package.json +2 -1
package/README.en.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# finhay-mcp-server
|
|
2
|
+
|
|
3
|
+
<!-- mcp-name: io.github.finhay/mcp-server -->
|
|
4
|
+
|
|
5
|
+
[Tiếng Việt](README.md) | [English](README.en.md)
|
|
6
|
+
|
|
7
|
+
MCP Server for Finhay Securities — view stock prices, portfolio, gold, crypto via Claude AI.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
### Step 1: Create API Key
|
|
12
|
+
|
|
13
|
+
Go to [https://www.finhay.com.vn/finhay-skills](https://www.finhay.com.vn/finhay-skills) → Login → Create API Key.
|
|
14
|
+
|
|
15
|
+
You will receive:
|
|
16
|
+
- **API Key**: `ak_live_xxx`
|
|
17
|
+
- **API Secret**: `sk_live_yyy`
|
|
18
|
+
|
|
19
|
+
### Step 2: Connect to Claude
|
|
20
|
+
|
|
21
|
+
Choose **one of three** methods below:
|
|
22
|
+
|
|
23
|
+
#### Method 1: Automatic install (no Node.js required — Recommended)
|
|
24
|
+
|
|
25
|
+
Open Terminal (macOS) or PowerShell (Windows), paste this command and press Enter:
|
|
26
|
+
|
|
27
|
+
**macOS:**
|
|
28
|
+
```bash
|
|
29
|
+
curl -fsSL https://raw.githubusercontent.com/finhay/mcp-server/main/install.sh | bash
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Windows (PowerShell):**
|
|
33
|
+
```powershell
|
|
34
|
+
irm https://raw.githubusercontent.com/finhay/mcp-server/main/install.ps1 | iex
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The script will auto-install Node.js (if needed), prompt for API Key/Secret, and configure Claude Desktop.
|
|
38
|
+
|
|
39
|
+
#### Method 2: Quick install (Node.js required)
|
|
40
|
+
|
|
41
|
+
If you already have Node.js (>= 18), run:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx -y finhay-mcp-server --install
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The script will prompt for API Key/Secret (Secret is masked with `*`), then automatically write the config to Claude Desktop.
|
|
48
|
+
|
|
49
|
+
#### Method 3: Manual configuration
|
|
50
|
+
|
|
51
|
+
**Step 3a.** Create credentials file at `~/.finhay/credentials/.env`:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
FINHAY_API_KEY=ak_live_xxx
|
|
55
|
+
FINHAY_API_SECRET=sk_live_yyy
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Step 3b.** Add to Claude Desktop config:
|
|
59
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
60
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
61
|
+
- Windows (Microsoft Store): `%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\claude_desktop_config.json`
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"finhay": {
|
|
67
|
+
"command": "npx",
|
|
68
|
+
"args": ["-y", "finhay-mcp-server"]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> API Key/Secret are **not stored** in Claude config — they live separately at `~/.finhay/credentials/.env` (shared with Finhay Skills).
|
|
75
|
+
|
|
76
|
+
Restart Claude Desktop after setup.
|
|
77
|
+
|
|
78
|
+
### Step 3: Usage
|
|
79
|
+
|
|
80
|
+
Open Claude and ask:
|
|
81
|
+
|
|
82
|
+
- "What's VNM stock price today?"
|
|
83
|
+
- "Show my investment portfolio"
|
|
84
|
+
- "Compare FPT and VNM"
|
|
85
|
+
- "What's SJC gold price today?"
|
|
86
|
+
- "Which bank has the highest savings rate?"
|
|
87
|
+
- "Recent Vietnam CPI index?"
|
|
88
|
+
|
|
89
|
+
## Tools
|
|
90
|
+
|
|
91
|
+
### Market data (20 tools)
|
|
92
|
+
|
|
93
|
+
| Tool | Description |
|
|
94
|
+
|------|-------------|
|
|
95
|
+
| `get_stock_realtime` | Realtime stock prices (single, multiple, or by exchange) |
|
|
96
|
+
| `get_price_history_chart` | OHLCV price history |
|
|
97
|
+
| `get_recommendation_reports` | Analyst recommendation reports |
|
|
98
|
+
| `get_funds` | List of investment funds |
|
|
99
|
+
| `get_fund_portfolio` | Fund portfolio composition |
|
|
100
|
+
| `get_fund_months` | Available months for fund data |
|
|
101
|
+
| `get_gold_prices` | Gold prices (SJC, DOJI, PNJ, BTMC) |
|
|
102
|
+
| `get_gold_chart` | Gold price chart |
|
|
103
|
+
| `get_gold_providers` | Gold prices by provider |
|
|
104
|
+
| `get_silver_prices` | Silver prices |
|
|
105
|
+
| `get_silver_chart` | Silver price chart |
|
|
106
|
+
| `get_metal_providers` | Gold + silver prices by provider |
|
|
107
|
+
| `get_all_financial_data` | All-in-one: gold, silver, crypto, rates, FX |
|
|
108
|
+
| `get_bank_interest_rates` | Bank savings interest rates |
|
|
109
|
+
| `get_crypto_top_trending` | Top trending cryptocurrencies |
|
|
110
|
+
| `get_company_financial_overview` | Company financial overview (PE, PB, ROE, EPS...) |
|
|
111
|
+
| `get_company_financial_analysis` | Financial analysis by year/quarter |
|
|
112
|
+
| `get_financial_statement` | Financial statements (income, balance sheet, cash flow) |
|
|
113
|
+
| `get_macro_data` | Macro indicators (CPI, PMI, IIP, FED rate...) |
|
|
114
|
+
| `get_market_session` | Trading session status |
|
|
115
|
+
|
|
116
|
+
### Account (8 tools)
|
|
117
|
+
|
|
118
|
+
| Tool | Description |
|
|
119
|
+
|------|-------------|
|
|
120
|
+
| `get_account_summary` | Balance: cash, securities, margin |
|
|
121
|
+
| `get_asset_summary` | Total assets |
|
|
122
|
+
| `get_portfolio` | Stock portfolio with P/L |
|
|
123
|
+
| `get_pnl_today` | Today's profit/loss |
|
|
124
|
+
| `get_order_history` | Order history |
|
|
125
|
+
| `get_order_book` | Today's order book |
|
|
126
|
+
| `get_order_detail` | Single order detail |
|
|
127
|
+
| `get_user_rights` | Shareholder rights: dividends, rights issues... |
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
## Requirements
|
|
131
|
+
|
|
132
|
+
- Node.js >= 18
|
|
133
|
+
- Finhay Securities account with API Key
|
package/README.md
CHANGED
|
@@ -1,67 +1,61 @@
|
|
|
1
1
|
# finhay-mcp-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<!-- mcp-name: io.github.finhay/mcp-server -->
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[Tiếng Việt](README.md) | [English](README.en.md)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
MCP Server cho Finhay Securities — xem giá cổ phiếu, danh mục đầu tư, vàng, crypto qua Claude AI.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Cài đặt
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
- **API Key**: `ak_live_xxx`
|
|
13
|
-
- **API Secret**: `sk_live_yyy`
|
|
14
|
-
|
|
15
|
-
### Buoc 2: Ket noi voi Claude
|
|
11
|
+
### Bước 1: Tạo API Key
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
Vào [https://www.finhay.com.vn/finhay-skills](https://www.finhay.com.vn/finhay-skills) → Đăng nhập → Tạo API Key.
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
Bạn sẽ nhận được:
|
|
16
|
+
- **API Key**: `ak_live_xxx`
|
|
17
|
+
- **API Secret**: `sk_live_yyy`
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
npx -y finhay-mcp-server --install
|
|
25
|
-
```
|
|
19
|
+
### Bước 2: Kết nối với Claude
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
Chọn **một trong ba** cách sau:
|
|
28
22
|
|
|
29
|
-
####
|
|
23
|
+
#### Cách 1: Cài đặt tự động (không cần Node.js — khuyên dùng)
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
Mở Terminal (macOS) hoặc PowerShell (Windows), dán lệnh sau và nhấn Enter:
|
|
32
26
|
|
|
33
27
|
**macOS:**
|
|
34
28
|
```bash
|
|
35
|
-
curl -fsSL https://raw.githubusercontent.com/finhay
|
|
29
|
+
curl -fsSL https://raw.githubusercontent.com/finhay/mcp-server/main/install.sh | bash
|
|
36
30
|
```
|
|
37
31
|
|
|
38
32
|
**Windows (PowerShell):**
|
|
39
33
|
```powershell
|
|
40
|
-
irm https://raw.githubusercontent.com/finhay
|
|
34
|
+
irm https://raw.githubusercontent.com/finhay/mcp-server/main/install.ps1 | iex
|
|
41
35
|
```
|
|
42
36
|
|
|
43
|
-
Script
|
|
37
|
+
Script sẽ tự động cài Node.js (nếu chưa có), hỏi API Key/Secret, và cấu hình Claude Desktop.
|
|
44
38
|
|
|
45
|
-
####
|
|
39
|
+
#### Cách 2: Cài đặt nhanh (cần Node.js)
|
|
46
40
|
|
|
47
|
-
|
|
41
|
+
Nếu bạn đã có Node.js (>= 18), chạy lệnh sau:
|
|
48
42
|
|
|
49
43
|
```bash
|
|
50
|
-
|
|
44
|
+
npx -y finhay-mcp-server --install
|
|
51
45
|
```
|
|
52
46
|
|
|
53
|
-
|
|
47
|
+
Script sẽ hỏi API Key/Secret (Secret được ẩn bằng dấu `*`), tự động ghi config vào Claude Desktop.
|
|
54
48
|
|
|
55
|
-
####
|
|
49
|
+
#### Cách 3: Cấu hình thủ công
|
|
56
50
|
|
|
57
|
-
**
|
|
51
|
+
**Bước 3a.** Tạo file credentials tại `~/.finhay/credentials/.env`:
|
|
58
52
|
|
|
59
53
|
```
|
|
60
54
|
FINHAY_API_KEY=ak_live_xxx
|
|
61
55
|
FINHAY_API_SECRET=sk_live_yyy
|
|
62
56
|
```
|
|
63
57
|
|
|
64
|
-
**
|
|
58
|
+
**Bước 3b.** Thêm vào file config Claude Desktop:
|
|
65
59
|
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
66
60
|
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
67
61
|
- Windows (Microsoft Store): `%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\claude_desktop_config.json`
|
|
@@ -77,61 +71,63 @@ FINHAY_API_SECRET=sk_live_yyy
|
|
|
77
71
|
}
|
|
78
72
|
```
|
|
79
73
|
|
|
80
|
-
> API Key/Secret **
|
|
74
|
+
> API Key/Secret **không nằm** trong file config Claude — được lưu riêng tại `~/.finhay/credentials/.env` (dùng chung với Finhay Skills).
|
|
81
75
|
|
|
82
|
-
Sau khi
|
|
76
|
+
Sau khi hoàn tất, khởi động lại Claude Desktop là xong.
|
|
83
77
|
|
|
84
|
-
###
|
|
78
|
+
### Bước 3: Sử dụng
|
|
85
79
|
|
|
86
|
-
|
|
80
|
+
Mở Claude, hỏi:
|
|
87
81
|
|
|
88
|
-
- "
|
|
89
|
-
- "Xem danh
|
|
90
|
-
- "So
|
|
91
|
-
- "
|
|
92
|
-
- "
|
|
93
|
-
- "
|
|
82
|
+
- "Giá cổ phiếu VNM hôm nay?"
|
|
83
|
+
- "Xem danh mục đầu tư của tôi"
|
|
84
|
+
- "So sánh FPT và VNM"
|
|
85
|
+
- "Giá vàng SJC hôm nay bao nhiêu?"
|
|
86
|
+
- "Lãi suất tiết kiệm ngân hàng nào cao nhất?"
|
|
87
|
+
- "Chỉ số CPI Việt Nam gần đây?"
|
|
94
88
|
|
|
95
89
|
## Tools
|
|
96
90
|
|
|
97
|
-
###
|
|
91
|
+
### Thị trường (20 tools)
|
|
98
92
|
|
|
99
|
-
| Tool |
|
|
93
|
+
| Tool | Mô tả |
|
|
100
94
|
|------|-------|
|
|
101
|
-
| `get_stock_realtime` |
|
|
102
|
-
| `get_price_history_chart` |
|
|
103
|
-
| `get_recommendation_reports` |
|
|
104
|
-
| `get_funds` | Danh
|
|
105
|
-
| `get_fund_portfolio` | Danh
|
|
106
|
-
| `get_fund_months` |
|
|
107
|
-
| `get_gold_prices` |
|
|
108
|
-
| `get_gold_chart` |
|
|
109
|
-
| `get_gold_providers` |
|
|
110
|
-
| `get_silver_prices` |
|
|
111
|
-
| `get_silver_chart` |
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
|
95
|
+
| `get_stock_realtime` | Giá cổ phiếu realtime (1 mã, nhiều mã, hoặc theo sàn) |
|
|
96
|
+
| `get_price_history_chart` | Lịch sử giá OHLCV |
|
|
97
|
+
| `get_recommendation_reports` | Báo cáo phân tích từ chuyên gia |
|
|
98
|
+
| `get_funds` | Danh sách quỹ đầu tư |
|
|
99
|
+
| `get_fund_portfolio` | Danh mục của quỹ |
|
|
100
|
+
| `get_fund_months` | Các tháng có dữ liệu quỹ |
|
|
101
|
+
| `get_gold_prices` | Giá vàng (SJC, DOJI, PNJ, BTMC) |
|
|
102
|
+
| `get_gold_chart` | Biểu đồ giá vàng |
|
|
103
|
+
| `get_gold_providers` | Giá vàng theo nhà cung cấp |
|
|
104
|
+
| `get_silver_prices` | Giá bạc |
|
|
105
|
+
| `get_silver_chart` | Biểu đồ giá bạc |
|
|
106
|
+
| `get_metal_providers` | Giá vàng + bạc theo nhà cung cấp |
|
|
107
|
+
| `get_all_financial_data` | Tổng hợp: vàng, bạc, crypto, lãi suất, tỷ giá |
|
|
108
|
+
| `get_bank_interest_rates` | Lãi suất tiết kiệm ngân hàng |
|
|
109
|
+
| `get_crypto_top_trending` | Crypto xu hướng |
|
|
110
|
+
| `get_company_financial_overview` | Tổng quan tài chính doanh nghiệp (PE, PB, ROE, EPS...) |
|
|
111
|
+
| `get_company_financial_analysis` | Phân tích tài chính theo năm/quý |
|
|
112
|
+
| `get_financial_statement` | Báo cáo tài chính (BCTC, CDKT, LCTT) |
|
|
113
|
+
| `get_macro_data` | Chỉ số vĩ mô (CPI, PMI, IIP, FED rate...) |
|
|
114
|
+
| `get_market_session` | Trạng thái phiên giao dịch |
|
|
115
|
+
|
|
116
|
+
### Tài khoản (8 tools)
|
|
117
|
+
|
|
118
|
+
| Tool | Mô tả |
|
|
121
119
|
|------|-------|
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `get_user_rights` | Quyen co dong: co tuc, quyen mua... |
|
|
131
|
-
| `get_trade_info` | Suc mua / so luong ban duoc |
|
|
120
|
+
| `get_account_summary` | Số dư: tiền mặt, chứng khoán, ký quỹ |
|
|
121
|
+
| `get_asset_summary` | Tổng tài sản |
|
|
122
|
+
| `get_portfolio` | Danh mục cổ phiếu với lãi/lỗ |
|
|
123
|
+
| `get_pnl_today` | Lãi/lỗ hôm nay |
|
|
124
|
+
| `get_order_history` | Lịch sử lệnh |
|
|
125
|
+
| `get_order_book` | Sổ lệnh trong ngày |
|
|
126
|
+
| `get_order_detail` | Chi tiết 1 lệnh |
|
|
127
|
+
| `get_user_rights` | Quyền cổ đông: cổ tức, quyền mua... |
|
|
132
128
|
|
|
133
129
|
|
|
134
|
-
##
|
|
130
|
+
## Yêu cầu
|
|
135
131
|
|
|
136
132
|
- Node.js >= 18
|
|
137
|
-
-
|
|
133
|
+
- Tài khoản Finhay Securities với API Key
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { FinhayClient } from './FinhayClient.js';
|
|
2
|
+
export interface SubAccount {
|
|
3
|
+
id: string;
|
|
4
|
+
type: string;
|
|
5
|
+
subAccountExt: string;
|
|
6
|
+
}
|
|
2
7
|
export interface AccountInfo {
|
|
3
8
|
userId: string;
|
|
4
|
-
|
|
5
|
-
subAccountIds: string[];
|
|
9
|
+
subAccounts: SubAccount[];
|
|
6
10
|
}
|
|
7
11
|
/**
|
|
8
|
-
* Fetches and caches account info (userId,
|
|
12
|
+
* Fetches and caches account info (userId, subAccounts) on startup.
|
|
9
13
|
* Tools use this as default when user does not provide subAccountId.
|
|
10
14
|
*/
|
|
11
15
|
export declare class AccountContext {
|
|
@@ -14,6 +18,7 @@ export declare class AccountContext {
|
|
|
14
18
|
constructor(client: FinhayClient);
|
|
15
19
|
init(): Promise<void>;
|
|
16
20
|
getUserId(): string | undefined;
|
|
21
|
+
getSubAccountByType(type: string): SubAccount | undefined;
|
|
17
22
|
getDefaultSubAccountId(): string | undefined;
|
|
18
23
|
getSubAccountIds(): string[];
|
|
19
24
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Fetches and caches account info (userId,
|
|
2
|
+
* Fetches and caches account info (userId, subAccounts) on startup.
|
|
3
3
|
* Tools use this as default when user does not provide subAccountId.
|
|
4
4
|
*/
|
|
5
5
|
export class AccountContext {
|
|
@@ -10,24 +10,27 @@ export class AccountContext {
|
|
|
10
10
|
}
|
|
11
11
|
async init() {
|
|
12
12
|
try {
|
|
13
|
-
// Step 1: get userId
|
|
13
|
+
// Step 1: get userId from .data.user_id
|
|
14
14
|
const meData = await this.client.get('/users/v1/users/me');
|
|
15
|
-
const
|
|
16
|
-
const userId = meResult?.user_id ?? meResult?.userId ?? meResult?.id ?? '';
|
|
15
|
+
const userId = meData.data?.user_id ?? '';
|
|
17
16
|
if (!userId) {
|
|
18
|
-
throw new Error('Could not extract
|
|
17
|
+
throw new Error('Could not extract user_id from /users/v1/users/me');
|
|
19
18
|
}
|
|
20
|
-
// Step 2: get sub-accounts
|
|
19
|
+
// Step 2: get sub-accounts with type and sub_account_ext
|
|
21
20
|
const subData = await this.client.get(`/users/v1/users/${userId}/sub-accounts`);
|
|
22
|
-
const subResult = subData.result
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
const subResult = Array.isArray(subData.result) ? subData.result
|
|
22
|
+
: Array.isArray(subData.data) ? subData.data
|
|
23
|
+
: [];
|
|
24
|
+
const subAccounts = subResult
|
|
25
|
+
.map((sa) => ({
|
|
26
|
+
id: sa.id ?? '',
|
|
27
|
+
type: (sa.type ?? '').toUpperCase(),
|
|
28
|
+
subAccountExt: sa.sub_account_ext ?? '',
|
|
29
|
+
}))
|
|
30
|
+
.filter((sa) => sa.id);
|
|
31
|
+
this.info = { userId: String(userId), subAccounts };
|
|
32
|
+
const summary = subAccounts.map((sa) => `${sa.type}=${sa.id}`).join(', ');
|
|
33
|
+
console.error(`[finhay-mcp] Account loaded: userId=${this.info.userId}, subAccounts=[${summary}]`);
|
|
31
34
|
}
|
|
32
35
|
catch (err) {
|
|
33
36
|
console.error(`[finhay-mcp] Warning: could not fetch account info: ${err.message}`);
|
|
@@ -37,18 +40,23 @@ export class AccountContext {
|
|
|
37
40
|
getUserId() {
|
|
38
41
|
return this.info?.userId || undefined;
|
|
39
42
|
}
|
|
43
|
+
getSubAccountByType(type) {
|
|
44
|
+
return this.info?.subAccounts.find((sa) => sa.type === type.toUpperCase());
|
|
45
|
+
}
|
|
40
46
|
getDefaultSubAccountId() {
|
|
41
|
-
|
|
47
|
+
// Prefer NORMAL, fallback to first available
|
|
48
|
+
const normal = this.getSubAccountByType('NORMAL');
|
|
49
|
+
return normal?.id || this.info?.subAccounts[0]?.id || undefined;
|
|
42
50
|
}
|
|
43
51
|
getSubAccountIds() {
|
|
44
|
-
return this.info?.
|
|
52
|
+
return this.info?.subAccounts.map((sa) => sa.id) ?? [];
|
|
45
53
|
}
|
|
46
54
|
/**
|
|
47
55
|
* Returns the given subAccountId if provided, otherwise falls back to default.
|
|
48
56
|
* Throws if neither is available.
|
|
49
57
|
*/
|
|
50
58
|
resolveSubAccountId(subAccountId) {
|
|
51
|
-
const resolved = subAccountId || this.
|
|
59
|
+
const resolved = subAccountId || this.getDefaultSubAccountId();
|
|
52
60
|
if (!resolved) {
|
|
53
61
|
throw new Error('subAccountId is required. Could not auto-detect — provide it explicitly or check your API credentials.');
|
|
54
62
|
}
|
package/dist/tools/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { registerMarketTools } from './market.js';
|
|
2
2
|
import { registerPortfolioTools } from './portfolio.js';
|
|
3
3
|
export function registerAllTools(server, client, account) {
|
|
4
|
-
registerMarketTools(server, client); //
|
|
5
|
-
registerPortfolioTools(server, client, account); //
|
|
4
|
+
registerMarketTools(server, client); // 20 tools: stock, funds, gold, silver, metals, crypto, macro, company financials, etc.
|
|
5
|
+
registerPortfolioTools(server, client, account); // 8 tools: account, portfolio, orders, pnl, rights, etc.
|
|
6
6
|
}
|
package/dist/tools/market.js
CHANGED
|
@@ -78,6 +78,11 @@ export function registerMarketTools(server, client) {
|
|
|
78
78
|
const data = await client.get('/market/financial-data/silver-chart', { days: String(days) });
|
|
79
79
|
return JSON.stringify(data.data, null, 2);
|
|
80
80
|
}));
|
|
81
|
+
// --- Metal providers ---
|
|
82
|
+
server.tool('get_metal_providers', 'Get gold and silver prices grouped by provider (superset of gold-providers and silver data)', {}, safeHandler(async () => {
|
|
83
|
+
const data = await client.get('/market/financial-data/metal-providers');
|
|
84
|
+
return JSON.stringify(data.data, null, 2);
|
|
85
|
+
}));
|
|
81
86
|
// --- Financial data ---
|
|
82
87
|
server.tool('get_all_financial_data', 'Get all financial data: gold, silver, crypto, bank rates, USD exchange rate', {}, safeHandler(async () => {
|
|
83
88
|
const data = await client.get('/market/financial-data');
|
|
@@ -91,6 +96,42 @@ export function registerMarketTools(server, client) {
|
|
|
91
96
|
const data = await client.get('/market/financial-data/cryptos/top-trending');
|
|
92
97
|
return JSON.stringify(data.data, null, 2);
|
|
93
98
|
}));
|
|
99
|
+
// --- Company financials ---
|
|
100
|
+
server.tool('get_company_financial_overview', 'Get financial overview for a company: PE, PB, EV/EBITDA, gross margin, ROE, EPS, dividend yield, NIM, ROA, and industry averages', {
|
|
101
|
+
symbol: z.string().describe('Stock symbol (e.g., VNM)'),
|
|
102
|
+
}, safeHandler(async ({ symbol }) => {
|
|
103
|
+
const data = await client.get('/market/company-financial/overview', {
|
|
104
|
+
symbol: symbol.toUpperCase(),
|
|
105
|
+
});
|
|
106
|
+
return JSON.stringify(data.data, null, 2);
|
|
107
|
+
}));
|
|
108
|
+
server.tool('get_company_financial_analysis', 'Get financial analysis entries by year or quarter for a company', {
|
|
109
|
+
symbol: z.string().describe('Stock symbol (e.g., VNM)'),
|
|
110
|
+
period: z.enum(['annual', 'quarterly']).optional().describe('Period type (default: annual)'),
|
|
111
|
+
}, safeHandler(async ({ symbol, period }) => {
|
|
112
|
+
const query = { symbol: symbol.toUpperCase() };
|
|
113
|
+
if (period)
|
|
114
|
+
query.period = period;
|
|
115
|
+
const data = await client.get('/market/company-financial/analysis', query);
|
|
116
|
+
return JSON.stringify(data.data, null, 2);
|
|
117
|
+
}));
|
|
118
|
+
server.tool('get_financial_statement', 'Get financial statements (income statement, balance sheet, or cash flow) for a company', {
|
|
119
|
+
symbol: z.string().describe('Stock symbol (e.g., VNM)'),
|
|
120
|
+
type: z.enum(['income-statement', 'balance-sheet', 'cash-flow']).describe('Statement type'),
|
|
121
|
+
period: z.enum(['annual', 'quarterly']).optional().describe('Period type'),
|
|
122
|
+
limit: z.number().min(1).max(5).optional().describe('Number of periods to return (1-5, default: 5)'),
|
|
123
|
+
}, safeHandler(async ({ symbol, type, period, limit }) => {
|
|
124
|
+
const query = {
|
|
125
|
+
symbol: symbol.toUpperCase(),
|
|
126
|
+
type,
|
|
127
|
+
};
|
|
128
|
+
if (period)
|
|
129
|
+
query.period = period;
|
|
130
|
+
if (limit)
|
|
131
|
+
query.limit = String(limit);
|
|
132
|
+
const data = await client.get('/market/v2/financial-statement/statement', query);
|
|
133
|
+
return JSON.stringify(data.data, null, 2);
|
|
134
|
+
}));
|
|
94
135
|
// --- Macro ---
|
|
95
136
|
server.tool('get_macro_data', 'Get macroeconomic indicators for Vietnam or US (CPI, PMI, IIP, FED rate, etc.)', {
|
|
96
137
|
type: z.enum([
|
package/dist/tools/portfolio.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { safeHandler } from '../utils/safeTool.js';
|
|
3
3
|
export function registerPortfolioTools(server, client, account) {
|
|
4
|
-
// --- Owner ---
|
|
5
|
-
server.tool('get_owner_info', 'Get owner identity info (name, accounts, sub-account IDs, etc.)', {}, safeHandler(async () => {
|
|
6
|
-
const data = await client.get('/users/v1/users/me');
|
|
7
|
-
return JSON.stringify(data.result, null, 2);
|
|
8
|
-
}));
|
|
9
4
|
// --- Account summary ---
|
|
10
5
|
server.tool('get_account_summary', 'Get account balance summary: cash, securities value, margin, net asset value', {
|
|
11
6
|
subAccountId: z.string().optional().describe('Sub-account ID (auto-detected if omitted)'),
|
|
@@ -16,10 +11,10 @@ export function registerPortfolioTools(server, client, account) {
|
|
|
16
11
|
}));
|
|
17
12
|
// --- Asset summary ---
|
|
18
13
|
server.tool('get_asset_summary', 'Get asset summary with total valuation', {
|
|
19
|
-
|
|
20
|
-
}, safeHandler(async ({
|
|
21
|
-
const
|
|
22
|
-
const data = await client.get(`/
|
|
14
|
+
userId: z.string().optional().describe('User ID (auto-detected if omitted)'),
|
|
15
|
+
}, safeHandler(async ({ userId }) => {
|
|
16
|
+
const uid = account.resolveUserId(userId);
|
|
17
|
+
const data = await client.get(`/users/v4/users/${uid}/assets/summary`);
|
|
23
18
|
return JSON.stringify(data.data, null, 2);
|
|
24
19
|
}));
|
|
25
20
|
// --- Portfolio ---
|
|
@@ -85,9 +80,10 @@ export function registerPortfolioTools(server, client, account) {
|
|
|
85
80
|
fromDate: z.string().optional().describe('Start date (YYYY-MM-DD)'),
|
|
86
81
|
toDate: z.string().optional().describe('End date (YYYY-MM-DD)'),
|
|
87
82
|
catType: z.string().optional().describe('Category type, comma-separated or ALL (default: ALL)'),
|
|
83
|
+
isCom: z.string().optional().describe('Commission filter (default: ALL)'),
|
|
88
84
|
symbol: z.string().optional().describe('Filter by symbol (default: ALL)'),
|
|
89
85
|
status: z.string().optional().describe('Status filter, comma-separated or ALL (default: ALL)'),
|
|
90
|
-
}, safeHandler(async ({ subAccountId, fromDate, toDate, catType, symbol, status }) => {
|
|
86
|
+
}, safeHandler(async ({ subAccountId, fromDate, toDate, catType, isCom, symbol, status }) => {
|
|
91
87
|
const id = account.resolveSubAccountId(subAccountId);
|
|
92
88
|
const query = {};
|
|
93
89
|
if (fromDate)
|
|
@@ -96,6 +92,8 @@ export function registerPortfolioTools(server, client, account) {
|
|
|
96
92
|
query.toDate = toDate;
|
|
97
93
|
if (catType)
|
|
98
94
|
query.catType = catType;
|
|
95
|
+
if (isCom)
|
|
96
|
+
query.isCom = isCom;
|
|
99
97
|
if (symbol)
|
|
100
98
|
query.symbol = symbol.toUpperCase();
|
|
101
99
|
if (status)
|
|
@@ -103,19 +101,4 @@ export function registerPortfolioTools(server, client, account) {
|
|
|
103
101
|
const data = await client.get(`/trading/v5/account/${id}/user-rights`, query);
|
|
104
102
|
return JSON.stringify(data.result, null, 2);
|
|
105
103
|
}));
|
|
106
|
-
// --- Trade info (pre-order check) ---
|
|
107
|
-
server.tool('get_trade_info', 'Get buying power (BUY) or available quantity (SELL) before placing an order', {
|
|
108
|
-
subAccountId: z.string().optional().describe('Sub-account ID (auto-detected if omitted)'),
|
|
109
|
-
symbol: z.string().describe('Stock symbol'),
|
|
110
|
-
side: z.enum(['BUY', 'SELL']).describe('Order side'),
|
|
111
|
-
quotePrice: z.number().describe('Quote price in VND'),
|
|
112
|
-
}, safeHandler(async ({ subAccountId, symbol, side, quotePrice }) => {
|
|
113
|
-
const id = account.resolveSubAccountId(subAccountId);
|
|
114
|
-
const data = await client.get(`/trading/sub-accounts/${id}/trade-info`, {
|
|
115
|
-
symbol: symbol.toUpperCase(),
|
|
116
|
-
side,
|
|
117
|
-
quote_price: String(quotePrice),
|
|
118
|
-
});
|
|
119
|
-
return JSON.stringify(data.result, null, 2);
|
|
120
|
-
}));
|
|
121
104
|
}
|
package/package.json
CHANGED