API 規格文檔
概述
Investment Agent API 提供 RESTful 端點(含 SSE 串流回應),支援股票查詢、分析和即時對話。
技術棧: - FastAPI - Pydantic (資料驗證) - SSE (串流回應)
Base URL: http://localhost:8000
端點清單
1. 健康檢查
GET /health
回應:
rows 與 results 內容相同,都是去重後的事件物件。
{
"status": "ok",
"timestamp": "2025-02-01T10:30:00Z",
"database": "connected",
"services": {
"llm": "ok",
"vector_store": "ok"
}
}
2. 股票查詢
2.1 搜尋股票
GET /api/stocks/search?q={query}
參數:
- q: 搜尋關鍵字(股票代碼或名稱)
回應:
{
"results": [
{
"ticker": "2330.TW",
"name": "台積電",
"market": "taiwan",
"industry": "半導體",
"surveillance_status": "normal"
},
{
"ticker": "2317.TW",
"name": "鴻海",
"market": "taiwan",
"industry": "電子製造",
"surveillance_status": "attention"
}
],
"total": 2
}
surveillance_status 可為 normal、attention、disposition。
2.2 獲取股票資訊
GET /api/stocks/{ticker}
回應:
{
"ticker": "2330.TW",
"name": "台積電",
"market": "taiwan",
"industry": "半導體",
"current_price": 625.0,
"price_change": 5.0,
"price_change_pct": 0.81,
"volume": 25000000,
"last_updated": "2025-02-01T13:30:00Z",
"surveillance_status": "normal"
}
2.2.1 查詢台股注意/處置事件
GET /api/market-surveillance/events?ticker=3026.TW&status=disposition
參數:
- ticker: 股票代號,可選
- status: attention 或 disposition,可選
- exchange: twse 或 tpex,可選
- start_date, end_date: 公告日期區間 (YYYY-MM-DD,可選)
- include_unmapped: 是否包含權證、可轉債等無法映射 ticker 的資料列
- limit, offset: 分頁
回應:
{
"results": [
{
"id": 7,
"ticker": "3026.TW",
"security_code": "3026",
"security_name": "禾伸堂",
"source_exchange": "twse",
"status_type": "disposition",
"announce_date": "2026-05-25",
"effective_start_date": "2026-05-26",
"effective_end_date": "2026-06-08",
"disposition_measure": "第二次處置",
"matching_interval_minutes": 20
}
],
"total": 1
}
matching_interval_minutes 為處置撮合頻率分鐘數;例如 約每五分鐘撮合一次 回傳 5,約每二十分鐘撮合一次 回傳 20。注意事件或無法解析時為 null。
2.2.2 查詢目前處置中股票名單
前端處置名單請使用此 endpoint;預設每個 ticker 最多一筆,避免同一檔股票因多筆重疊有效處置事件而重複顯示。若傳 status=attention 或 status_type=attention,則回傳 as_of 當日或之前最新可用公告日的注意資料;非公告日不會因為 as_of 當天沒有 row 而直接回空清單。
GET /api/market-surveillance/active-dispositions?as_of=2026-05-29&limit=500
注意清單範例:
GET /api/market-surveillance/active-dispositions?as_of=2026-06-03&status=attention&limit=500
注意模式下 summary.requested_as_of 是前端要求日期,summary.data_as_of 是實際回傳的注意公告日期,summary.date_mode 固定為 latest_available。
回應:
{
"results": [
{
"ticker": "6806.TW",
"security_name": "森崴能源",
"status_type": "disposition",
"announce_date": "2026-05-28",
"effective_start_date": "2026-05-29",
"effective_end_date": "2026-06-11",
"matching_interval_minutes": 60
},
{
"ticker": "4916.TW",
"security_name": "事欣科",
"status_type": "disposition",
"announce_date": "2026-05-25",
"effective_start_date": "2026-05-26",
"effective_end_date": "2026-06-08",
"matching_interval_minutes": 5
}
],
"rows": [
{
"ticker": "6806.TW",
"security_name": "森崴能源",
"status_type": "disposition",
"announce_date": "2026-05-28",
"effective_start_date": "2026-05-29",
"effective_end_date": "2026-06-11",
"matching_interval_minutes": 60
},
{
"ticker": "4916.TW",
"security_name": "事欣科",
"status_type": "disposition",
"announce_date": "2026-05-25",
"effective_start_date": "2026-05-26",
"effective_end_date": "2026-06-08",
"matching_interval_minutes": 5
}
],
"total": 2,
"summary": {
"as_of": "2026-05-29",
"dedupe_by": "ticker"
}
}
注意清單回應中的 summary 範例:
{
"total": 36,
"summary": {
"as_of": "2026-06-02",
"requested_as_of": "2026-06-03",
"data_as_of": "2026-06-02",
"date_mode": "latest_available",
"dedupe_by": "none"
}
}
2.2.3 查詢今日離開處置名單
查詢 as_of 當天已經不在處置中的股票。API 會使用前一個交易日作為 effective_end_date 比對日;若無法解析交易日,fallback 為 as_of - 1 day。
GET /api/market-surveillance/exited-dispositions?as_of=2026-06-12&limit=500
回應:
{
"results": [
{
"ticker": "6806.TW",
"security_name": "森崴能源",
"status_type": "disposition",
"announce_date": "2026-05-28",
"effective_start_date": "2026-05-29",
"effective_end_date": "2026-06-11",
"matching_interval_minutes": 60
}
],
"rows": [
{
"ticker": "6806.TW",
"security_name": "森崴能源",
"status_type": "disposition",
"announce_date": "2026-05-28",
"effective_start_date": "2026-05-29",
"effective_end_date": "2026-06-11",
"matching_interval_minutes": 60
}
],
"total": 1,
"summary": {
"as_of": "2026-06-12",
"dedupe_by": "ticker",
"exit_date_field": "effective_end_date",
"exit_effective_end_date": "2026-06-11"
}
}
2.2.4 查詢單檔目前監管狀態
GET /api/market-surveillance/stocks/{ticker}/status
回應:
{
"ticker": "3026.TW",
"status": "disposition",
"as_of": "2026-05-29",
"near_disposition_threshold": null,
"active_events": [
{
"ticker": "3026.TW",
"status_type": "disposition",
"matching_interval_minutes": 20
}
]
}
near_disposition_threshold 僅在單檔距離處置條件只差一次可計算的注意事件時回傳;否則為 null。前端只需顯示 upper_close、lower_close、min_volume,不需也不會收到內部累計規則文字。
回傳物件範例:
{
"as_of": "2026-05-29",
"trading_date": "2026-05-29",
"source_exchange": "twse",
"upper_close": 612.0,
"lower_close": null,
"min_volume": 2500000
}
2.3 獲取歷史資料
GET /api/stocks/{ticker}/ohlc?start_date={start}&end_date={end}&limit={limit}
參數:
- start_date: 開始日期 (YYYY-MM-DD, 可選)
- end_date: 結束日期 (YYYY-MM-DD, 可選)
- limit: 限制筆數 (可選)
回應:
{
"ticker": "2330.TW",
"data": [
{
"date": "2025-02-01",
"open": 620.0,
"high": 628.0,
"low": 618.0,
"close": 625.0,
"volume": 25000000
},
// ...
],
"total": 90
}
2.4 獲取技術指標
GET /api/stocks/{ticker}/indicators?latest=true
參數:
- latest: 只取最新 (預設 true)
- days: 歷史天數 (可選)
回應:
{
"ticker": "2330.TW",
"timestamp": "2025-02-01",
"indicators": {
"ma": {
"5": 622.5,
"20": 615.3,
"50": 598.7
},
"rsi": 65.4,
"macd": {
"value": 2.5,
"signal": 1.8,
"histogram": 0.7
},
"kd": {
"k": 72.3,
"d": 68.9
},
"bollinger": {
"upper": 635.0,
"middle": 615.0,
"lower": 595.0
}
}
}
3. 股票分析
3.1 執行分析 (同步)
POST /api/analyze
請求:
{
"ticker": "2330.TW",
"analysis_type": "comprehensive", // comprehensive, technical, trend
"timeframe": "3_months" // 1_month, 3_months, 6_months, 1_year
}
回應:
{
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"ticker": "2330.TW",
"timestamp": "2025-02-01T10:30:45Z",
"analysis": {
"summary": "台積電目前處於中性偏多區間...",
"trend": {
"overall": "bullish",
"short_term": "neutral",
"medium_term": "bullish"
},
"key_levels": {
"support": [600, 590, 580],
"resistance": [630, 640, 650]
},
"indicators_interpretation": {
"rsi": "RSI 65.4,處於中性偏多區間,尚未超買",
"macd": "MACD 金叉形成,短期趨勢偏多",
"ma": "價格站上 20MA,中期趨勢向上"
},
"risks": [
"短期 RSI 接近超買區,注意回調風險",
"成交量較前期萎縮,需觀察後續量能"
],
"recommendations": [
"觀察 630 壓力位能否突破",
"若回調至 600 支撐位可考慮進場",
"設定停損於 590 以下"
]
},
"metadata": {
"confidence": 0.75,
"verification_passed": true,
"iterations": 1,
"execution_time_ms": 3250
}
}
5. 系統管理
5.1 觸發資料更新
POST /api/admin/update-data
請求:
{
"markets": ["taiwan", "us"], // 可選,預設全部
"force": false // 強制更新
}
回應:
{
"status": "started",
"job_id": "update-20250201-103045",
"estimated_time_minutes": 15
}
5.2 查詢任務狀態
GET /api/admin/jobs/{job_id}
回應:
{
"job_id": "update-20250201-103045",
"status": "running", // pending, running, completed, failed
"progress": {
"current": 500,
"total": 1700,
"percentage": 29.4
},
"started_at": "2025-02-01T10:30:45Z",
"estimated_completion": "2025-02-01T10:45:00Z"
}
6. 題材 (Themes)
題材(概念股)來自 coverage 知識庫的 coverage_theme_membership,也就是 data/my-tw-coverage/themes/*.md 匯入後的主題成員資料。這份資料比策略用的 stock_group concept 群組更完整。API 只回傳最新 knowledge_as_of 快照的題材資料。
6.1 題材清單
GET /api/themes
回應:
[
{
"theme_id": "AI 伺服器",
"theme_name": "AI 伺服器",
"member_count": 148,
"is_eligible": true
}
]
6.2 查詢題材對應個股列表
GET /api/themes/{theme_id}/stocks?limit={limit}&offset={offset}
參數:
- theme_id: coverage 題材名稱(例如 AI 伺服器、CoWoS、電動車;URL path 需做 URL encoding)
- limit: 可選,每頁筆數,預設 themes.default_limit(200),上限 themes.max_limit(1000,超過自動截到上限)
- offset: 可選,分頁位移,預設 0
回應:
{
"theme_id": "AI 伺服器",
"theme_name": "AI 伺服器",
"found": true,
"member_count": 148,
"stocks": [
{
"ticker": "2330.TW",
"name": "台積電",
"english_name": "TSMC",
"industry": "半導體",
"sector": "科技"
}
]
}
題材不存在時,回傳 found: false 與空 stocks 陣列(HTTP 200)。
FastAPI 實現
main.py
檔案: backend/api/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import logging
# 初始化日誌
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""應用生命週期管理"""
# 啟動時
logger.info("初始化資料庫連接...")
await init_database()
logger.info("載入 LLM 和 Agents...")
await init_agents()
logger.info("API 服務啟動完成")
yield
# 關閉時
logger.info("關閉資料庫連接...")
await close_database()
# 建立 FastAPI app
app = FastAPI(
title="Cortex Investment Agent API",
version="1.0.0",
lifespan=lifespan
)
# CORS 設定
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Production 應限制來源
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 載入路由
from api.routes import stocks, analyze, chat, admin
app.include_router(stocks.router, prefix="/api/stocks", tags=["stocks"])
app.include_router(analyze.router, prefix="/api", tags=["analyze"])
app.include_router(chat.router, prefix="/ws", tags=["chat"])
app.include_router(admin.router, prefix="/api/admin", tags=["admin"])
@app.get("/health")
async def health_check():
"""健康檢查"""
return {
"status": "ok",
"timestamp": datetime.now().isoformat(),
"database": "connected", # 實際檢查
"services": {
"llm": "ok",
"vector_store": "ok"
}
}
路由範例: analyze.py
檔案: backend/api/routes/analyze.py
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from typing import Literal
from agents.coordinator import StockAnalysisWorkflow
from database.crud import get_db
router = APIRouter()
class AnalyzeRequest(BaseModel):
ticker: str
analysis_type: Literal["comprehensive", "technical", "trend"] = "comprehensive"
timeframe: Literal["1_month", "3_months", "6_months", "1_year"] = "3_months"
class AnalyzeResponse(BaseModel):
request_id: str
ticker: str
timestamp: str
analysis: dict
metadata: dict
@router.post("/analyze", response_model=AnalyzeResponse)
async def analyze_stock(
request: AnalyzeRequest,
db = Depends(get_db)
):
"""執行股票分析"""
# 驗證股票存在
stock = await StockCRUD.get_stock(db, request.ticker)
if not stock:
raise HTTPException(status_code=404, detail="股票不存在")
# 執行分析
workflow = get_analysis_workflow() # 從 app state 獲取
result = await workflow.run(
ticker=request.ticker,
analysis_type=request.analysis_type,
timeframe=request.timeframe
)
return AnalyzeResponse(
request_id=str(uuid.uuid4()),
ticker=request.ticker,
timestamp=datetime.now().isoformat(),
analysis=result.to_dict(),
metadata={
"confidence": result.confidence,
"verification_passed": result.verification_passed,
"iterations": result.iterations
}
)
投資組合 Portfolio
交易列表
GET /api/portfolio/transactions
列出目前登入會員的交易紀錄。此端點需登入,沿用 portfolio API 的 JWT 驗證。每筆 row 維持既有交易列表 response shape,包含交易基本欄位、broker 欄位、當沖配對欄位與 warning/matched/unmatched 欄位。
Query Parameters:
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
limit |
integer | no | portfolio.transaction_list.default_limit |
回傳筆數;backend clamp 到設定的最大值。 |
ticker |
string | no | null |
篩選單一個股交易列表;backend 會正規化為大寫,例如 2330.tw 以 2330.TW 查詢。 |
Request modes:
GET /api/portfolio/transactions:全交易列表,既有 response shape 不變。GET /api/portfolio/transactions?ticker=2330.TW:單一個股交易列表,既有 response shape 不變。- 指定 ticker 且無交易時回傳
[]。
類股配置彙總
GET /api/portfolio/allocation
依目前仍持有的正部位(position_qty > 0)彙總股票分類、投資總額與配置比例。此端點需登入,沿用 portfolio API 的 JWT 驗證。
Query Parameters:
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
type |
Group | ETF |
no | Group |
Group 依產業分類;ETF 依 ETF/個股分類。 |
分類規則:
type=Group:優先使用stocks.industry,缺失時使用stocks.sector,仍缺失時歸入Unclassified。type=ETF:使用etf_funds.etf_ticker判斷 ETF,回傳etf與stock兩種 bucket。Response format 不變。
回應:
{
"total_market_value": 11500.0,
"total_cost_basis": 11000.0,
"groups": [
{
"category": "Semiconductors",
"category_source": "industry",
"market_value": 10500.0,
"cost_basis_total": 10000.0,
"allocation_pct": 91.3043,
"holdings_count": 2,
"tickers": ["2330.TW", "8299.TWO"]
}
]
}
type=ETF 範例:
{
"total_market_value": 4500.0,
"total_cost_basis": 4100.0,
"groups": [
{
"category": "stock",
"category_source": "asset_type",
"market_value": 2700.0,
"cost_basis_total": 2500.0,
"allocation_pct": 60.0,
"holdings_count": 2,
"tickers": ["2330.TW", "8299.TWO"]
},
{
"category": "etf",
"category_source": "asset_type",
"market_value": 1800.0,
"cost_basis_total": 1600.0,
"allocation_pct": 40.0,
"holdings_count": 1,
"tickers": ["0050.TW"]
}
]
}
market_value 以 market_price * position_qty 計算;若市價缺失,使用 cost_basis_total 作為 fallback。allocation_pct 以市場價值為分母,四捨五入到小數第 4 位。空投資組合回傳 total_market_value=0.0、total_cost_basis=0.0、groups=[]。
投資組合期間績效
GET /api/portfolio/performance
取得目前使用者持股的未實現績效彙總與 day/month/year 期間變化。此端點需登入,沿用 portfolio API 的 JWT 驗證。
Query Parameters:
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
ticker |
string | no | null |
單一 ticker 期間績效;不帶時為整體投資組合。 |
type |
Group | ETF |
no | null |
分組期間績效;Group 依產業分類,ETF 依 ETF/個股分類。不可與 ticker 同時使用。 |
Request modes:
GET /api/portfolio/performance:整體投資組合期間績效,既有 response shape 不變。GET /api/portfolio/performance?ticker=2330.TW:單一 ticker 期間績效,既有 response shape 不變。GET /api/portfolio/performance?type=Group:產業分組期間績效,分類口徑比照GET /api/portfolio/allocation?type=Group。GET /api/portfolio/performance?type=ETF:ETF / 個股分組期間績效,分類口徑比照GET /api/portfolio/allocation?type=ETF。ticker與type同時提供回 HTTP 422。
既有整體 / 單一 ticker 回應:
{
"as_of_date": "2026-06-22",
"ticker": null,
"positions_count": 2,
"market_value": 2400.0,
"cost_basis_total": 2000.0,
"total_unrealized_pnl": 400.0,
"total_unrealized_pnl_pct": 20.0,
"periods": {
"day": {
"label": "day",
"baseline_date": "2026-06-19",
"baseline_value": 2420.0,
"unrealized_pnl": -20.0,
"unrealized_pnl_pct": -0.8264
},
"month": {
"label": "month",
"baseline_date": "2026-06-01",
"baseline_value": 2200.0,
"unrealized_pnl": 200.0,
"unrealized_pnl_pct": 9.0909
},
"year": {
"label": "year",
"baseline_date": "2026-01-02",
"baseline_value": 1700.0,
"unrealized_pnl": 700.0,
"unrealized_pnl_pct": 41.1765
}
}
}
分組回應(type=Group / type=ETF):
{
"type": "ETF",
"as_of_date": "2026-06-22",
"positions_count": 2,
"market_value": 2200.0,
"cost_basis_total": 1800.0,
"total_unrealized_pnl": 400.0,
"total_unrealized_pnl_pct": 22.2222,
"groups": [
{
"category": "stock",
"category_source": "asset_type",
"positions_count": 1,
"market_value": 1200.0,
"cost_basis_total": 1000.0,
"total_unrealized_pnl": 200.0,
"total_unrealized_pnl_pct": 20.0,
"periods": {
"day": {
"label": "day",
"baseline_date": "2026-06-19",
"baseline_value": 1180.0,
"unrealized_pnl": 20.0,
"unrealized_pnl_pct": 1.6949
},
"month": {
"label": "month",
"baseline_date": "2026-06-01",
"baseline_value": 1100.0,
"unrealized_pnl": 100.0,
"unrealized_pnl_pct": 9.0909
},
"year": {
"label": "year",
"baseline_date": "2026-01-02",
"baseline_value": 900.0,
"unrealized_pnl": 300.0,
"unrealized_pnl_pct": 33.3333
}
},
"tickers": ["2330.TW"]
},
{
"category": "etf",
"category_source": "asset_type",
"positions_count": 1,
"market_value": 1000.0,
"cost_basis_total": 800.0,
"total_unrealized_pnl": 200.0,
"total_unrealized_pnl_pct": 25.0,
"periods": {
"day": {
"label": "day",
"baseline_date": "2026-06-19",
"baseline_value": 980.0,
"unrealized_pnl": 20.0,
"unrealized_pnl_pct": 2.0408
},
"month": {
"label": "month",
"baseline_date": "2026-06-01",
"baseline_value": 950.0,
"unrealized_pnl": 50.0,
"unrealized_pnl_pct": 5.2632
},
"year": {
"label": "year",
"baseline_date": "2026-01-02",
"baseline_value": 800.0,
"unrealized_pnl": 200.0,
"unrealized_pnl_pct": 25.0
}
},
"tickers": ["0050.TW"]
}
]
}
type=Group 的 category_source 為 industry / sector / unclassified;type=ETF 的 category_source 為 asset_type。若某期間任一 group holding 缺少 current 或 baseline close,該 group 的對應 period 欄位回傳 null。空投資組合回傳 zero totals 與 groups=[]。
投資操作績效(全時/指定期間)
GET /api/portfolio/investment-performance
此 endpoint 與既有 /api/portfolio/performance 不同:既有 endpoint 只看「目前持倉市場變化」;本 endpoint 計入有效 closure 的一般/當沖已實現、依除息日 entitlement 計算的現金股利、期末未實現與資金投入報酬。
| Query | Type | Required | Default | Notes |
|---|---|---|---|---|
period |
all/day/week/month/quarter/year |
no | all |
操作 lot 開倉期間 |
value |
string | period 非 all 時 yes | null |
day: YYYYMMDD/YYYY-MM-DD; week: YYYY-Www; month: YYYYMM/YYYY-MM; quarter: YYYYQn/YYYY-Qn; year: YYYY |
ticker |
string | no | null |
可查已清倉 ticker;與 type 互斥 |
type |
Group/ETF |
no | null |
Group=industry→sector→Unclassified; ETF=etf/stock |
非 all 只計入該期間開倉 lots;期間前舊 lots 的損益與股利只出現在 all/cumulative。ex_dividend_date <= end_date(timeseries 為 point period_end)即依除息權利認列已實現股利,即使最新股價停在前一交易日也不延後;更晚 event 只進 expected_cash_dividend,不進 total P&L。歷史股數使用除息日前 transactions/closures 重建;缺價時 unrealized_pnl/total_pnl/return_pct/equity_multiple 為 null。
主要 response 欄位:period/value/start_date/end_date/valuation_date/is_partial/ticker、opened_lots_count/closed_lots_count、realized.{regular_pnl,day_trade_pnl,cash_dividend,total}、expected_cash_dividend、unrealized_pnl/total_pnl/capital_employed/return_pct/equity_multiple、data_quality。Grouped response 另含 type/classification_basis/overall/groups[]。
data_quality 會揭露 unmatched sells、missing-price tickers、standalone/unattributed cash dividends,以及逐 ticker/provider/month 的歷史股利 coverage;任何缺口都令 is_complete=false,但仍回傳可驗證的部分數值。
投資操作績效逐筆明細
GET /api/portfolio/investment-performance/details?period=month&value=2026-07&metric=cash_dividend&offset=0&limit=100
必填 metric:regular_pnl/day_trade_pnl/cash_dividend/expected_cash_dividend/realized_total/unrealized_pnl/capital_employed/opened_lots_count/closed_lots_count/total_pnl。period/value/ticker 沿用單期 endpoint;分類 drill-down 必須同時傳 type/category/category_source,由 backend 驗證 current metadata scope,且與 ticker 互斥。offset 預設 0,limit 預設 100、最大 1000。
Response 為 window/scope/metric/unit/value/reconciliation/pagination/items/data_quality。前端必須顯示 backend value,不可自行 sum 當頁 items;reconciliation.detail_value 在分頁前以全量 items 計算。capital_employed 取 capital_required 最大值。所有 items 依主要日期由新到舊;capital-flow cumulative/peak 仍按正向時間計算,只反轉 response 排列。Items 以 item_type=closure/dividend/lot/capital_flow 判別,欄位與完整 example 見 docs/frontend-integration/investment-performance.md。
投資操作績效序列
GET /api/portfolio/investment-performance/timeseries?start_date=2026-01-01&end_date=2026-07-10&interval=month
| Query | Type | Required | Default | Notes |
|---|---|---|---|---|
start_date |
date | yes | - | inclusive |
end_date |
date | yes | - | inclusive,不得晚於今日 |
interval |
day/week/month/quarter/year |
no | month |
chart bucket |
ticker |
string | no | null |
與 type 互斥 |
type |
Group/ETF |
no | null |
current metadata classification |
series[].points[] 同時回 period(該 bucket 開倉 cohort)與 cumulative(inception 至 point valuation date)。歷史 point 不倒灌 future closures 或今天才知道的 expected dividend。總點數超過 2000 回 HTTP 422。
Portfolio summary 股利修正
GET /api/portfolio/summary request/response shape 不變;dividends.events[].position_qty 的 received event 改用除息日前歷史 entitlement,pending event 使用台北今日 transactions/closures 的 current holdings。同 ticker/ex-date 的 cash_dividend transaction 優先且不與 derived event 重複。
錯誤處理
標準錯誤格式
{
"error": {
"code": "STOCK_NOT_FOUND",
"message": "股票代碼不存在",
"details": {
"ticker": "INVALID"
}
}
}
HTTP 狀態碼
200: 成功400: 請求錯誤404: 資源不存在429: 請求過於頻繁500: 伺服器錯誤
API 測試
Pytest 範例
import pytest
from httpx import AsyncClient
from api.main import app
@pytest.mark.asyncio
async def test_analyze_stock():
async with AsyncClient(app=app, base_url="http://test") as ac:
response = await ac.post(
"/api/analyze",
json={
"ticker": "2330.TW",
"analysis_type": "comprehensive",
"timeframe": "3_months"
}
)
assert response.status_code == 200
data = response.json()
assert "analysis" in data
assert data["ticker"] == "2330.TW"
文檔版本: 1.0
最後更新: 2025-02-01