summaryrefslogtreecommitdiff
path: root/frontend/src/lib/format.ts
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-17 14:32:37 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-17 14:32:37 +0800
commit5c0ba37eda80d39e6ceca59bb1d5f4942f858995 (patch)
tree948723f9cedf7ccb0707fa6ee516bd30fe20fd10 /frontend/src/lib/format.ts
downloadstrategy-lab-5c0ba37eda80d39e6ceca59bb1d5f4942f858995.tar.gz
chore: establish Strategy Lab source baseline (development, not release)
Diffstat (limited to 'frontend/src/lib/format.ts')
-rw-r--r--frontend/src/lib/format.ts118
1 files changed, 118 insertions, 0 deletions
diff --git a/frontend/src/lib/format.ts b/frontend/src/lib/format.ts
new file mode 100644
index 0000000..13e9d1e
--- /dev/null
+++ b/frontend/src/lib/format.ts
@@ -0,0 +1,118 @@
+import type {
+ BacktestMetrics,
+ Dataset,
+ Instrument,
+ Run
+} from './types';
+
+export function uuidLike(id: string): boolean {
+ return /^[0-9a-f-]+$/i.test(id);
+}
+
+export function fmtDateTime(iso?: string | null): string {
+ if (!iso) return '—';
+ const d = new Date(iso);
+ if (isNaN(d.getTime())) return iso;
+ return d.toLocaleString('zh-CN', { hour12: false });
+}
+
+export function fmtDate(iso?: string | null): string {
+ if (!iso) return '—';
+ return iso.replace('T', ' ').replace(/Z$|\+08:00$/i, '');
+}
+
+export function fmtPct(v: number | null | undefined, digits = 2): string {
+ if (v === null || v === undefined || Number.isNaN(v)) return '—';
+ return `${(v * 100).toFixed(digits)}%`;
+}
+
+export function fmtNum(v: number | null | undefined, digits = 2): string {
+ if (v === null || v === undefined || Number.isNaN(v)) return '—';
+ if (Math.abs(v) >= 1e8) return `${(v / 1e8).toFixed(2)} 亿`;
+ if (Math.abs(v) >= 1e4) return `${(v / 1e4).toFixed(2)} 万`;
+ let r = v.toFixed(digits);
+ if (r === '0.00' && v !== 0) r = '0';
+ return r;
+}
+
+export function itemSourceLabel(itemSource?: string | null, fallback = ''): string {
+ const s = itemSource || fallback;
+ if (!s) return '—';
+ if (s === 'catalog') return '目录';
+ if (s === 'provider_suggest') return '供应商';
+ if (s === 'eastmoney') return '东财';
+ if (s === 'tencent') return '腾讯';
+ if (s === 'sina') return '新浪';
+ return s;
+}
+
+export const runStatusLabel: Record<Run['status'], string> = {
+ queued: '排队中',
+ running: '执行中',
+ succeeded: '已完成',
+ failed: '失败',
+ cancelled: '已取消'
+};
+
+export const runStatusBadge: Record<Run['status'], 'grey' | 'warn' | 'ok' | 'danger' | 'teal'> = {
+ queued: 'grey',
+ running: 'warn',
+ succeeded: 'ok',
+ failed: 'danger',
+ cancelled: 'grey'
+};
+
+export const datasetStatusLabel: Record<Dataset['status'], string> = {
+ pending: '等待中',
+ running: '获取数据',
+ ready: '就绪',
+ failed: '失败'
+};
+
+export const adjustmentLabel: Record<string, string> = {
+ none: '不复权',
+ qfq: '前复权',
+ hfq: '后复权'
+};
+
+export const assetTypeLabel: Record<string, string> = {
+ stock: '股票',
+ etf: 'ETF',
+ index: '指数'
+};
+
+export function instrumentLabel(i: Instrument): string {
+ const name = i.name ? ` ${i.name}` : '';
+ return `${i.market}.${i.symbol}${name}`;
+}
+
+export function formatMoney(v: number | null | undefined): string {
+ if (v === null || v === undefined || Number.isNaN(v)) return '—';
+ return `¥ ${fmtNum(v)}`;
+}
+
+export const versionSourceLabel: Record<string, string> = {
+ manual: '手动快照',
+ run: '回测快照',
+ ai: 'AI 采用',
+ restore: '恢复产生'
+};
+
+export function metricsLabels(): string[] {
+ // trade_count semantics (worker/backtest.py): COUNT of closed round trips —
+ // an open that is fully closed afterwards counts as 1; fills/orders and
+ // cancellations do NOT count. Consistent label for the result card AND the
+ // run comparison tables.
+ return ['总收益率', '年化收益率', '最大回撤', '夏普比率', '平仓回合数', '期末权益'];
+}
+
+export function metricsRow(m: BacktestMetrics): string[] {
+ return [
+ fmtPct(m.total_return),
+ fmtPct(m.annual_return),
+ fmtPct(m.max_drawdown),
+ m.sharpe === null || m.sharpe === undefined ? '—' : m.sharpe.toFixed(2),
+ m.trade_count === null || m.trade_count === undefined ? '—' : String(m.trade_count),
+ m.final_equity === null || m.final_equity === undefined ? '—' : fmtNum(m.final_equity)
+ ];
+}