工作坊系列
- 工作坊 1:使用 Kiro 建置:他加祿語 學習 App 的提示優先產品設計工作坊
- 工作坊 2:使用 Kiro 建置:他加祿語 學習 App 的教育優先開發技巧工作坊
- 工作坊 3:使用 Kiro 建置:他加祿語 學習 App 的深入開發流程工作坊
- 工作坊 4:使用 Kiro 建置:將 他加祿語 學習 App 在地化為中文變體工作坊
- 工作坊 5:使用 Kiro 建置:他加祿語 卡片的文法與發音補強管線工作坊
- 工作坊 6:使用 Kiro 建置:他加祿語 學習 App 中可審查的獨特額外例句工作坊
- 工作坊 7:與 Kiro 一起開發:重建 CME Direct 風格的量化損益排行榜 UI
- 工作坊 8:與 Kiro 一起開發:重建損益排行榜背後的量化分析引擎
- 工作坊 9:與 Kiro 一起建構:適用於量化排行榜的 AWS AI 驅動交易台助理


摘要: 本獨立工作坊將教導開發者使用 Kiro、TypeScript、純財務函數、SVG 圖表生成器、測試以及受控的即時模擬,重新構建上傳的排行榜中之量化 JavaScript 邏輯。開發者將實作報酬率、回撤、波動度、Calmar 比率、VaR、最佳單日、最差單日、勝率、排序、搜尋過濾、走勢線路徑、權益曲線、直方圖,以及具備完整原型覆蓋率與審查任務的更新迴圈安全性。
工作坊目的
這個為期 2 小時的工作坊專注於 Demo 背後的分析與互動引擎。本工作坊不側重於 UI 樣式,而是將原始的 JavaScript 行為萃取到可測試的 TypeScript 模組中。開發者將學習如何使用 Kiro 來設計財務函數、測試邊緣案例、解釋指標意義,並在不引入可靠性存疑的計算情況下保留即時更新行為。
Demo 功能覆蓋地圖
本工作坊涵蓋以下 Demo 實作細節:
- 包含 8 位交易員的參與者陣列
P:策略、淨值(NAV)%、每日 %、夏普比率、獲利因子、勝率、偏態、最大回撤(Max DD)以及淨值序列。 - 市場陣列
M:包含 ES、CL、GC、BTC、狀態標籤、變動值與迷你序列。 - 格式化輔助函式:帶正負號的百分比顯示與正/負類別(class)選取。
ret(series):每日報酬率序列。dd(series):回撤序列。avg,sd,perc:平均值、標準差、百分位數。path:SVG 折線路徑生成。spark,eqChart,ddChart,hist:迷你圖與完整圖表生成概念。panel:衍生進階指標,包含區間報酬率、已實現波動度、Calmar 比率、VaR 95、最佳單日、最差單日、勝率、最大回撤。data:搜尋與排序行為。render:資料列建構與展開面板狀態保留。stat:參與者人數、最佳夏普比率、平均勝率、最佳淨值。- 5 秒模擬淨值與每日報酬率更新迴圈。
目標開發者
- 將儀表板數學公式轉換為經測試模組的量化開發者。
- 正在學習財務指標實作的 TypeScript 開發者。
- 需要確定性圖表路徑生成的前端工程師。
- 正在學習如何使用 Kiro 審查數學與邊緣案例的工程師。
兩小時議程
| 時間 | 模組 | 開發者產出 |
|---|---|---|
| 0:00-0:10 | 萃取需求 | 來自 HTML Demo 的分析資產清單 |
| 0:10-0:25 | Kiro 引導設定 | 指標正負號、單位、測試與模擬規則 |
| 0:25-0:45 | 指標引擎 | 報酬率、回撤、波動度、百分位數、Calmar 比率 |
| 0:45-1:05 | 聚合引擎 | 排行榜統計資料與排序/過濾邏輯 |
| 1:05-1:25 | SVG 圖表引擎 | 走勢線、權益曲線、回撤、直方圖路徑 |
| 1:25-1:45 | 即時模擬 | 受控更新迴圈與狀態安全性 |
| 1:45-1:55 | 測試 | 單元測試與基於屬性的測試 |
| 1:55-2:00 | Kiro 審查 | 邊緣案例與生產環境強化待辦清單 |
架構
src/domain/
├─ types.ts # Trader, Market, RiskMetrics, SortKey
├─ formatting.ts # 帶正負號的百分比與類別
├─ calculations.ts # 報酬率、回撤、波動度、百分位數
├─ leaderboard.ts # 搜尋、排序、統計資料、資料列模型
├─ charts.ts # SVG 路徑與圖表模型函數
├─ simulator.ts # 確定性更新迴圈輔助函式
└─ __tests__/
├─ calculations.test.ts
├─ leaderboard.test.ts
├─ charts.test.ts
└─ simulator.test.ts
財務指標定義與交易決策用途
| 指標 | Demo 公式或解讀 | 交易決策用途 |
|---|---|---|
| 區間報酬率 (Window Return) | (最後一個 NAV / 第一個 NAV - 1) * 100。 |
衡量顯示視窗內的累積績效。 |
| 每日報酬率 (Daily Return) | (今日 NAV / 前日 NAV - 1) * 100。 |
顯示短期貢獻並驅動直方圖長條。 |
| 已實現波動度 (Realized Volatility) | 每日報酬率的標準差乘以 sqrt(252)。 |
用於比較不同策略之間的風險強度。 |
| 回撤 (Drawdown) | (當前 NAV / 歷史高點 - 1) * 100。 |
偵測策略從其頂峰下跌了多少。 |
| 最大回撤 (Max Drawdown) | 序列中的最小回撤值。 | 用於風險限制和停損審查閾值。 |
| Calmar 比率 (Calmar Ratio) | 區間報酬率除以最大回撤的絕對值。 | 比較報酬效率與資金痛苦程度。 |
| VaR 95 | 簡化 Demo 中每日報酬率的第五百分位數。 | 根據歷史報酬率近似估算下檔風險閾值。 |
| 最佳單日 (Best Day) | 最大每日報酬率。 | 識別潛在的向上跳空潛力。 |
| 最差單日 (Worst Day) | 最小每日報酬率。 | 識別單一期間的下檔衝擊。 |
| 勝率 (Win Rate) | 正每日報酬率天數除以總天數。 | 衡量一致性,但不衡量交易規模。 |
| 獲利因子 (Profit Factor) | 來自參與者資料的 Demo 資料列品質指標。 | 比較總收益與總損失,作為策略品質指標。 |
| 偏態 (Skew) | Demo 資料列的不對稱性指標。 | 負偏態警告可能存在下檔尾部行為。 |
步驟 1 — 為財務計算加入 Kiro 引導設定
建立 .kiro/steering/quant-math.md:
# 量化數學引導設定
- 將所有顯示的報酬率視為百分點,而非小數。
- 回撤必須為零或負數。
- 在此 Demo 中,VaR 95 為每日報酬率的第五百分位數。
- 已實現波動度使用 sqrt(252) 進行年化。
- 保持所有財務函數為純函數(pure functions)且無副作用。
- 請勿虛構即時或真實的市場資料。
建立 .kiro/steering/testing.md:
# 測試引導設定
每個財務公式都需要確定性的單元測試。
使用屬性風格的測試(property-style tests)來驗證不變量,例如回撤 <= 0。
測試空陣列、單點陣列、平坦淨值、全勝淨值、全敗淨值、無效淨值以及極端回撤。
Kiro 提示詞範例
建立一個規格說明,將上傳的排行榜 JavaScript 萃取至 TypeScript 分析模組中。包含財務公式定義、排序與搜尋行為、SVG 路徑生成、即時模擬行為、測試與邊緣案例。
商業邏輯: 引導設定在生成程式碼之前先定義財務意義。開發者需要一致的單位與正負號慣例,以避免產生誤導性的排名。
程式碼邏輯: Kiro 使用引導設定規則來生成純函數與測試,而不是將計算嵌入到 UI 渲染中。
預期結果: Kiro 為分析引擎產生需求/設計/任務流程。
系統設計原理解析:
- 量化數學引導設定是強制性的,因為原始原型包含諸如
ret、dd和perc等簡短的輔助函式名稱;生產環境開發者需要明確的定義。 - 測試引導設定與數學引導設定分開,因為測試覆蓋率是一項工程政策,而非財務公式。
- 「不虛構即時資料」的規則保持了工作坊的真實性。模擬數據只有在明確標記為 Demo 行為時才能使用。
步驟 2 — 定義型別
建立 src/domain/types.ts:
export type SortKey = 'navPct' | 'dailyPct' | 'sharpe' | 'profitFactor' | 'winRatePct' | 'maxDrawdownPct';
export type Trader = {
name: string;
strategy: string;
navPct: number;
dailyPct: number;
sharpe: number;
profitFactor: number;
winRatePct: number;
skew: number;
maxDrawdownPct: number;
series: number[];
};
export type AdvancedMetrics = {
windowReturnPct: number;
realizedVolPct: number;
calmarRatio: number;
valueAtRisk95Pct: number;
bestDayPct: number;
worstDayPct: number;
winRatePct: number;
maxDrawdownPct: number;
};
商業邏輯: 型別記錄了哪些值是原始輸入,哪些是衍生分析指標。
程式碼邏輯: SortKey 模擬了 Demo 的排序按鈕。Trader 對應參與者物件,而 AdvancedMetrics 對應詳細資料面板的區塊。
預期結果: TypeScript 在開發過程中能捕捉到無效的排序鍵或缺失的指標欄位。
系統設計原理解析:
- 明確的型別取代了隱式的 JavaScript 物件形狀。這至關重要,因為交易儀表板經常需要透過新增欄位來演進。
- 排序鍵使用領域名稱(domain names),而非 UI 標籤(如
SR或PF),這使程式碼更容易理解,同時在呈現層保留標籤。 - 進階指標與交易員輸入分離,因為 Calmar 和 VaR 等數值是從
series計算而來,而非手動維護。
步驟 3 — 實作計算
建立 src/domain/calculations.ts:
import type { AdvancedMetrics } from './types';
export function returnsPct(series: number[]): number[] {
if (series.length < 2) return [];
return series.slice(1).map((value, index) => ((value / series[index]) - 1) * 100);
}
export function drawdownPct(series: number[]): number[] {
if (!series.length) return [];
let peak = series[0];
return series.map(value => {
peak = Math.max(peak, value);
return peak === 0 ? 0 : ((value / peak) - 1) * 100;
});
}
export function average(values: number[]): number {
return values.length ? values.reduce((a, b) => a + b, 0) / values.length : 0;
}
export function standardDeviation(values: number[]): number {
const m = average(values);
return values.length ? Math.sqrt(average(values.map(v => (v - m) ** 2))) : 0;
}
export function percentile(values: number[], p: number): number {
if (!values.length) return 0;
const sorted = [...values].sort((a, b) => a - b);
const index = Math.min(sorted.length - 1, Math.max(0, Math.floor((p / 100) * sorted.length)));
return sorted[index];
}
export function advancedMetrics(series: number[]): AdvancedMetrics {
const r = returnsPct(series);
const d = drawdownPct(series);
const windowReturnPct = series.length >= 2 ? ((series.at(-1)! / series[0]) - 1) * 100 : 0;
const maxDrawdownPct = d.length ? Math.min(...d) : 0;
return {
windowReturnPct,
realizedVolPct: standardDeviation(r) * Math.sqrt(252),
calmarRatio: maxDrawdownPct === 0 ? 0 : windowReturnPct / Math.abs(maxDrawdownPct),
valueAtRisk95Pct: percentile(r, 5),
bestDayPct: r.length ? Math.max(...r) : 0,
worstDayPct: r.length ? Math.min(...r) : 0,
winRatePct: r.length ? r.filter(x => x > 0).length / r.length * 100 : 0,
maxDrawdownPct,
};
}
商業邏輯: These functions 使用專業名稱與安全的空陣列處理行為,重現了分析面板的邏輯。
程式碼邏輯: 程式碼將原型輔助函式轉換為可組合的函數。advancedMetrics 聚合了詳細資料面板中顯示的特定區塊數值。
預期結果: 傳入 Sofia 的資產淨值(NAV)序列會返回大約 18.4% 的區間報酬率、負的最大回撤,以及面板所需的進階分析數值。
系統設計原理解析:
- 純函數使分析引擎可用於 UI、API、測試或批次作業。這保護了商業邏輯免受 UI 重構的影響。
advancedMetrics集中了面板計算,因此 React 詳細資料面板不需要重複公式。- 百分位數方法刻意鏡像了原始的簡化 Demo。工作坊應解釋生產環境的 VaR 需要更強大的方法論與資料治理。
步驟 4 — 實作排序、過濾與摘要統計
建立 src/domain/leaderboard.ts:
import type { SortKey, Trader } from './types';
export function signed(value: number, digits = 1, suffix = ''): string {
return `${value >= 0 ? '+' : ''}${value.toFixed(digits)}${suffix}`;
}
export function searchAndSort(traders: Trader[], query: string, sortKey: SortKey): Trader[] {
const q = query.trim().toLowerCase();
return traders
.filter(t => !q || t.name.toLowerCase().includes(q) || t.strategy.toLowerCase().includes(q))
.sort((a, b) => sortKey === 'maxDrawdownPct' ? a.maxDrawdownPct - b.maxDrawdownPct : b[sortKey] - a[sortKey]);
}
export function boardStats(traders: Trader[]) {
return {
participants: traders.length,
bestSharpe: Math.max(...traders.map(t => t.sharpe)),
avgWinRatePct: traders.reduce((s, t) => s + t.winRatePct, 0) / traders.length,
bestNavPct: Math.max(...traders.map(t => t.navPct)),
};
}
商業邏輯: 使用者可以按績效或品質進行排名,並按人員或策略進行搜尋。摘要統計資料支援快速的交易桌層級讀取。
程式碼邏輯: 該函數重現了 Demo 的 data() 和 stat() 行為。最大回撤採升序排序,因為在原始邏輯中,較低的回撤代表較好的風險控制。
預期結果: 按資產淨值(NAV)排序會將 Sofia 排在第一位。搜尋 crypto 會返回 Lucia Fernandez 和 Carmen Lopez。
系統設計原理解析:
- 排序從渲染中分離出來,因此測試可以獨立於 UI 證明排名行為。
- 搜尋同時使用名稱與策略,因為原始 Demo 允許使用者透過策略風格來發現交易員,而不僅僅是看名字。
- 最大回撤排序經過特殊處理,因為風險指標通常與報酬指標的方向相反。
步驟 5 — 實作 SVG 路徑輔助函式
建立 src/domain/charts.ts:
export function svgPath(values: number[], width: number, height: number, padding = 18): string {
if (!values.length) return '';
const min = Math.min(...values);
const max = Math.max(...values);
const range = max - min || 1;
const innerWidth = width - padding * 2;
const innerHeight = height - padding * 2;
return values.map((value, index) => {
const x = padding + index * innerWidth / Math.max(1, values.length - 1);
const y = padding + innerHeight - ((value - min) / range) * innerHeight;
return `${index ? 'L' : 'M'}${x.toFixed(1)} ${y.toFixed(1)}`;
}).join(' ');
}
export function histogramBars(returns: number[], width: number, height: number, padding = 18) {
const maxAbs = Math.max(...returns.map(Math.abs), 1);
const barWidth = (width - padding * 2) / Math.max(1, returns.length);
const mid = height / 2;
return returns.map((value, index) => {
const barHeight = Math.abs(value) / maxAbs * (height / 2 - padding);
return {
x: padding + index * barWidth + 1,
y: value >= 0 ? mid - barHeight : mid,
width: Math.max(2, barWidth - 2),
height: barHeight,
positive: value >= 0,
};
});
}
商業邏輯: 圖表協助交易員比僅看數字更快理解趨勢、回撤壓力與每日報酬率分佈。
程式碼邏輯: svgPath 鏡像了原型的路徑生成器。histogramBars 將報酬率轉換為正/負長條的矩形幾何形狀。
預期結果: 相同的資產淨值(NAV)序列可以生成走勢線、權益曲線、回撤線與報酬率直方圖。
系統設計原理解析:
- 圖表幾何形狀在純函數中計算,因此無需瀏覽器渲染即可進行測試。
- 路徑函數將數值正規化到視窗(view box)。這使得同一個輔助函式可以同時支援小型走勢線與大型圖表。
- 直方圖長條帶有
positive旗標,因此 UI 可以直接應用綠色或紅色樣式,而無需重新計算正負號邏輯。
步驟 6 — 實作受控的即時模擬
建立 src/domain/simulator.ts:
import type { Trader } from './types';
export function nextDemoTick(traders: Trader[], random = Math.random): Trader[] {
return traders.map(trader => {
const bump = (random() - 0.48) * 0.18;
const nextDailyPct = Number((trader.dailyPct + bump).toFixed(2));
const nextNavPct = Number((trader.navPct + bump * 0.25).toFixed(2));
return {
...trader,
dailyPct: nextDailyPct,
navPct: nextNavPct,
series: [...trader.series.slice(1), 100 + nextNavPct],
};
});
}
商業邏輯: Demo 每 5 秒更新一次以模擬即時看板。開發者必須明確將其標記為模擬數據,而非真實市場數據。
程式碼邏輯: 該函數鏡像了原始的更新迴圈,但使隨機性可注入(injectable)以進行確定性測試。
預期結果: 每個 Tick 都會微調每日報酬率、微調資產淨值(NAV)、移動序列視窗,並附加一個新的合成淨值點。
系統設計原理解析:
- 隨機性是注入的,因為測試需要確定性的行為。這保留了即時 Demo 的感覺,又不會使測試變得不穩定(flaky)。
- 序列透過捨棄最舊的點來使用滾動視窗(rolling window)。這保留了圖表長度並避免了記憶體無限制增長。
- 該函數返回新物件,而不是修改(mutating)原始陣列,符合 React 狀態管理預期。
步驟 7 — 新增測試
建立 src/domain/__tests__/calculations.test.ts:
import { describe, expect, it } from 'vitest';
import { advancedMetrics, drawdownPct, returnsPct } from '../calculations';
describe('量化計算', () => {
it('從 NAV 計算報酬率', () => {
expect(returnsPct([100, 110, 99]).map(x => Number(x.toFixed(2)))).toEqual([10, -10]);
});
it('回撤絕不為正數', () => {
expect(drawdownPct([100, 120, 90, 130]).every(x => x <= 0)).toBe(true);
});
it('建立進階面板指標', () => {
const metrics = advancedMetrics([100, 105, 99.75]);
expect(Number(metrics.windowReturnPct.toFixed(2))).toBe(-0.25);
expect(Number(metrics.maxDrawdownPct.toFixed(2))).toBe(-5.00);
});
});
Kiro 提示詞範例
審查分析模組與測試。為每個 Demo 參與者序列、淨值(NAV)與最大回撤(Max DD)的排序行為、按策略關鍵字搜尋、SVG 路徑邊界、直方圖正負長條,以及注入隨機值的確定性模擬新增測試案例。
商業邏輯: 測試保護了驅動交易解讀的計算公式。
程式碼邏輯: 單元測試驗證了已知的數值範例,並可擴展以覆蓋所有參與者。
預期結果: npx vitest run 通過且 Kiro 提出了額外的覆蓋建議。
系統設計原理解析:
- 已知值測試可快速捕捉公式退化(regressions)。財務審查人員很容易理解這些測試。
- 要求 Kiro 新增特定參與者的回歸測試,使資料或公式的變更不會意外更改顯示的分析結果。
- 圖表測試專注於幾何邊界,因為 SVG 視覺測試成本高昂且對此工作坊而言並非必要。
最終實驗室挑戰
詢問 Kiro:
生成一份完整的分析差異報告,對比 TypeScript 模組與原始 Demo 的 JavaScript 函數:sg、cl、id、ret、dd、avg、sd、perc、path、spark、eqChart、ddChart、hist、panel、data、render、tog、stat、markets、clock 以及 5 秒更新迴圈。識別哪些已實現、哪些刻意移至 React,以及哪些仍需要測試。
完成檢查清單
- 所有參與者與市場資料皆已定義型別。
- 報酬率、回撤、波動度、百分位數、Calmar 比率、VaR、最佳/最差單日以及勝率皆已實作。
- 搜尋與排序重現了 Demo 的行為。
- SVG 路徑與直方圖幾何形狀為純函數。
- 即時模擬已明確標記並在測試中具備確定性。
- Kiro 已審查數學邊緣案例。
- 測試涵蓋了計算、排序、圖表與模擬。
附錄 — 用於分析測試的完整參與者與市場覆蓋範圍
使用此檢查清單以確保分析引擎覆蓋完整的 HTML Demo,而不僅僅是範例資料列:
- Sofia Garcia — 跨資產凸性總經阿爾法 (Cross-Asset Convex Macro Alpha):NAV +18.4%, 每日 +0.42%, 夏普比率 0.73, 獲利因子 1.8, 勝率 58%, 偏態 +0.44, 最大回撤 18。
- Lucia Fernandez — 加密貨幣動能輪動 (Crypto Momentum Rotation):NAV +16.9%, 每日 +0.88%, 夏普比率 0.91, 獲利因子 1.7, 勝率 61%, 偏態 +0.31, 最大回撤 22。
- Carmen Lopez — 加密貨幣套利與波動度 (Crypto Carry & Volatility):NAV +14.2%, 每日 -0.31%, 夏普比率 0.68, 獲利因子 1.6, 勝率 56%, 偏態 +0.22, 最大回撤 25。
- Elena Martin — 全球總經趨勢跟隨 (Global Macro Trend Rider):NAV +11.8%, 每日 +0.17%, 夏普比率 0.62, 獲利因子 1.5, 勝率 54%, 偏態 +0.18, 最大回撤 17。
- Marta Sanchez — 利率與外匯相對價值 (Rates & FX Relative Value):NAV +9.6%, 每日 +0.09%, 夏普比率 0.57, 獲利因子 1.4, 勝率 53%, 偏態 +0.09, 最大回撤 15。
- Paula Romero — 權益因子集合 (Equity Factor Ensemble):NAV +7.1%, 每日 -0.12%, 夏普比率 0.49, 獲利因子 1.3, 勝率 52%, 偏態 -0.04, 最大回撤 14。
- Ana Torres — 大宗商品突破系統 (Commodity Breakout System):NAV +5.4%, 每日 +0.28%, 夏普比率 0.42, 獲利因子 1.2, 勝率 51%, 偏態 +0.12, 最大回撤 19。
- Laura Navarro — 多資產均值回歸 (Multi-Asset Mean Reversion):NAV +3.8%, 每日 -0.06%, 夏普比率 0.35, 獲利因子 1.1, 勝率 49%, 偏態 -0.11, 最大回撤 16。
市場圖卡回歸案例:
- ES: +0.38%,
買方堆疊 (BID STACK), 序列[20,21,20,22,23,23,24,25,24,26]。 - CL: -0.22%,
賣方成交 (OFFER HIT), 序列[30,29,31,28,27,26,25,24,23,22]。 - GC: +0.62%,
買方堆疊 (BID STACK), 序列[18,18.5,19,18.7,20,21,20.5,22,23,23.5]。 - BTC: +2.18%,
買方堆疊 (BID STACK), 序列[20,22,21,24,26,25,29,28,32,34]。
Kiro 完整覆蓋提示詞:
生成一個回歸測試套件,載入所有 8 個參與者資料列與所有 4 個市場圖卡。驗證摘要統計資料、每個排序鍵的排序順序、按每個策略關鍵字搜尋、每個序列的 SVG 路徑,以及每個參與者的進階面板指標。
原始 Demo 參考
本工作坊基於上傳的 aws_quant_pnl_leaderboard_v3.html Demo。該 Demo 包含一個 CME Direct 風格的深色工作空間、參與者排行榜、市場卡片、可排序/可搜尋的損益板、進階分析面板、圖表函數、響應式 CSS、即時 HKT 時鐘以及模擬的週期性資產淨值更新。所有資料均視為 Demo 預留位置資料,不構成投資建議。
進階開發者附加動手實作實驗室 — HTML 圖形分析
這些實驗室透過分析上傳的 HTML 檔案的 SVG 圖表圖形來擴展量化分析工作坊。它們專注於原型如何將資產淨值(NAV)序列、回撤陣列與每日報酬率轉化為視覺標記。它們不會重複基礎財務公式或 UI 元件遷移。
進階圖形分析目標
到本節結束時,進階開發者將能夠:
- 解釋 HTML 如何將數值序列轉換為 SVG 路徑、區域、線條與長條。
- 透過確定性測試驗證圖表幾何形狀。
- 將視覺編碼邏輯與財務計算分離。
- 建構協助審查人員理解圖形意義的圖表審查中繼資料(chart-audit metadata)。
- 偵測由尺度、填補(padding)或邊緣案例資料引起的誤導性圖表輸出。
來自 HTML 檔案的圖表圖形清冊
上傳的 HTML 使用了幾個 JavaScript 函數來產生 SVG 圖形:
path(a,w,h,p)將序列正規化為 SVGM與L指令。spark(a)渲染緊湊的資料列與市場卡片走勢線。grid(w,h,p)建立水平網格線與底部軸線。eqChart(a)渲染帶有填滿區域與 NAV 起始/結束標籤的權益曲線。ddChart(a)將回撤渲染為零軸下方的紅色水位線形狀。hist(a)圍繞中線渲染每日報酬率長條,使用獨立的正、負類別(classes)。
進階實驗室 1 — 圖表幾何契約測試
目標: 建立測試以證明 SVG 幾何形狀保持在圖表邊界內,並安全地處理平坦、過短以及劇烈波動的序列。
建立 src/domain/__tests__/chartGeometry.test.ts:
import { describe, expect, it } from 'vitest';
import { svgPath, histogramBars } from '../charts';
function extractNumbers(path: string): number[] {
return path.match(/-?\d+(\.\d+)?/g)?.map(Number) ?? [];
}
describe('SVG 圖表幾何契約', () => {
it('將路徑座標保持在視窗填補邊界之內', () => {
const d = svgPath([100, 102, 101, 104], 120, 32, 2);
const numbers = extractNumbers(d);
const xs = numbers.filter((_, index) => index % 2 === 0);
const ys = numbers.filter((_, index) => index % 2 === 1);
expect(Math.min(...xs)).toBeGreaterThanOrEqual(2);
expect(Math.max(...xs)).toBeLessThanOrEqual(118);
expect(Math.min(...ys)).toBeGreaterThanOrEqual(2);
expect(Math.max(...ys)).toBeLessThanOrEqual(30);
});
it('渲染平坦序列時不會發生除以零的幾何錯誤', () => {
const d = svgPath([100, 100, 100], 120, 32, 2);
expect(d).toContain('M');
expect(d).toContain('L');
expect(d).not.toContain('NaN');
expect(d).not.toContain('Infinity');
});
it('圍繞中線建立正向和負向的直方圖長條', () => {
const bars = histogramBars([1, -2, 0.5], 680, 120, 18);
expect(bars.some((bar) => bar.positive)).toBe(true);
expect(bars.some((bar) => !bar.positive)).toBe(true);
});
});
Kiro 提示詞:
為上傳的 HTML 之路徑、走勢線、權益曲線、回撤與直方圖行為生成圖表幾何測試。驗證座標邊界、無 NaN 或 Infinity、平坦序列行為、短序列行為、正/負直方圖標記,以及各個 Demo 參與者序列的繪圖一致性。
預期結果: 開發者可以重構圖表程式碼,而不會意外產生損壞的 SVG。
進階實驗室 2 — SVG 視覺編碼文件
目標: 記錄財務概念與圖形標記之間的關係,使圖表行為可供審查。
建立 docs/svg-visual-encoding.md:
# SVG 視覺編碼筆記
## 走勢線 (Sparkline)
- 資料輸入:NAV 序列或市場迷你序列。
- 標記類型:單一綠線。
- 用途:緊湊的趨勢預覽。
- 風險:未顯示 Y 軸尺度,因此不應視為精確測量。
## 權益曲線 (Equity curve)
- 資料輸入:指數化(indexed)的 NAV 序列。
- 標記類型:綠線加上半透明填滿區域。
- 用途:可視化的累積績效路徑。
- 標籤:首個 NAV 值與最後一個 NAV 值。
## 回撤水位線 (Drawdown waterline)
- 資料輸入:從 NAV 衍生出的回撤百分比序列。
- 標記類型:紅線以及零軸下方的紅色填滿區域。
- 用途:顯示波峰到波谷的痛苦與復原壓力。
## 每日損益直方圖 (Daily P&L histogram)
- 資料輸入:每日報酬率序列。
- 標記類型:圍繞水平中線的垂直長條。
- 正向編碼:中線之上的綠色長條。
- 負向編碼:中線之下的紅色長條。
Kiro 提示詞:
為 HTML 圖表建立視覺編碼文件。使用資料輸入、SVG 標記類型、顏色編碼、尺度限制與審查注意事項來解釋走勢線、權益曲線、回撤水位線與每日損益直方圖。
預期結果: 圖表圖形對開發者、設計師與風險審查人員變得易於解釋。
進階實驗室 3 — 圖表審查中繼資料生成器
目標: 為每張圖表生成中繼資料,以便審查人員檢查尺度、極值、範圍、正負長條計數與標籤文字。
建立 src/domain/chartAudit.ts:
import { drawdownPct, returnsPct } from './calculations';
export type SeriesAudit = {
pointCount: number;
min: number;
max: number;
range: number;
first: number;
last: number;
};
export type ChartAudit = {
nav: SeriesAudit;
drawdown: SeriesAudit;
returns: SeriesAudit & {
positiveCount: number;
negativeCount: number;
zeroCount: number;
};
};
function auditSeries(values: number[]): SeriesAudit {
if (!values.length) {
return { pointCount: 0, min: 0, max: 0, range: 0, first: 0, last: 0 };
}
const min = Math.min(...values);
const max = Math.max(...values);
return {
pointCount: values.length,
min,
max,
range: max - min,
first: values[0],
last: values.at(-1)!,
};
}
export function auditChartSeries(navSeries: number[]): ChartAudit {
const drawdown = drawdownPct(navSeries);
const returns = returnsPct(navSeries);
const returnsAudit = auditSeries(returns);
return {
nav: auditSeries(navSeries),
drawdown: auditSeries(drawdown),
returns: {
...returnsAudit,
positiveCount: returns.filter((value) => value > 0).length,
negativeCount: returns.filter((value) => value < 0).length,
zeroCount: returns.filter((value) => value === 0).length,
},
};
}
Kiro 提示詞:
為每個淨值(NAV)序列新增圖表審查中繼資料。包含點數、最小值、最大值、範圍、首/尾值、回撤範圍、報酬率範圍,以及正/負/零報酬率計數。為所有 8 個參與者序列新增測試。
預期結果: 分析審查人員無需開啟瀏覽器即可檢查圖表輸入與尺度風險。
進階實驗室 4 — 誤導性圖形邊緣案例實驗室
目標: 教導開發者識別圖形在技術上正確但視覺上具誤導性的邊緣案例。
建立 docs/misleading-chart-edge-cases.md:
# 誤導性圖表邊緣案例
## 平坦序列 (Flat series)
平坦的 NAV 序列會產生一條直線,但如果忽略了標籤,視覺範圍的退化可能會使極微小的變動看起來比實際更大。
## 單點序列 (Single-point series)
單個點無法代表趨勢。圖表應渲染安全的預留位置或顯示資料不足的訊息。
## 極端離群值 (Extreme outlier)
一個巨大的跳空可能會壓縮所有其他變動,使正常的波動看起來像消失了一樣。
## 短報酬率視窗 (Short return window)
包含 14 個報酬率的直方圖具有教育意義,但不足以得出穩健的分佈結論。
## 缺少尺度標籤 (Missing scale labels)
走勢線對於快速形狀識別很有用,但不應被視為精確的風險證據。
Kiro 提示詞:
為 HTML 圖表函數建立誤導性圖表邊緣案例指南。涵蓋平坦序列、單點序列、極端離群值、短報酬率視窗、缺少 Y 軸尺度以及直方圖解讀限制。
進階實驗室 5 — 圖表渲染驗收標準
目標: 在生產環境重構驗收之前,定義圖表圖形的驗收標準。
建立 docs/chart-rendering-acceptance-criteria.md:
# 圖表渲染驗收標準
## 權益曲線 (Equity curve)
- 使用與 HTML 原型相同之正規化座標邏輯。
- 包含可見的線條與填滿區域。
- 標註第一個和最後一個 NAV 值。
- 不渲染 NaN 或 Infinity。
## 回撤水位線 (Drawdown waterline)
- 渲染前回撤值必須為零或負數。
- 零軸清晰可見。
- 紅色區域隨回撤加深而向下增長。
- 最大回撤標籤與計算出的最小回撤相符。
## 每日報酬率直方圖 (Daily return histogram)
- 正向長條顯示在中線之上。
- 負向長條顯示在中線之下。
- 數值為零的長條不會引發視覺錯誤。
- 在所有支援的圖表尺寸下,長條寬度保持清晰可見。
## 走勢線 (Sparkline)
- 緊湊的圖表不包含未支援的尺度宣告。
- 保留所有參與者與市場迷你序列的趨勢形狀。
Kiro 提示詞:
為 React/SVG 重構生成圖表渲染驗收標準。包含權益曲線、回撤水位線、每日報酬率直方圖與走勢線檢查。盡可能將每個視覺斷言與確定性測試相連結。
進階最終挑戰 — SVG 圖表保真度審查
詢問 Kiro:
對上傳的 HTML 檔案進行 SVG 圖表保真度審查。對比路徑正規化、填補(padding)、權益區域閉合、網格線、回撤零軸、直方圖中線、正負長條放置、標籤以及邊緣案例行為。產出優先順序修復待辦清單。
進階圖形分析完成檢查清單
- 圖表幾何測試驗證了邊界與無效數字保護。
- 視覺編碼文件解釋了每種 SVG 圖表類型。
- 圖表審查中繼資料總結了 NAV、回撤與報酬率序列。
- 誤導性圖表的邊緣案例已記錄成冊。
- 渲染驗收標準保護了圖表重構。
