diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-17 14:32:37 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-17 14:32:37 +0800 |
| commit | 5c0ba37eda80d39e6ceca59bb1d5f4942f858995 (patch) | |
| tree | 948723f9cedf7ccb0707fa6ee516bd30fe20fd10 /frontend/src/lib/format.ts | |
| download | strategy-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.ts | 118 |
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) + ]; +} |
