跳轉到

API 規格文檔

概述

Investment Agent API 提供 RESTful 端點(含 SSE 串流回應),支援股票查詢、分析和即時對話。

技術棧: - FastAPI - Pydantic (資料驗證) - SSE (串流回應)

Base URL: http://localhost:8000


端點清單

1. 健康檢查

GET /health

回應:

rowsresults 內容相同,都是去重後的事件物件。

{
  "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 可為 normalattentiondisposition

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: attentiondisposition,可選 - exchange: twsetpex,可選 - 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=attentionstatus_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_closelower_closemin_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.tw2330.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,回傳 etfstock 兩種 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_valuemarket_price * position_qty 計算;若市價缺失,使用 cost_basis_total 作為 fallback。allocation_pct 以市場價值為分母,四捨五入到小數第 4 位。空投資組合回傳 total_market_value=0.0total_cost_basis=0.0groups=[]

投資組合期間績效

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
  • tickertype 同時提供回 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=Groupcategory_sourceindustry / sector / unclassifiedtype=ETFcategory_sourceasset_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_multiplenull

主要 response 欄位:period/value/start_date/end_date/valuation_date/is_partial/tickeropened_lots_count/closed_lots_countrealized.{regular_pnl,day_trade_pnl,cash_dividend,total}expected_cash_dividendunrealized_pnl/total_pnl/capital_employed/return_pct/equity_multipledata_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

必填 metricregular_pnl/day_trade_pnl/cash_dividend/expected_cash_dividend/realized_total/unrealized_pnl/capital_employed/opened_lots_count/closed_lots_count/total_pnlperiod/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_employedcapital_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