极点宏观|Financial Cloud Cloud · 构建文章
与 Kiro 一起开发:重建损益排行榜背后的量化分析引擎
摘要: 本独立工作坊将教导开发者使用 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) | 来自参与者数据的数据行质量指标。 | 比较总收益与总损失,作为策略质量指标。 |
| 偏度 (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,
};
}
商业逻辑: 这些函数使用专业名称与安全的空数组处理行为,重现了分析面板的逻辑。
代码逻辑: 代码将原型辅助函数转换为可组合的函数。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、回撤与收益率序列。
- [ ] 误导性图表的边缘案例已记录成册。
- [ ] 渲染验收标准保护了图表重构。