建立量化交易儀表板

在本課程的最後一章,我們要整合所學的全部知識,建立一個完整的量化交易儀表板 (Dashboard)。這個儀表板可以:

  1. 監控即時股價與技術指標
  2. 顯示最新的交易信號
  3. 回測報告與績效指標
  4. 投資組合風險分析

使用 Plotly 建立互動式儀表板

Plotly 是一個可以建立互動式圖表的 Python 函式庫。與 Matplotlib 不同,Plotly 的圖表可以縮放、平移、懸停查看數據。

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import yfinance as yf
import pandas as pd
import numpy as np

# 下載資料
df = yf.download("2330.TW", start="2023-01-01", end="2024-12-31")

# 計算技術指標
df['MA10'] = df['Close'].rolling(10).mean()
df['MA30'] = df['Close'].rolling(30).mean()

# 建立子圖
fig = make_subplots(
    rows=3, cols=1,
    shared_xaxes=True,
    vertical_spacing=0.05,
    row_heights=[0.5, 0.25, 0.25],
    subplot_titles=('股價與移動平均線', 'RSI', '成交量')
)

# 上圖:K 線圖 + 移動平均線
fig.add_trace(go.Candlestick(
    x=df.index,
    open=df['Open'],
    high=df['High'],
    low=df['Low'],
    close=df['Close'],
    name='K 線'
), row=1, col=1)

fig.add_trace(go.Scatter(
    x=df.index, y=df['MA10'],
    line=dict(color='orange', width=1),
    name='MA10'
), row=1, col=1)

fig.add_trace(go.Scatter(
    x=df.index, y=df['MA30'],
    line=dict(color='purple', width=1),
    name='MA30'
), row=1, col=1)

# 中圖:RSI
# 計算 RSI
from ta.momentum import RSIIndicator
rsi = RSIIndicator(close=df['Close'], window=14).rsi()

fig.add_trace(go.Scatter(
    x=df.index, y=rsi,
    line=dict(color='purple', width=1),
    name='RSI (14)'
), row=2, col=1)

# RSI 超買超賣線
fig.add_hline(y=70, line_dash="dash", line_color="red", row=2, col=1)
fig.add_hline(y=30, line_dash="dash", line_color="green", row=2, col=1)

# 下圖:成交量
fig.add_trace(go.Bar(
    x=df.index, y=df['Volume'],
    name='成交量',
    marker_color='lightblue'
), row=3, col=1)

# 更新版面
fig.update_layout(
    title='台積電 (2330) 技術分析儀表板',
    xaxis_title='日期',
    yaxis_title='價格',
    template='plotly_dark',
    height=900,
    showlegend=True,
    xaxis_rangeslider_visible=False
)

fig.show()

建立交易信號儀表板

# === 信號產生器 ===
def generate_signals(df):
    """
    根據技術指標產生綜合交易信號
    回傳:-1 = 賣出, 0 = 中立, 1 = 買進
    """
    signals = pd.DataFrame(index=df.index)
    
    # 1. 移動平均線信號
    ma10 = df['Close'].rolling(10).mean()
    ma30 = df['Close'].rolling(30).mean()
    signals['MA_Signal'] = 0
    signals.loc[ma10 > ma30, 'MA_Signal'] = 1
    signals.loc[ma10 < ma30, 'MA_Signal'] = -1
    
    # 2. RSI 信號
    rsi = RSIIndicator(close=df['Close'], window=14).rsi()
    signals['RSI_Signal'] = 0
    signals.loc[rsi < 30, 'RSI_Signal'] = 1   # 超賣 → 買進
    signals.loc[rsi > 70, 'RSI_Signal'] = -1  # 超買 → 賣出
    
    # 3. 綜合信號
    signals['Overall'] = (
        signals['MA_Signal'] + signals['RSI_Signal']
    )
    
    return signals

signals = generate_signals(df)

# 顯示最新信號
latest = signals.iloc[-1]
print("=== 最新交易信號 ===")
print(f"移動平均線信號: {'買進' if latest['MA_Signal'] == 1 else '賣出' if latest['MA_Signal'] == -1 else '中立'}")
print(f"RSI 信號: {'買進' if latest['RSI_Signal'] == 1 else '賣出' if latest['RSI_Signal'] == -1 else '中立'}")
print(f"綜合信號: {'強烈買進 🔥' if latest['Overall'] >= 2 else '買進 📈' if latest['Overall'] == 1 else '中立 ➖' if latest['Overall'] == 0 else '賣出 📉' if latest['Overall'] == -1 else '強烈賣出 🚨'}")

建立回測報告

# === 回測報告產生器 ===
def generate_backtest_report(results):
    """從 Backtrader 結果產生報告"""
    strategy = results[0]
    
    # 取得分析數據
    sharpe = strategy.analyzers.sharpe.get_analysis()
    drawdown = strategy.analyzers.drawdown.get_analysis()
    returns = strategy.analyzers.returns.get_analysis()
    trades = strategy.analyzers.trades.get_analysis()
    
    report = {
        '績效指標': {
            '總報酬率': f"{returns.get('rtot100', 0):.2f}%",
            '年化報酬率': f"{returns.get('rnorm100', 0):.2f}%",
            '夏普比率': f"{sharpe.get('sharperatio', 'N/A'):.2f}" if sharpe.get('sharperatio') else 'N/A',
            '最大回撤': f"{drawdown['max']['drawdown']:.2f}%",
            '最大回撤期間': f"{drawdown['max']['len']} 天",
        },
        '交易統計': {}
    }
    
    if 'total' in trades:
        report['交易統計'] = {
            '總交易次數': trades['total']['total'],
            '獲利交易': trades['won']['total'],
            '虧損交易': trades['lost']['total'],
            '勝率': f"{trades['won']['total'] / trades['total']['total'] * 100:.1f}%",
            '平均獲利': f"${trades['won']['pnl']['average']:.2f}" if trades['won']['total'] > 0 else 'N/A',
            '平均虧損': f"${trades['lost']['pnl']['average']:.2f}" if trades['lost']['total'] > 0 else 'N/A',
        }
    
    return report

# 使用方式
report = generate_backtest_report(results)
for category, metrics in report.items():
    print(f"\n=== {category} ===")
    for key, value in metrics.items():
        print(f"{key}: {value}")

使用 FastAPI 建立量化交易 API

from fastapi import FastAPI, Query
from pydantic import BaseModel
import yfinance as yf
import pandas as pd

app = FastAPI(title="量化交易 API")

class StockAnalysis(BaseModel):
    ticker: str
    current_price: float
    ma10: float
    ma30: float
    rsi: float
    signal: str
    recommendation: str

@app.get("/analyze/{ticker}", response_model=StockAnalysis)
def analyze_stock(ticker: str):
    """分析單一股票並產生交易建議"""
    data = yf.download(ticker, period="6mo")
    close = data['Close']
    
    ma10 = close.rolling(10).mean().iloc[-1]
    ma30 = close.rolling(30).mean().iloc[-1]
    
    from ta.momentum import RSIIndicator
    rsi = RSIIndicator(close=close, window=14).rsi().iloc[-1]
    
    # 決定信號
    if ma10 > ma30 and rsi < 70:
        signal = "bullish"
        recommendation = "買進 📈"
    elif ma10 < ma30 and rsi > 30:
        signal = "bearish"
        recommendation = "賣出 📉"
    else:
        signal = "neutral"
        recommendation = "觀望 ⏸️"
    
    return StockAnalysis(
        ticker=ticker,
        current_price=round(float(close.iloc[-1]), 2),
        ma10=round(float(ma10), 2),
        ma30=round(float(ma30), 2),
        rsi=round(float(rsi), 2),
        signal=signal,
        recommendation=recommendation
    )

@app.get("/watchlist")
def scan_watchlist(tickers: str = Query("2330.TW,2317.TW,TSLA,AAPL")):
    """掃描多檔股票"""
    ticker_list = tickers.split(",")
    results = []
    
    for ticker in ticker_list:
        try:
            result = analyze_stock(ticker.strip())
            results.append(result.dict())
        except:
            results.append({"ticker": ticker, "error": "讀取失敗"})
    
    return {"results": results}

使用 Vibe Coding 建立儀表板

🔥 【量化儀表板詠唱範例】 「請幫我建立一個 Streamlit 量化交易儀表板: 1. 使用者可以輸入股票代號(支援台股美股)。 2. 顯示 K 線圖、MA10/MA30、RSI、MACD、成交量。 3. 顯示最新的綜合交易信號(買進/賣出/中立)。 4. 顯示一個回測報告表格(總報酬、夏普比率、最大回撤)。 5. 支援多股比較功能。 6. 使用深色主題,專業的金融風格。」

本日總結

在本章中,你學到了:

  1. Plotly 互動式圖表:建立專業級的技術分析圖表
  2. 多信號綜合判斷:整合 MA 與 RSI 產生買賣信號
  3. 回測報告產生器:自動化生成績效報告
  4. FastAPI 量化 API:將分析能力包裝成 API
  5. 完整的儀表板架構:從資料到信號到報告的完整流程

恭喜你完成了整個 量化交易回測系統 課程!

你現在已經具備了:

  • 📊 用程式抓取與分析股票資料的能力
  • 📈 計算與解讀技術指標的能力
  • 🔄 建立回測系統驗證策略的能力
  • 🛡️ 風險管理與投資組合最佳化的能力
  • 🚀 將量化交易系統部署為 API 的能力

這些技能無論是拿來打造自己的被動收入系統,或是作為量化交易工程師求職/接案,都能讓你擁有巨大的競爭優勢!

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!