Skip to main content
Glama
scorpio-su

sj-trading-mcp

by scorpio-su
README.md
# sj-trading-mcp

永豐 Shioaji **MCP Server**,讓 Claude(或任何 MCP 客戶端)能直接操作台灣股票、期貨與選擇權交易,並查詢即時行情與帳務資料。  
遵循 **OOP + Clean Code(Uncle Bob)** 原則,預設執行於 `simulation=True` 模擬環境。

---

## 專案結構

```
sj_trading-mcp/
├── config/
│   ├── credentials.json.example  ← 金鑰範本(JSON 格式)
│   ├── credentials.txt.example   ← 金鑰範本(key=value 格式)
│   ├── credentials.json          ← 實際金鑰(git ignored)
│   └── credentials.txt           ← 實際金鑰(git ignored)
├── src/
│   ├── core/
│   │   ├── credentials.py        ← 金鑰載入器(支援 .json / .txt 自動判斷)
│   │   └── api_client.py         ← Shioaji 連線封裝(支援 with 語法)
│   ├── market/
│   │   ├── stock_market.py       ← 股票行情:快照、K 線、Tick、資券、券源
│   │   ├── futures_market.py     ← 期貨行情:歷史 K 線(含連續合約)
│   │   └── market_info.py        ← 市場資訊:掃描器排行、處置股、注意股
│   ├── accounting/
│   │   ├── stock_account.py      ← 股票帳務:餘額、交易額度、交割資訊
│   │   ├── futures_account.py    ← 期貨帳務:保證金查詢
│   │   └── common_account.py     ← 跨帳戶:損益查詢、持倉明細
│   ├── trading/
│   │   ├── stock_trader.py       ← 股票交易:下單、改單、市價單、查持倉
│   │   ├── futures_trader.py     ← 期貨交易:開平倉、改單、查持倉
│   │   └── options_trader.py     ← 選擇權交易:下單、刪單、查持倉
│   ├── option_strategy/
│   │   ├── _types.py             ← 資料模型:Action / Right / InstrumentType / Leg / Strategy
│   │   ├── _pure_option.py       ← 純選擇權策略(45 種)
│   │   ├── _stock_legged.py      ← 含股票腿策略(6 種)
│   │   ├── _future_legged.py     ← 含期貨腿策略(7 種)
│   │   └── strategies.py         ← 策略總表 STRATEGIES + get_strategy()
│   └── grid_martingale_strategy/
│       ├── __init__.py           ← 對外 export 兩策略 + factories
│       ├── base/                 ← BaseStrategy、TradeJournal、RiskPolicy
│       ├── grid/                 ← GridStrategy、GridConfig、build_grid_strategy
│       ├── martingale/           ← MartingaleStrategy、MartingaleConfig、build_martingale_strategy
│       ├── adapters/             ← MarketAdapter + stock/futures 實作
│       ├── risk/                 ← GridRisk、MartingaleStockRisk、MartingaleFuturesRisk
│       ├── config/               ← 向後相容 re-export(grid/、martingale/ 為 canonical)
│       └── _types.py             ← 共用 enum / dataclass
├── tests/
│   ├── test_stock.py             ← 股票模擬測試
│   ├── test_futures.py           ← 期貨模擬測試
│   ├── test_market.py            ← 行情資料模擬測試
│   ├── test_accounting.py        ← 帳務查詢模擬測試
│   └── test_options.py           ← 選擇權模擬測試
├── .claude/
│   └── settings.json             ← Claude Code MCP 設定
├── server.py                     ← MCP Server 主程式
├── main.py                       ← 快速端對端測試(含 grid / martingale)
├── Dockerfile
├── pyproject.toml
└── requirements.txt
```

---

## 設定金鑰

複製範本後填入真實值:

```bash
# JSON 格式
cp config/credentials.json.example config/credentials.json

# 或 txt 格式
cp config/credentials.txt.example config/credentials.txt
```

**僅行情 / 股票下單**(`credentials.json`,不需 CA):

```json
{
  "api_key": "YOUR_API_KEY",
  "secret_key": "YOUR_SECRET_KEY"
}
```

**期貨 / 選擇權下單**須填寫 CA 憑證欄位(模擬與真實模式皆需):

```json
{
  "api_key": "YOUR_API_KEY",
  "secret_key": "YOUR_SECRET_KEY",
  "ca_path": "config/ca/YOUR_CA.pfx",
  "ca_password": "YOUR_CA_PASSWORD",
  "person_id": "YOUR_PERSON_ID"
}
```

> CA 欄位存在時,`ApiClient` 在連線後**立即啟動憑證**(無論模擬或真實模式)。  
> TAIFEX(期貨交易所)即使在模擬環境也需要帳戶層級的 CA 簽署才能接受委託。

---

## Docker 啟動(推薦)

```bash
# 第一次 build
docker build -t sj-trading-mcp .

# 啟動(模擬模式)
docker run -d \
  --name sj-trading-mcp \
  -p 127.0.0.1:9090:9090 \
  -v ./config:/app/config:ro \
  -v ./logs:/app/logs \
  --restart unless-stopped \
  sj-trading-mcp

# 真實下單模式
docker run -d \
  --name sj-trading-mcp \
  -p 127.0.0.1:9090:9090 \
  -v ./config:/app/config:ro \
  -v ./logs:/app/logs \
  -e SJ_SIMULATION=false \
  --restart unless-stopped \
  sj-trading-mcp
```

**停止 / 重啟:**

```bash
docker stop sj-trading-mcp
docker start sj-trading-mcp
```

**查 log:**

```bash
docker logs -f sj-trading-mcp
# 或查 log 檔
cat logs/server.log
```

---

## 整合 Claude Code

`.claude/settings.json` 已預設配置,在此專案目錄下開啟 Claude Code 即自動載入。

**CLI 加入(專案範圍):**

```bash
claude mcp add sj-trading --transport http http://127.0.0.1:9090/mcp --scope project
```

---

## MCP 工具清單

### 連線管理

| 工具                      | 說明                                                     |
| ------------------------- | -------------------------------------------------------- |
| `connect_api(simulation)` | 連線 API(`simulation=true` 預設模擬,`false` 真實下單) |
| `disconnect_api()`        | 登出斷線                                                 |
| `connection_status()`     | 查詢連線狀態                                             |

### 股票交易

| 工具                                                | 說明                      |
| --------------------------------------------------- | ------------------------- |
| `stock_buy(symbol, price, quantity)`                | 整股限價買進(ROD)       |
| `stock_sell(symbol, price, quantity)`               | 整股限價賣出(ROD)       |
| `stock_market_buy(symbol, quantity)`                | 整股市價買進(MKT + IOC) |
| `stock_market_sell(symbol, quantity)`               | 整股市價賣出(MKT + IOC) |
| `stock_buy_odd(symbol, price, quantity, intraday)`  | 零股買進                  |
| `stock_sell_odd(symbol, price, quantity, intraday)` | 零股賣出                  |
| `stock_margin_buy(symbol, price, quantity)`         | 融資買進                  |
| `stock_margin_sell(symbol, price, quantity)`        | 融資賣出/還券            |
| `stock_short_sell(symbol, price, quantity)`         | 融券賣出                  |
| `stock_short_cover(symbol, price, quantity)`        | 融券回補                  |
| `stock_cancel(order_id)`                            | 刪除委託單                |
| `stock_update_order(order_id, price, qty)`          | 改價或減量(二選一)      |
| `stock_list_trades()`                               | 查詢所有委託紀錄          |
| `stock_positions()`                                 | 查詢股票持倉              |
| `stock_contract_info(symbol)`                       | 查詢合約基本資訊          |

### 期貨交易

| 工具                                           | 說明                          |
| ---------------------------------------------- | ----------------------------- |
| `futures_open_long(symbol, price, quantity)`   | 開多倉(Buy + OCType.New)    |
| `futures_open_short(symbol, price, quantity)`  | 開空倉(Sell + OCType.New)   |
| `futures_close_long(symbol, price, quantity)`  | 平多倉(Sell + OCType.Cover) |
| `futures_close_short(symbol, price, quantity)` | 平空倉(Buy + OCType.Cover)  |
| `futures_cancel(order_id)`                     | 刪除委託單                    |
| `futures_update_order(order_id, price, qty)`   | 改價或減量                    |
| `futures_positions()`                          | 查詢期貨持倉                  |
| `futures_contract_info(symbol)`                | 查詢合約基本資訊              |

### 選擇權交易

| 工具                                            | 說明                                     |
| ----------------------------------------------- | ---------------------------------------- |
| `options_buy(symbol, price, quantity, octype)`  | 買進選擇權(LMT + ROD)                  |
| `options_sell(symbol, price, quantity, octype)` | 賣出選擇權(LMT + ROD)                  |
| `options_cancel(order_id)`                      | 刪除委託單                               |
| `options_positions()`                           | 查詢選擇權/期貨持倉                     |
| `options_contract_info(symbol)`                 | 查詢合約資訊(到期月、履約價、Call/Put) |

> `symbol` 格式:`TXO20260620200C`(商品代碼 + YYYYMMDD + 履約價 + C/P)  
> `octype`:`"Auto"`(預設)/ `"NewPosition"`(新倉)/ `"Cover"`(平倉)

### 選擇權策略(多腿組合)

| 工具                                                               | 說明                                                                       |
| ------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| `strategy_list()`                                                  | 列出所有可用策略(純選擇權 45 種、含股票腿 6 種、含期貨腿 7 種,共 58 種) |
| `strategy_info(strategy_id)`                                       | 查詢策略腿部結構(方向、標的類型、Put/Call、履約價位、口數)               |
| `strategy_execute(strategy_id, leg_symbols, leg_prices, base_qty)` | 執行多腿策略,逐腿送出委託                                                 |

> 使用流程:`strategy_list()` → `strategy_info()` 確認腿部順序 → `strategy_execute()` 下單  
> `leg_symbols` / `leg_prices` 長度須與策略腿數一致;`base_qty` 為口數乘數(預設 `1`)

### 網格策略(Grid)

| 工具                                                                                         | 說明                                                       |
| -------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `grid_create(market_type, symbol, upper_price, lower_price, grid_count, grid_unit_qty, ...)` | 建立純網格策略,回傳 `grid-N`                              |
| `grid_start(strategy_id)`                                                                    | 啟動(idle → running)                                     |
| `grid_stop(strategy_id)`                                                                     | 停止行情訂閱                                               |
| `grid_status(strategy_id)`                                                                   | 查詢狀態(`strategy_type=grid`、格位數、triggered_levels) |
| `grid_rebalance(strategy_id)`                                                                | 重新對齊格位                                               |
| `grid_destroy(strategy_id)`                                                                  | 停止並移除                                                 |
| `grid_list()`                                                                                | 列出所有網格策略                                           |

> **使用流程**:`grid_create(...)` → `grid_start('grid-1')` → `grid_status(...)` → `grid_stop` / `grid_destroy`  
> `grid_create` **不再**包含馬丁參數;`grid_mode`:`"arithmetic"` / `"geometric"`;`direction`:`"LONG"` / `"SHORT"`(僅期貨)

### 馬丁格爾策略(Martingale)

| 工具                                                                                                                                     | 說明                                                   |
| ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `martingale_create(market_type, symbol, mart_multiplier, mart_trigger_pct, mart_max_times, mart_initial_qty, mart_take_profit_pct, ...)` | 建立純馬丁策略,回傳 `mart-N`                          |
| `martingale_start(strategy_id)`                                                                                                          | 啟動(idle → running)                                 |
| `martingale_stop(strategy_id)`                                                                                                           | 停止行情訂閱                                           |
| `martingale_status(strategy_id)`                                                                                                         | 查詢狀態(layer_count、avg_cost、frozen、liquidating) |
| `martingale_destroy(strategy_id)`                                                                                                        | 停止並移除                                             |
| `martingale_list()`                                                                                                                      | 列出所有馬丁策略                                       |

> **使用流程**:`martingale_create(...)` → `martingale_start('mart-1')` → `martingale_status(...)` → `martingale_stop` / `martingale_destroy`  
> `qty_mode`:`"multiply"` / `"linear"`;策略 ID 前綴為 `mart-`(與網格的 `grid-` 分開管理)

> 網格與馬丁為**完全獨立**策略:`grid/` 與 `martingale/` 互不 import,可各自建立、各自運行。原 `GridMartingaleStrategy` / `build_strategy` 已移除。

### 行情資料

| 工具                                              | 說明                                                                |
| ------------------------------------------------- | ------------------------------------------------------------------- |
| `stock_snapshot(symbols)`                         | 即時快照(最多 500 檔,含現價 / 漲跌 / 量)                         |
| `stock_kbars(symbol, start, end)`                 | 歷史 K 棒(1 分鐘),`start`/`end` 格式 `YYYY-MM-DD`                |
| `stock_ticks(symbol, date, query_type, ...)`      | 歷史 Tick,`query_type`: `"AllDay"` / `"RangeTime"` / `"LastCount"` |
| `stock_credit_enquiries(symbols)`                 | 個股融資融券餘額                                                    |
| `stock_short_sources(symbols)`                    | 可借券來源與張數                                                    |
| `futures_kbars(symbol, start, end)`               | 期貨歷史 K 棒(支援連續合約 `TXFR1`/`TXFR2`)                       |
| `market_scanners(scanner_type, ascending, count)` | 市場排行掃描器                                                      |
| `market_punish()`                                 | 處置股清單                                                          |
| `market_notice()`                                 | 注意股清單                                                          |

> `scanner_type` 可用值:`"ChangePercentRank"` / `"ChangePriceRank"` / `"DayRangeRank"` / `"VolumeRank"` / `"AmountRank"`

### 帳務查詢

| 工具                                                   | 說明                                 |
| ------------------------------------------------------ | ------------------------------------ |
| `account_balance()`                                    | 股票帳戶可用餘額                     |
| `stock_trading_limits()`                               | 股票交易額度(含融資 / 融券額度)    |
| `list_settlements()`                                   | 交割資訊(T / T+1 / T+2)            |
| `futures_margin()`                                     | 期貨保證金(可用保證金、風險指標等) |
| `list_profit_loss(account_type, begin_date, end_date)` | 已實現損益,按日期區間查詢           |
| `list_position_detail(account_type, detail_id)`        | 持倉明細(含成本價、進場日)         |

> `account_type`:`"stock"` 或 `"futopt"`

---

## 核心模組說明

### `CredentialsLoader`(`src/core/credentials.py`)

- 單一職責:只負責讀取與驗證金鑰
- 以 `frozen=True` dataclass 保存,防止意外修改
- 缺少欄位時拋出含明確說明的 `KeyError`

### `ApiClient`(`src/core/api_client.py`)

- 封裝 `login` / `logout` 流程
- 支援 `with` 語法,確保 `logout` 必然執行
- 未連線前訪問 `.api` 屬性拋出 `RuntimeError`
- 所有模組共用同一 `ApiClient` 實例(避免超過 5 connections/person ID 上限)
- 若 credentials 含 CA 欄位,則**模擬與真實模式皆啟動憑證**(期貨 / 選擇權下單需要)

### Trading 模組(`src/trading/`)

| 模組                | 類別            | 主要方法                                                                                                                                    |
| ------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `stock_trader.py`   | `StockTrader`   | buy / sell / market_buy / market_sell / buy_odd / sell_odd / margin_buy / short_sell / cancel / update_order / list_trades / list_positions |
| `futures_trader.py` | `FuturesTrader` | open_long / open_short / close_long / close_short / cancel / update_order / list_positions                                                  |
| `options_trader.py` | `OptionsTrader` | buy / sell / cancel / contract_info / list_positions                                                                                        |

### Market 模組(`src/market/`)

| 模組                | 類別            | 主要方法                                                                            |
| ------------------- | --------------- | ----------------------------------------------------------------------------------- |
| `stock_market.py`   | `StockMarket`   | stock_snapshot / stock_kbars / stock_ticks / credit_enquiries / short_stock_sources |
| `futures_market.py` | `FuturesMarket` | futures_kbars                                                                       |
| `market_info.py`    | `MarketInfo`    | market_scanners / market_punish / market_notice                                     |

### Accounting 模組(`src/accounting/`)

| 模組                 | 類別             | 主要方法                                            |
| -------------------- | ---------------- | --------------------------------------------------- |
| `stock_account.py`   | `StockAccount`   | account_balance / trading_limits / list_settlements |
| `futures_account.py` | `FuturesAccount` | futures_margin                                      |
| `common_account.py`  | `CommonAccount`  | list_profit_loss / list_position_detail             |

### OptionStrategy 模組(`src/option_strategy/`)

純資料驅動的多腿策略定義層,不依賴 Shioaji API,可獨立測試。

| 類型                            | 策略數量 | 範例                                                                          |
| ------------------------------- | -------- | ----------------------------------------------------------------------------- |
| 純選擇權(`_pure_option.py`)   | 45 種    | long_call / straddle / iron_condor / calendar_call_spread / double_diagonal … |
| 含股票腿(`_stock_legged.py`)  | 6 種     | covered_call / protective_put / collar / cash_secured_put …                   |
| 含期貨腿(`_future_legged.py`) | 7 種     | long_synthetic_future / protective_futures_put / futures_collar …             |

- `Strategy` — frozen dataclass,持有 `strategy_id` 與 `legs: tuple[Leg, ...]`
- `OptionLeg` / `StockLeg` / `FutureLeg` — 各腿類型,描述 action(BUY/SELL)、qty、right、strike、expiry
- `get_strategy(strategy_id)` — 依 ID 取得策略,找不到時拋出 `KeyError`
- `server.py` 使用此模組實作 `strategy_list` / `strategy_info` / `strategy_execute` 三個 MCP 工具

### 策略模組(`src/grid_martingale_strategy/`)

網格與馬丁已拆分為兩個獨立策略,共用 `base/` 骨架(Template Method)與 `adapters/` 基礎設施。  
`grid/` 與 `martingale/` **互不 import**,各自持有專屬 config 與 position book。

| 子模組        | 職責                                                                                      |
| ------------- | ----------------------------------------------------------------------------------------- |
| `base/`       | `BaseStrategy`、`TradeJournal`、`RiskPolicy`(`GridRiskPolicy` / `MartingaleRiskPolicy`) |
| `grid/`       | `GridStrategy`、`GridConfig`、`GridPositionBook`、`build_grid_strategy()`                 |
| `martingale/` | `MartingaleStrategy`、`MartingaleConfig`、`LayerBook`、`build_martingale_strategy()`      |
| `adapters/`   | `MarketAdapter` + stock/futures 實作                                                      |
| `risk/`       | `GridRisk`、`MartingaleStockRisk`、`MartingaleFuturesRisk`                                |

**對外 API(`__init__.py`):**

| 符號                                                                  | 說明               |
| --------------------------------------------------------------------- | ------------------ |
| `GridStrategy` / `build_grid_strategy`                                | 純網格             |
| `MartingaleStrategy` / `build_martingale_strategy`                    | 純馬丁             |
| `GridConfig` / `MartingaleConfig`                                     | 各自設定 dataclass |
| `MarketType` / `Direction` / `GridMode` / `QtyMode` / `StrategyState` | 共用列舉           |

> `MartingaleConfig` 第一個參數為 `symbol`(拆分後新增)。  
> 舊路徑 `config/grid_config.py`、`config/martingale_config.py` 仍 re-export,canonical 位置分別在 `grid/`、`martingale/`。

```python
from src.grid_martingale_strategy import (
    build_grid_strategy, build_martingale_strategy,
    GridConfig, MartingaleConfig,
    MarketType, Direction, GridMode, QtyMode,
)

grid_cfg = GridConfig("2330", 1000.0, 800.0, 10, GridMode.ARITHMETIC, grid_unit_qty=1)
grid = build_grid_strategy(MarketType.STOCK, client, grid_cfg)
grid.initialize(); grid.start()

mart_cfg = MartingaleConfig("2330", 2.0, -0.05, 3, 2, 0.03, QtyMode.MULTIPLY)
mart = build_martingale_strategy(MarketType.STOCK, client, mart_cfg)
mart.initialize(); mart.start()
```

---

## 執行測試

所有測試皆在 `simulation=True` 模擬模式下執行,**不會觸發真實交易**。

```bash
uv run python tests/test_stock.py
uv run python tests/test_futures.py
uv run python tests/test_market.py
uv run python tests/test_accounting.py
uv run python tests/test_options.py
```

| 測試檔               | 必要條件                | 備註                                                       |
| -------------------- | ----------------------- | ---------------------------------------------------------- |
| `test_stock.py`      | 僅 api_key / secret_key | 股票模擬完整測試                                           |
| `test_futures.py`    | CA 憑證(建議)         | 無 CA 或帳戶未在 TAIFEX 簽署時,下單步驟自動跳過並顯示警告 |
| `test_market.py`     | 僅 api_key / secret_key | 行情查詢不需 CA                                            |
| `test_accounting.py` | 僅 api_key / secret_key | 帳務查詢不需 CA                                            |
| `test_options.py`    | 僅 api_key / secret_key | 合約與持倉查詢;自動尋找當前有效 TXO 合約                  |

**`main.py` 快速端對端測試**(含 Grid / Martingale):

```bash
python main.py                  # 全部測試(stock + futures + options + grid + martingale)
python main.py stock            # 只測股票
python main.py futures          # 只測期貨
python main.py options          # 只測選擇權
python main.py grid             # 只測純網格(現股)
python main.py grid_futures     # 只測純網格(期貨)
python main.py martingale       # 只測純馬丁(現股)
python main.py martingale_futures  # 只測純馬丁(期貨)
```

---

## 環境變數

| 變數            | 預設      | 說明                 |
| --------------- | --------- | -------------------- |
| `SJ_SIMULATION` | `true`    | `false` 切換真實下單 |
| `MCP_HOST`      | `0.0.0.0` | server 監聽 host     |
| `MCP_PORT`      | `9090`    | server 監聽 port     |
| `LOG_DIR`       | `logs`    | log 檔目錄           |

---

## Design Principles

- **SRP**:每個 class 只負責一件事(交易 / 行情 / 帳務明確分層)
- **依賴注入**:所有模組接收 `ApiClient`,不直接建立連線
- **共用連線**:`_Session` 管理單一 `ApiClient` 生命週期,防止超過連線數上限
- **語意方法名**:`open_long` / `close_short` 優於 `place_order(octype=...)`
- **不回傳 None**:錯誤以例外表達(`ValueError` / `RuntimeError`)
- **非同步行情與帳務**:市場資料與帳務工具使用 `async + run_in_executor` 避免卡頓

---

## ⚠️ 注意事項

- **預設 `SJ_SIMULATION=true`,絕對不會動用真實帳戶。**
- 期貨 / 選擇權委託(含模擬模式)需在 `credentials` 填入 CA 欄位,且帳戶需已在 TAIFEX 完成 CA 簽署。
- 切換真實模式前,請確認 CA 欄位(`ca_path`、`ca_password`、`person_id`)正確填寫。
- 真實下單操作不可逆,請在確認合約、價格、數量後再執行。
- 行情查詢限速:50 req / 5s;帳務查詢限速:25 req / 5s。