2025年8月10日 星期日

📈 AI 驅動的投資組合風險平台:Django、FastAPI、React、Flutter 全棧微服務與自動化 CI/CD 實踐

📈 AI 驅動的投資組合風險平台:Django、FastAPI、React、Flutter 全棧微服務與自動化 CI/CD 實踐


前言

在當今快節奏的金融市場中,及時、精準的風險評估對於投資決策至關重要。然而,傳統的手動數據處理和報告生成流程不僅耗時,還容易出錯。為了解決這個痛點,我們開發了一個 AI 驅動的投資組合風險評估與報告平台

您可以透過以下 GitHub 連結檢閱本專案的原始碼:https://github.com/BpsEason/investment-risk-platform.git

這個專案不僅是一個功能完整的應用程式,更是一個展示現代軟體工程實踐的典範,涵蓋了全棧微服務架構、自動化開發流程、多層次測試與完整的 CI/CD 管道。本文將深入剖析專案的技術亮點、關鍵程式碼,並解答開發過程中常見的問題。


專案核心技術與亮點

我們的平台融合了多種前沿技術,旨在實現高效率、高品質的開發與運維。

1. 🤖 AI 驅動程式碼生成 (AI-Driven Code Generation)

  • 核心思想: 藉由 generate_ai_code.py 腳本,我們將程式碼生成過程交給 AI。這不僅是自動化,更是知識傳承的自動化。AI 能根據預設好的 Prompt (*/prompts/*.txt),快速產出符合規範的 Django Model、DRF Serializer、React 組件、Flutter Widget 及其對應的測試文件。

  • 優勢: 極大加速專案啟動速度(Scaffold),確保程式碼風格與結構一致,並將開發者的精力從重複性工作解放出來,專注於核心業務邏輯的實現。

2. 🚀 全棧微服務架構 (Full-Stack Microservices Architecture)

  • 解耦與專業化: 專案採用了前後端分離與微服務的架構,各服務各司其職,易於維護與擴展。

    • Django: 擔任核心業務後台,負責金融風險指標的精準計算(如 VaR, CVaR, Sharpe Ratio),並提供 RESTful API。其強大的 ORM 和 Admin 介面非常適合處理業務邏輯與資料庫管理。

    • FastAPI: 作為高效能的數據 ETL 層,專門處理來自 CSV/Excel 等檔案的大量數據匯入與預處理。其異步 (Asynchronous) 能力確保了 I/O 密集型任務的高吞吐量。

    • React + D3.js: 構建網頁儀表板,利用 D3.js 實現互動式數據可視化,為決策者提供直觀的數據洞察。

    • Flutter: 開發原生行動應用,為使用者提供流暢的跨平台體驗

3. ✅ 自動化測試與 CI/CD (Automated Testing & CI/CD)

  • 品質保證: 專案實現了多層次測試,涵蓋單元測試、集成測試和 E2E 測試。

  • 深度驗證: 透過 Mutation Testing (Mutpy for Python, Stryker for JS) 來驗證測試的有效性,確保測試不是「假性」通過,而是真正能夠捕捉到程式碼的錯誤。

  • 持續整合: 藉助 GitHub Actions,每次提交程式碼都會自動觸發一連串的測試、程式碼品質檢查與覆蓋率報告生成(整合 Codecov)。CI/CD 流程還包含 API 文件可用性檢查,確保 API 文件的正確性。


關鍵程式碼解析

這部分將深入探討專案中幾個核心模組的程式碼實現,展現專案的設計巧思。

1. Django Model: PortfolioRisk

PortfolioRisk 模型負責儲存每一次的風險計算結果。其設計亮點在於使用了 UUIDField 作為主鍵,並結合 unique_together 來確保數據的完整性。

Python
# backend/django_risk_app/risk_metrics/models.py
import uuid
from django.db import models

# AI 生成程式碼開始...
class PortfolioRisk(models.Model):
    portfolio_id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        verbose_name="投資組合 ID"
    )
    metric = models.CharField(max_length=100, verbose_name="風險指標")
    value = models.FloatField(verbose_name="指標數值")
    calculated_at = models.DateTimeField(auto_now_add=True, verbose_name="計算時間")

    class Meta:
        verbose_name = "投資組合風險"
        verbose_name_plural = "投資組合風險"
        unique_together = ("portfolio_id", "metric")
        ordering = ['-calculated_at']

    def __str__(self):
        return f"{self.portfolio_id}{self.metric}: {self.value:.4f}"
# AI 生成程式碼結束

解析:

  • portfolio_id: 使用 UUIDField 而非傳統的整數主鍵,可以避免多個服務在創建資料時的 ID 衝突,尤其適合微服務架構。

  • unique_together: 確保同一個投資組合不會重複計算同一個指標,防止數據冗餘。

  • ordering: 預設按計算時間降序排列,這對於 API 獲取最新數據非常有用。

2. FastAPI 端點: /calculate-risk/etl/import-data

FastAPI 服務展示了其在處理數據 I/O 與計算方面的強大能力。

Python
# backend/fastapi_etl_service/main.py (部分)
# ...
@app.post("/calculate-risk", response_model=RiskCalculationResponse, tags=["risk_calculation"])
async def calculate_risk(request: RiskCalculationRequest):
    """根據歷史價格計算指定風險指標"""
    # ... 數據驗證邏輯 ...
    prices = pd.Series(d.get("price", 0) for d in request.data)
    returns = prices.pct_change().dropna()
    if returns.empty:
        raise HTTPException(status_code=400, detail="數據不足以計算收益率。")

    calculated_value = 0.0
    # ... VaR, CVaR, Sharpe Ratio 計算邏輯 ...

    return RiskCalculationResponse(
        metric=request.metric,
        value=float(calculated_value),
        unit=unit,
        description=description
    )

@app.post("/etl/import-data", tags=["etl"])
async def import_data(file: UploadFile = File(...)):
    """接收 CSV 或 Excel 文件並模擬匯入數據"""
    if not file.filename:
        raise HTTPException(status_code=400, detail="未提供文件名。")

    try:
        if file.filename.endswith('.csv'):
            df = pd.read_csv(io.StringIO((await file.read()).decode('utf-8')))
        elif file.filename.endswith(('.xls', '.xlsx')):
            df = pd.read_excel(io.BytesIO(await file.read()))
        else:
            raise HTTPException(status_code=400, detail="只支援 CSV 或 XLSX 格式的文件。")

        return {"message": f"文件 '{file.filename}' 已成功模擬匯入", "rows_processed": len(df.index)}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"文件處理失敗: {e}")

解析:

  • calculate_risk: 使用 pandasnumpy 進行底層的金融計算,展示了 Python 在科學計算生態的優勢。其同步的計算流程可以根據需要,調整為異步或交由 Celery 等後台任務處理,以避免阻塞主執行緒。

  • import-data: 利用 FastAPI 的 UploadFileFile 依賴注入,輕鬆處理檔案上傳。通過 io.StringIOio.BytesIO,將上傳的檔案直接讀取到記憶體中,再交由 pandas 處理,流程清晰高效。

3. React 組件: PortfolioRiskDisplay

前端組件負責將後端返回的 JSON 數據轉換為使用者友好的視覺化內容。

JavaScript
// frontend-react/src/components/PortfolioRiskDisplay.js
import React from 'react';

/**
 * 風險指標顯示元件
 * @param {Array<{ metric: string, value: number }>} riskData
 */
function PortfolioRiskDisplay({ riskData }) {
    // ... 數據驗證與渲染邏輯 ...
    return (
        <div className="grid grid-cols-1 md:grid-cols-3 gap-4 my-6">
            {riskData.map(({ metric, value }) => (
                <div key={metric} className="p-4 bg-white rounded-lg shadow-lg flex flex-col items-center justify-center">
                    <h3 className="text-xl font-semibold text-gray-700 mb-2">{metric}</h3>
                    <p className="mt-2 text-3xl font-bold text-blue-600">
                        {metric.toLowerCase().includes("ratio")
                            ? value.toFixed(2)
                            : `${(value * 100).toFixed(2)}%`}
                    </p>
                </div>
            ))}
        </div>
    );
}
export default PortfolioRiskDisplay;

解析:

  • props 傳遞: 通過 riskData prop 接收數據,遵循 React 的單向數據流原則。

  • 樣式化: 結合 Tailwind CSS,實現快速、響應式的 UI 佈局。

  • 數據格式化: 根據指標名稱(如是否包含 "ratio"),動態格式化顯示數值,體現了對數據展示細節的考量。

4. Flutter Widget: RiskMetricCard

RiskMetricCard 是一個可重用的 Widget,展示了 Flutter 在構建清晰 UI 方面的優勢。

Dart
// flutter-app/lib/widgets/risk_metric_card.dart
import 'package:flutter/material.dart';

/// 顯示單一風險指標的卡片
class RiskMetricCard extends StatelessWidget {
  final String metric;
  final double value;

  const RiskMetricCard({
    Key? key,
    required this.metric,
    required this.value,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),
      elevation: 6,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              metric,
              style: const TextStyle(
                fontSize: 20,
                fontWeight: FontWeight.bold,
                color: Colors.deepPurple,
              ),
            ),
            const SizedBox(height: 8),
            Text(
              metric.toLowerCase().contains('ratio')
                ? value.toStringAsFixed(2)
                : '${(value * 100).toStringAsFixed(2)}%',
              style: const TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.w600,
                color: Colors.blueAccent,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

解析:

  • Widget 組合: 透過 Card, Padding, Column 等 Widget 的嵌套,輕鬆構建出層次分明的卡片樣式。

  • 參數化: final 屬性確保 Widget 的不可變性,符合 Flutter 的最佳實踐。

  • 平台原生: 儘管程式碼是單一的,但在 Android 和 iOS 上都能渲染出符合各自平台規範的 UI。


常見問題 (FAQs)

Q1: 為什麼專案後端同時使用 Django 和 FastAPI?

A: 這是一個經典的微服務設計考量。

  • Django 擅長處理複雜的業務邏輯、ORM、使用者驗證和後台管理(Django Admin)。它適合用來構建穩定、功能豐富的核心應用。

  • FastAPI 則以其高效能和異步處理能力見長,非常適合擔任輕量級的 API Gateway 或專門處理 I/O 密集型任務(如文件匯入)。這種搭配讓我們能夠充分發揮兩種框架的優勢。

Q2: AI 生成的程式碼是否真的能用於生產環境?

A: AI 生成的程式碼是強大的開發輔助工具,而非終端產品

  • 我們的流程將 AI 程式碼視為高品質的 Scaffold

  • 所有生成的程式碼都會被註釋包裹,並附帶 請審閱後移除註釋並使用 的提示。這強調了人為審閱的重要性,確保程式碼符合業務需求、安全規範和團隊風格。

Q3: 專案的金融計算準確性如何保證?

A: 程式碼中的金融計算範例是基於歷史模擬法進行的簡化實現,目的是展示技術框架的可行性

  • 在實際生產環境中,我們會使用更成熟的金融計算庫,並結合專業的金融模型(如蒙地卡羅模擬、參數化模型等),並通過嚴格的數據驗證和回測,來確保計算的準確性。

Q4: 專案的部署與擴展性如何?

A: 專案的架構設計已充分考慮擴展性。

  • 本地開發: 使用 docker-compose up -d 即可一鍵啟動所有服務,極大簡化開發環境。

  • 生產環境: 由於各服務都是獨立的 Docker 容器,可以很方便地將它們部署到 Kubernetes 等容器編排平台,實現彈性擴展、服務發現和負載均衡。


總結

這個「AI 驅動的投資組合風險評估與報告平台」專案不僅展示了多種前沿技術的整合能力,更體現了對軟體開發生命週期的全面考量——從自動化開發、微服務設計,到嚴謹的測試與持續整合

它是一個功能完整、架構優良的範例專案,也是一個理想的學習與展示平台,證明開發者具備將複雜需求轉化為高效、可維護軟體系統的能力。

沒有留言:

張貼留言

熱門文章