iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0
生成式 AI

AI 給我錢錢錢 ! AI 股神養成計劃系列 第 23

Day 23:讓 Demo 動起來:把 AI 股價分析功能搬進網站

  • 分享至 

  • xImage
  •  

昨天我們已經把文章(Markdown)搬進網站,讓「展示館」開始有文字內容。今天要更進一步,把之前做過的 AI 股價分析功能 也搬進來,讓訪客可以直接在網站上體驗。這樣一來,我們的網站不只是文章展示館,而是 互動式 AI 投資 Demo 平台

實作

網站結構調整

在 pages 或 routes 新增一個 demo.jsx 頁面,並在側邊欄加上「Demo 體驗」選項。

jsx
// src/pages/demo.jsx
import { useState } from "react";
import { Button } from "@/components/ui/button";

export default function DemoPage() {
  const [ticker, setTicker] = useState("");
  const [result, setResult] = useState("");

  const handleAnalyze = async () => {
    setResult("分析中,請稍候...");
    const res = await fetch("/api/analyze", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ ticker }),
    });
    const data = await res.json();
    setResult(data.message);
  };

  return (
    <div className="p-6">
      <h1 className="text-2xl font-bold mb-4">AI 股價分析 Demo</h1>
      <div className="flex gap-2 mb-4">
        <input
          className="border p-2 rounded w-60"
          type="text"
          placeholder="輸入股票代號,例如 AAPL"
          value={ticker}
          onChange={(e) => setTicker(e.target.value)}
        />
        <Button onClick={handleAnalyze}>開始分析</Button>
      </div>
      <div className="p-4 border rounded bg-gray-50 min-h-[100px]">
        {result || "輸入股票代號開始分析"}
      </div>
    </div>
  );
}

這樣網站就會出現一個輸入框和按鈕,可以輸入股票代號並得到分析。

後端 API (股價 + AI)

我們需要一個 API route 來處理請求:

  • 用 yfinance 抓股價資料

  • 簡單用 matplotlib 畫趨勢圖

  • 丟給 AI 做解釋

python
# api/analyze.py (FastAPI / Flask 範例)
from fastapi import FastAPI, Request
import yfinance as yf
import matplotlib.pyplot as plt
import io, base64
import google.generativeai as genai

app = FastAPI()
genai.configure(api_key="YOUR_GEMINI_API_KEY")

@app.post("/api/analyze")
async def analyze(request: Request):
    data = await request.json()
    ticker = data["ticker"]

    # 抓取股價
    stock = yf.Ticker(ticker)
    hist = stock.history(period="1mo")

    # 繪製趨勢圖
    plt.figure(figsize=(8, 4))
    plt.plot(hist.index, hist["Close"], label="Close Price")
    plt.legend()
    buf = io.BytesIO()
    plt.savefig(buf, format="png")
    buf.seek(0)
    img_b64 = base64.b64encode(buf.read()).decode("utf-8")

    # 丟給 AI 解釋
    model = genai.GenerativeModel("gemini-1.5-flash")
    prompt = f"以下是 {ticker} 最近一個月的股價走勢:\n{hist.tail(5)}\n請用投資小白也能懂的語言解釋。"
    response = model.generate_content(prompt)

    return {
        "message": response.text,
        "chart": f"data:image/png;base64,{img_b64}"
    }

前端顯示圖表

修改剛剛的 demo.jsx,把 API 回傳的圖片也顯示出來:

jsx
{result && (
  <div className="mt-4">
    <p className="mb-2 font-semibold">AI 分析結果:</p>
    <p>{result}</p>
    {data.chart && (
      <img src={data.chart} alt="股價走勢圖" className="mt-4 border rounded" />
    )}
  </div>
)}

執行結果

https://ithelp.ithome.com.tw/upload/images/20250906/20169444wki4ywusHc.png

點開 Demo 即可看到輸入框

https://ithelp.ithome.com.tw/upload/images/20250906/201694441RyAE3pXuu.png

輸入 AAPL 並按下開始分析,就會顯示分析中,請稍後

但在此時遇到了問題,就是疑似連接一直有問題,一直卡在分析中,沒有跑出結果來

為了先行解決這個問題,我先改用 ngrok 來處理這個部分

ngrok 驗證金鑰

要先完成 ngrok (https://dashboard.ngrok.com)的帳號註冊

python
from pyngrok import ngrok
ngrok.set_auth_token("你的專屬金鑰") # 請將這段換成你的實際金鑰

改寫程式碼,使用 pyngrok 啟動服務

python

# 第一步:安裝所需套件
!pip install streamlit yfinance google-generativeai matplotlib pyngrok

# --- 新增:上傳字體與配置 ---
# 你需要手動將 NotoSerifCJKtc-Regular.otf 上傳到 Colab 的文件系統中
# 最簡單的方法是點擊左側的文件夾圖標,然後將字體文件拖曳進去
# 或者執行以下程式碼,它會要求你上傳文件
from google.colab import files
import matplotlib.font_manager as fm
import os

# 上傳字體檔案
# 注意:這會開啟一個上傳框,你需要選擇 NotoSerifCJKtc-Regular.otf
print("請上傳 NotoSerifCJKtc-Regular.otf 字體文件:")
uploaded = files.upload()

font_path = './NotoSerifCJKtc-Regular.otf' # 假設你上傳到根目錄

# 清除 Matplotlib 字體緩存並添加字體
if os.path.exists(font_path):
    fm.fontManager.addfont(font_path)
    # 設置全局字體為 Noto Serif CJK TC
    plt.rcParams['font.family'] = ['Noto Serif CJK TC']
    # 確保負號顯示正常
    plt.rcParams['axes.unicode_minus'] = False
    # 清除 Matplotlib 緩存,確保新字體被識別
    !rm -rf ~/.cache/matplotlib
    print(f"字體 {font_path} 已成功加載並設置。")
else:
    print(f"警告:字體文件 {font_path} 未找到。圖表可能無法正常顯示中文。")
# --- 新增結束 ---


# 第二步:寫入 Streamlit 應用程式碼到檔案
# Streamlit 應用需要一個 .py 檔案來運行
app_code = """
import streamlit as st
import yfinance as yf
import matplotlib.pyplot as plt
import google.generativeai as genai
import os
import matplotlib.font_manager as fm # 新增:導入 font_manager

# --- 新增:字體配置到 Streamlit 應用檔案中 ---
# 確保字體路徑正確。在 Colab 環境中,直接用 './' 應該可以找到
font_path_in_app = './NotoSerifCJKtc-Regular.otf' 

if os.path.exists(font_path_in_app):
    fm.fontManager.addfont(font_path_in_app)
    plt.rcParams['font.family'] = ['Noto Serif CJK TC']
    plt.rcParams['axes.unicode_minus'] = False
    # 注意:在 Streamlit 應用內部不需要再次清除緩存,因為環境是新的
else:
    st.warning(f"警告:Streamlit 應用中字體文件 {font_path_in_app} 未找到。圖表可能無法正常顯示中文。")
# --- 新增結束 ---


# 設定 Google API 金鑰
genai.configure(api_key="YOUR_GEMINI_API_KEY")

st.title("股票趨勢 AI 分析器")
st.write("請輸入股票代碼,讓 AI 幫你分析近一個月的股價走勢。")

ticker = st.text_input("輸入股票代碼 (e.g., AAPL, 2330.TW)", "2330.TW")

if st.button("開始分析"):
    if not ticker:
        st.error("請輸入有效的股票代碼!")
    else:
        with st.spinner("正在分析中...請稍候"):
            try:
                stock = yf.Ticker(ticker)
                hist = stock.history(period="1mo")
                
                if hist.empty:
                    st.error(f"找不到股票代碼:{ticker}。請確認代碼是否正確。")
                else:
                    plt.figure(figsize=(10, 6))
                    # 修改:確保中文標題、圖例、軸標籤都使用設定好的字體
                    plt.plot(hist.index, hist["Close"], label="收盤價") # 圖例中文
                    plt.title(f"{ticker} 近一個月股價趨勢") # 標題中文
                    plt.xlabel("日期") # X軸中文
                    plt.ylabel("股價") # Y軸中文
                    plt.legend()
                    st.pyplot(plt)
                    plt.close()

                    model = genai.GenerativeModel("gemini-1.5-flash")
                    prompt = f"以下是 {ticker} 最近一個月的股價走勢資料:\\n{hist.tail(5)}\\n\\n請用台灣股市投資新手也能懂的白話文解釋這段時間的股價變化,並簡要說明其可能的趨勢,不需要提供任何投資建議。請用繁體中文回答。"
                    response = model.generate_content(prompt)
                    ai_response_text = response.text

                    st.markdown("### AI 分析結果")
                    st.success(ai_response_text)

            except Exception as e:
                st.error(f"發生錯誤:{e}")
"""
with open("app.py", "w") as f:
    f.write(app_code)

# 第三步:設定 ngrok 驗證金鑰
# 請將這段換成你的實際金鑰
from pyngrok import ngrok
ngrok.set_auth_token("你的專屬金鑰")

# 第四步:使用 pyngrok 啟動 Streamlit 服務
import subprocess
import threading
import time

def run_streamlit():
    """在背景執行 Streamlit 服務"""
    subprocess.run(["streamlit", "run", "app.py"])

# 使用線程來啟動 Streamlit,這樣主程式碼才能繼續執行
streamlit_thread = threading.Thread(target=run_streamlit, daemon=True)
streamlit_thread.start()

# 等待 Streamlit 服務啟動
print("等待 Streamlit 服務啟動...")
time.sleep(5) # 稍微增加等待時間,確保 Streamlit 完全啟動

# 建立 ngrok 隧道
try:
    public_url = ngrok.connect(8501).public_url
    print(f"你的網頁應用程式已啟動,公開網址是:{public_url}")
    print("請點擊此網址在瀏覽器中開啟。")
except Exception as e:
    print(f"Ngrok 連線失敗:{e}")
    print("請檢查你的 ngrok 驗證金鑰是否設定正確,或 Streamlit 服務是否成功啟動。")
    

執行結果

https://ithelp.ithome.com.tw/upload/images/20250906/20169444qKIcpJRzU0.png

插入完文字檔案後,就可以開啟網站

https://ithelp.ithome.com.tw/upload/images/20250906/20169444vJiBzCEbah.png

就可以輸入你想查詢的股票代碼,並加以分析

https://ithelp.ithome.com.tw/upload/images/20250906/20169444AqXTSxXyWP.png!

https://ithelp.ithome.com.tw/upload/images/20250906/20169444Lams83JQDd.png

結語

雖然未能在原本的網站上成功實作出成果,但經過不斷嘗試與研究後,我們臨時改用 ngrok 建立另一個網站,並順利完成預期的功能。使用者只需輸入股票代號(例如 AAPL),系統即可自動抓取近一個月的股價資料並繪製圖表,接著由 AI 生成自然語言解釋,最終在頁面上同時呈現 股價走勢圖AI 分析說明。未來只要將這些模組進一步串接整合,相信能順利完成完整的網站服務。
👉 明天(Day 24),我們會讓 AI 報告「開口說話」,把股市分析變成語音播報!


上一篇
Day 22 :把文章搬進網站:自動載入 Markdown 內容
系列文
AI 給我錢錢錢 ! AI 股神養成計劃23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言