01金融網站的四個層次
一個完整的金融網站,從使用者看到的畫面到後台管理,可以拆成四層。初學者不需要第一天就精通全部,先從前台介面與簡單運算開始就好。
02五種技術的分工
複利計算器背後其實動用了五種技術,各自只負責自己最擅長的事——這正是網頁開發「分工」的核心概念。
| 技術 | 主要職責 | 金融應用舉例 |
|---|---|---|
| HTML | 網頁結構與內容 | 放置本金、利率、年數輸入欄位 |
| CSS | 排版與視覺設計 | 讓輸入畫面清楚美觀、支援手機版 |
| JavaScript | 互動邏輯與即時計算 | 讀取數值並執行複利公式 |
| JSON | 結構化資料儲存與傳遞 | 保存文章、題庫、金融商品資料 |
| Python | 大量分析與後台管理 | 分析股票資料、建立 Streamlit 後台 |
Python 是一種容易閱讀、應用範圍廣泛的程式語言,適合執行較複雜的工作:清理股票價格資料、計算大量報酬率、估算債券殖利率、進行風險分析、執行投資組合模擬、建立 Streamlit 管理後台、自動產生 JSON 資料、進行檔案備份。本書將 JavaScript 與 Python 依工作需求分工:簡單即時的網頁計算由 JavaScript 完成;較大量的資料處理與管理工作則交由 Python 執行。
03第一個金融網站專案:建立專案資料夾
在電腦中建立資料夾 financial-platform,並在其中建立以下結構:
financial-platform/ ├─ index.html ← 首頁與複利工具 ├─ css/ │ └─ style.css ← 網頁外觀與排版 ├─ js/ │ └─ calculator.js ← 金融計算與互動 ├─ data/ ← 未來存放 JSON / CSV ├─ images/ ← 圖片及圖示 └─ README.md ← 專案說明
| 檔案或資料夾 | 主要用途 |
|---|---|
| index.html | 網站首頁與複利工具結構 |
| css/style.css | 網頁外觀與排版 |
| js/calculator.js | 金融計算與互動邏輯 |
| data/ | 未來存放 JSON 或 CSV 資料 |
| images/ | 存放圖片及圖示 |
| README.md | 記錄專案說明與操作方式 |
Windows 建立方式
- 在桌面或指定位置按滑鼠右鍵,選擇「新增」→「資料夾」,命名為 financial-platform。
- 進入資料夾後建立 css、js、data 及 images 四個子資料夾。
- 使用 Visual Studio Code 建立 index.html、css/style.css 及 js/calculator.js。
- 在主資料夾建立 README.md。
- 注意副檔名:檔案應為 index.html,不是 index.html.txt。
使用 Visual Studio Code 開啟專案
開啟 Visual Studio Code →「檔案」→「開啟資料夾」→ 找到 financial-platform →「選取資料夾」。左側檔案總管應顯示完整資料夾結構。
不建議使用 Word 編輯程式碼,因為 Word 可能加入特殊格式而導致程式錯誤。
04複利終值的金融觀念
單利與複利——單利只針對最初本金計算利息。假設本金 100,000 元,年利率 5%,投資 3 年:
單利終值 = 100,000 + 100,000 × 5% × 3 = 115,000 元
複利則將前一期利息加入本金,下一期再以較大的金額計息:
複利終值 = 100,000 × (1.05)³ = 115,762.50 元
兩者的差距在投資期間短時可能不明顯,但投資期間越長,複利效果越顯著。
複利終值公式
FV 為未來價值;PV 為目前本金;r 為每期報酬率;n 為總期數。
假設本金 200,000 元,年報酬率 6%,投資 15 年:FV = 200,000 × (1.06)¹⁵ ≈ 479,312 元。
百分比轉換——使用者通常輸入 5,代表 5%。但程式執行運算時,必須將 5% 轉成 0.05:
const annualRate = ratePercent / 100; // 5 → 0.05 // 正確:100,000 × (1 + 0.05)^10 // 錯誤:100,000 × (1 + 5)^10 ← 忘記除以 100
金融程式最常見的錯誤:忘記把百分比除以 100。使用者輸入「5」代表 5%,但程式運算時必須是 0.05。若直接把 5 代入公式,等於用 500% 計算,結果會誇張地離譜。永遠記得:const annualRate = ratePercent / 100;
模型的限制——複利計算器只是情境模擬,不代表實際投資一定會依照固定報酬率成長。實際投資可能受到下列因素影響:
- 每年報酬率不同,可能發生虧損
- 稅負與手續費會降低實際報酬
- 通貨膨脹會降低實質購買力
- 投資人可能中途提領資金
網站應將計算結果稱為「試算結果」或「情境估計」,而不是「保證報酬」。
05系統設計流程(IPO 架構)
幾乎所有金融工具都遵循同一種資料流動方式:輸入 → 驗證 → 運算 → 輸出。記住這個順序,之後學債券定價、選擇權、退休試算都是同一套邏輯。本工具需要三項輸入(本金、年報酬率、投資年數),系統輸出投資期末金額、投資總獲利及情境說明。
06HTML 網頁結構實作
HTML 負責建立標題、說明文字、輸入欄位、計算按鈕及結果區。
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>複利終值計算器</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<main class="page-container">
<section class="calculator-card">
<h1>複利終值計算器</h1>
<p class="intro-text">
輸入本金、預期年報酬率及投資年數,試算投資期末金額。
</p>
<div class="form-group">
<label for="principal">投資本金(元)</label>
<input type="number" id="principal" min="0" step="1000"
placeholder="例如:100000">
</div>
<div class="form-group">
<label for="rate">預期年報酬率(%)</label>
<input type="number" id="rate" step="0.1" placeholder="例如:5">
</div>
<div class="form-group">
<label for="years">投資年數</label>
<input type="number" id="years" min="1" step="1" placeholder="例如:10">
</div>
<button type="button" id="calculateButton">開始計算</button>
<div id="message" class="message" aria-live="polite"></div>
<section id="resultBox" class="result-box hidden">
<h2>試算結果</h2>
<p>投資期末金額:<strong id="futureValue"></strong></p>
<p>投資總獲利:<strong id="totalProfit"></strong></p>
<p id="resultDescription"></p>
</section>
<p class="notice">本工具僅供金融教育及情境試算使用,不代表投資報酬保證。</p>
</section>
</main>
<script src="js/calculator.js"></script>
</body>
</html>
HTML 重點說明
| 標籤 / 屬性 | 用途說明 |
|---|---|
| <!DOCTYPE html> | 宣告使用 HTML5 標準 |
| lang="zh-Hant" | 設定頁面主要語言為繁體中文 |
| meta charset="UTF-8" | 使瀏覽器正確顯示中文字元 |
| viewport meta | 讓網頁在手機依裝置寬度顯示 |
| <label for="..."> | 說明輸入欄位內容,連結對應 input |
| input type="number" | 限制欄位輸入數值 |
| id="..." | 程式辨識網頁元素的唯一名稱 |
| aria-live="polite" | 讓輔助工具注意動態更新的訊息 |
id 不可重複。本頁使用的 id 清單:principal、rate、years、calculateButton、message、resultBox、futureValue、totalProfit、resultDescription。同一頁面中,每個 id 原則上只能出現一次,JavaScript 根據 id 找到指定欄位。
07CSS 畫面設計實作
* { box-sizing: border-box; }
body {
margin: 0;
font-family: "Microsoft JhengHei", Arial, sans-serif;
background: #f4f6f8;
color: #1f2933;
}
.page-container {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 24px;
}
.calculator-card {
width: 100%;
max-width: 560px;
background: #ffffff;
padding: 32px;
border-radius: 16px;
box-shadow: 0 8px 24px rgba(0,0,0,0.08);
}
h1 { margin-top: 0; margin-bottom: 12px; font-size: 30px; }
.intro-text { margin-bottom: 24px; line-height: 1.7; color: #52606d; }
.form-group { margin-bottom: 18px; }
label { display: block; margin-bottom: 8px; font-weight: 700; }
input {
width: 100%; padding: 12px 14px;
border: 1px solid #bcccdc;
border-radius: 8px; font-size: 16px;
}
input:focus { outline: 2px solid #4c8bf5; border-color: #4c8bf5; }
button {
width: 100%; margin-top: 8px; padding: 14px;
border: none; border-radius: 8px;
background: #2457a6; color: #ffffff;
font-size: 17px; font-weight: 700; cursor: pointer;
}
button:hover { background: #183f7a; }
.message { min-height: 24px; margin-top: 16px; font-weight: 700; }
.error-message { color: #b42318; }
.result-box {
margin-top: 20px; padding: 20px;
border-radius: 10px; background: #edf5ff;
}
.result-box h2 { margin-top: 0; }
.result-box strong { font-size: 20px; }
.notice { margin-top: 20px; font-size: 14px; color: #66788a; }
.hidden { display: none; }
@media (max-width: 600px) {
.calculator-card { padding: 22px; }
h1 { font-size: 25px; }
}
CSS 重點說明
| CSS 屬性 | 用途說明 |
|---|---|
| box-sizing: border-box | 寬度計算包含內距與邊框,較易控制版面 |
| max-width: 560px | 限制卡片最大寬度,避免在大螢幕上過度延伸 |
| border-radius | 產生圓角效果 |
| box-shadow | 產生卡片陰影,增加立體感 |
| :focus 偽類 | 使用者點選輸入欄位時顯示藍色提示框 |
| :hover 偽類 | 滑鼠移到按鈕上時改變背景色 |
| .hidden { display:none } | 暫時隱藏結果區,計算完成後顯示 |
| @media (max-width:600px) | 響應式設計,手機版使用較小的內距與字體 |
08JavaScript 金融計算實作
"use strict";
// -- 取得網頁元素 --
const principalInput = document.getElementById("principal");
const rateInput = document.getElementById("rate");
const yearsInput = document.getElementById("years");
const calculateButton = document.getElementById("calculateButton");
const messageBox = document.getElementById("message");
const resultBox = document.getElementById("resultBox");
const futureValueText = document.getElementById("futureValue");
const totalProfitText = document.getElementById("totalProfit");
const resultDescription = document.getElementById("resultDescription");
// -- 金融計算函數 --
function calculateFutureValue(principal, annualRate, years) {
return principal * Math.pow(1 + annualRate, years);
}
// -- 格式化新臺幣 --
function formatCurrency(amount) {
return new Intl.NumberFormat("zh-TW", {
style: "currency",
currency: "TWD",
maximumFractionDigits: 0
}).format(amount);
}
// -- 顯示錯誤訊息 --
function showError(message) {
messageBox.textContent = message;
messageBox.className = "message error-message";
resultBox.classList.add("hidden");
}
function clearMessage() {
messageBox.textContent = "";
messageBox.className = "message";
}
// -- 輸入驗證 --
function validateInputs(principal, ratePercent, years) {
if (Number.isNaN(principal) || Number.isNaN(ratePercent) || Number.isNaN(years))
return "請完整輸入本金、報酬率及年數。";
if (principal <= 0) return "投資本金必須大於 0。";
if (years <= 0) return "投資年數必須大於 0。";
if (!Number.isInteger(years)) return "本工具的投資年數請輸入整數。";
if (ratePercent <= -100) return "年報酬率不得小於或等於 -100%。";
if (ratePercent > 100) return "年報酬率過高,請確認輸入是否正確。";
return "";
}
// -- 主計算流程 --
function handleCalculation() {
const principal = Number(principalInput.value);
const ratePercent = Number(rateInput.value);
const years = Number(yearsInput.value);
const errorMessage = validateInputs(principal, ratePercent, years);
if (errorMessage !== "") { showError(errorMessage); return; }
clearMessage();
const annualRate = ratePercent / 100;
const futureValue = calculateFutureValue(principal, annualRate, years);
const totalProfit = futureValue - principal;
futureValueText.textContent = formatCurrency(futureValue);
totalProfitText.textContent = formatCurrency(totalProfit);
resultDescription.textContent =
`以年報酬率 ${ratePercent}% 投資 ${years} 年,` +
`本金約成長為原來的 ${(futureValue / principal).toFixed(2)} 倍。`;
resultBox.classList.remove("hidden");
}
// -- 事件監聽 --
calculateButton.addEventListener("click", handleCalculation);
09JavaScript 程式逐步解析
| 步驟 | 程式寫法 | 說明 |
|---|---|---|
| 取得欄位 | document.getElementById("principal") | 透過 id 找到 HTML 輸入欄位 |
| 轉換數值 | Number(principalInput.value) | 將文字欄位值轉成可運算數字 |
| 輸入驗證 | validateInputs() | 檢查空白、負數、不合理值 |
| 百分比轉換 | ratePercent / 100 | 5 → 0.05,避免計算錯誤 |
| 計算終值 | Math.pow(1 + annualRate, years) | 執行複利公式 (1+r)^n |
| 格式化金額 | Intl.NumberFormat("zh-TW",{...}).format() | 顯示新臺幣千分位格式 |
| 顯示結果 | resultBox.classList.remove("hidden") | 移除 hidden class 呈現結果 |
| 事件監聽 | addEventListener("click", handleCalculation) | 點擊按鈕時執行計算函數 |
網頁互動的基本模式:使用者動作 → 事件發生 → 執行函數 → 更新畫面。
10操作實務與測試
開啟網頁——儲存三個檔案(index.html、style.css、calculator.js)後,回到 financial-platform 資料夾,使用瀏覽器開啟 index.html。
基本測試——輸入本金 100,000 元、年報酬率 5%、投資年數 10 年,預期結果:
| 項目 | 預期結果 |
|---|---|
| 投資期末金額 | 約 162,889 元 |
| 投資總獲利 | 約 62,889 元 |
| 成長倍數 | 約 1.63 倍 |
邊界測試——金融工具不能只測試正常資料,也要測試錯誤資料:
| 測試情境 | 輸入內容 | 預期反應 |
|---|---|---|
| 空白欄位 | 未輸入本金 | 顯示請完整輸入 |
| 零本金 | 本金輸入 0 | 顯示本金必須大於 0 |
| 負年數 | 年數輸入 -5 | 顯示年數必須大於 0 |
| 小數年數 | 年數輸入 3.5 | 顯示年數應為整數 |
| 報酬率 -100% | 利率輸入 -100 | 顯示報酬率不合理 |
| 過高報酬率 | 利率輸入 500 | 提醒確認資料 |
| 零報酬率 | 利率輸入 0 | 終值應等於本金 |
結果合理性檢查
- 報酬率為 0% 時,終值應等於本金
- 正報酬率下,投資年數越長,終值通常越高
- 其他條件不變時,報酬率越高,終值越高
- 本金增加一倍,終值也應增加一倍
- 報酬率為負值時,終值可能低於本金
11實務案例
(一)長期複利效果
王先生目前投入 200,000 元,預期年報酬率 6%,分別投資 10 年、20 年與 30 年:
| 投資期間 | 預估終值 | 每多 10 年增加金額 |
|---|---|---|
| 10 年 | 358,170 元 | — |
| 20 年 | 641,427 元 | 約 283,257 元 |
| 30 年 | 1,148,698 元 | 約 507,271 元 |
後面 10 年的增加金額更大,原因是較大的本金持續參與複利。這就是「時間是複利最好的朋友」的核心原理。
(二)高報酬率假設的風險
某網站讓使用者以每年 30% 的報酬率試算 30 年,投入 100,000 元,計算結果會超過 2.62 億元。數學公式可能沒有錯,但金融假設極為樂觀。長期維持 30% 年報酬率十分困難,工具應明確提醒:高報酬通常伴隨高風險、歷史報酬不代表未來報酬、固定年報酬率只是簡化假設、長期試算對利率假設非常敏感。
(三)百分比轉換錯誤案例
學生在程式中直接寫 const annualRate = ratePercent;,未除以 100。使用者輸入 5 時,程式將其當成 500%,產生極端結果。
// ✗ 錯誤:忘記除以 100 const annualRate = ratePercent; // 5 被當成 500% // ✓ 正確:轉換百分比 const annualRate = ratePercent / 100; // 5 → 0.05
12常見錯誤與除錯對照表
在挑戰任務中,你會實際遇到這些狀況——先熟悉症狀與原因,除錯時會更有方向感。
| 症狀 | 可能原因 | 修正方式 |
|---|---|---|
| 網頁只有文字沒有樣式 | CSS 路徑錯誤或未儲存 | 確認 <link href="css/style.css"> |
| 按鈕沒有反應 | JS 路徑錯誤或 id 名稱不一致 | 確認 <script src="js/calculator.js"> |
| 中文字顯示亂碼 | 缺少或錯誤的 charset 設定 | 加入 <meta charset="UTF-8"> |
| 輸入 5% 卻得到極大結果 | 忘記將百分比除以 100 | const annualRate = ratePercent / 100 |
| 顯示 NaN | 欄位空白或輸入非數字 | 加入 Number.isNaN() 輸入驗證 |
| 修改後畫面未更新 | 檔案未儲存或瀏覽器快取 | 儲存後按 Ctrl+F5 強制重新整理 |
13關鍵字
14實作練習
- 將預設測試資料改為本金 300,000 元、年報酬率 4%、投資 12 年,記錄計算結果
- 將按鈕文字由「開始計算」改為「立即試算」
- 將頁面標題改為「個人投資複利試算工具」
- 在結果區增加「原始本金」顯示
- 測試報酬率為 0% 時,終值是否等於本金
- 增加「清除資料」按鈕,點擊後清空所有欄位與結果
- 增加「累積報酬率」顯示:(終值 - 本金) / 本金 × 100%
- 增加本金不得高於 10 億元的檢查
- 增加投資年數不得超過 100 年的檢查
- 報酬率高於 20% 時,顯示高風險提醒文字
- 增加每年期末資產金額逐年表格
- 加入 Chart.js 繪製資產成長曲線圖
- 增加單利與複利比較,並繪製差距圖
- 增加每月複利功能(將年利率轉換為月利率)
- 加入「樂觀(8%)、中性(5%)、保守(3%)」三種報酬率情境比較
- 將工具整合進個人金融知識網站的導覽系統
上面「基礎操作」的前兩項與「功能修改」的第一項,正是本章挑戰任務(04 分頁)裡任務一、二、三的原型——先在這裡讀懂需求,再到挑戰任務分頁動手實作與除錯。
本章結語——本章建立了金融程式設計與網站實作的基本地圖。金融網站不只是將文字放到網路上,而是將金融知識轉化為可以輸入、計算、比較及解釋的互動工具。
HTML 負責網頁骨架,CSS 負責畫面設計,JavaScript 負責互動與金融計算,JSON 負責結構化資料,Python 則適合大量分析與後台管理。這些技術各有分工,並不需要在一開始全部精通。
讀者已完成第一個複利終值計算器,並理解輸入—處理—輸出的基本架構。這個架構未來可以延伸至債券定價、股票報酬、選擇權、退休規劃與風險管理。
下一章將進一步說明 HTML、CSS 與 JavaScript 的互動設計,並將目前的單一複利工具擴充為包含現值、終值、報酬率與投資年數反推的多功能貨幣時間價值計算器。
—複利終值計算器(教材原始範例)
這就是本章要完成的成品,直接輸入本金、預期年報酬率、投資年數試試看。它與課本第六~八節的 index.html/style.css/calculator.js 完全對應——等一下的挑戰任務就是在這份程式碼上動手修改。
—觀念測驗
共 15 題選擇題,涵蓋本章所有核心觀念。每題作答後會立即顯示解析。
挑戰任務:修改程式碼,讓工具自己告訴你哪裡錯
下面兩個練習都是「真的會執行」的程式碼。修改完成後按下「執行測試」,系統會把你的程式碼放進真實瀏覽器環境中運行,並用白話文告訴你哪一行邏輯不對、哪個語法漏了什麼——就像有一位助教在旁邊即時看你的程式碼。
任務一:功能修改
對應教材「十四、實作練習(一)基礎操作」
- 把按鈕文字從「開始計算」改成「立即試算」
- 在結果區補上「原始本金」這一行的顯示邏輯(HTML 已經幫你留好
#originalPrincipal欄位,只需要在 JavaScript 中賦值)
提示(卡住了再打開)
calculateButton.textContent = "..."; 動態修改。補充欄位:在
handleCalculation() 函式中,計算完 futureValue 之後,加入一行把對應內容寫進指定的 id。
💡 查看範例解答(建議自己先試過再打開)
看完解答後,可以按上面的「還原初始程式碼」重新自己動手做一次,或按「換一題再練習」挑戰另一個類似的任務。
任務二:抓出隱藏的語法錯誤
對應教材「十二、常見錯誤與除錯」— id 對不起來
- 這份程式碼交接自另一位同學,執行後按鈕完全沒反應
- 找出問題並修正,讓計算器恢復正常運作
提示(卡住了再打開)
document.getElementById("...") 抓到的是 null。逐一比對 HTML 裡每個 id="..." 跟 JS 裡 getElementById("...") 的拼字是否一模一樣。
💡 查看範例解答(建議自己先試過再打開)
看完解答後,可以按「還原初始程式碼」重新自己抓一次,或按「換一題再練習」練習抓另一個類似的 id 錯誤。
任務三:抓出百分比換算錯誤
對應教材「十一、實務案例(三)百分比轉換錯誤案例」
- 這份程式碼可以正常執行、不會報錯,但算出來的數字完全不合理
- 本金 100,000、報酬率 5%、投資 10 年,正確答案應約為 162,889 元
- 找出問題所在並修正
提示(卡住了再打開)
💡 查看範例解答(建議自己先試過再打開)
看完解答後,可以按「還原初始程式碼」重新自己抓一次,或按「換一題再練習」練習抓另一種百分比換算錯誤。