feat(web): 因子工作台接线——eval API 客户端+3路由+投研组菜单/面包屑 [nas]
This commit is contained in:
@@ -33,3 +33,89 @@ export function reportUrl(taskId: string, factor: string): string {
|
||||
const auth = useAuthStore()
|
||||
return `/api/v1/task/${taskId}/report/${factor}?token=${encodeURIComponent(auth.token ?? '')}`
|
||||
}
|
||||
|
||||
// —— Factor Evaluation API ——
|
||||
|
||||
export interface EvalRun {
|
||||
run_id: string
|
||||
label: string
|
||||
universe: string
|
||||
symbols_count: number
|
||||
factors_total: number
|
||||
factors_done: number
|
||||
start: string
|
||||
end: string
|
||||
status: string
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface MonthlyIcPoint { month: string; ic: number }
|
||||
|
||||
export interface LeaderboardRow {
|
||||
rank: number
|
||||
factor: string
|
||||
category: string
|
||||
expression: string
|
||||
ic_mean: number | null
|
||||
icir: number | null
|
||||
t_stat: number | null
|
||||
win_rate: number | null
|
||||
ls_annual: number | null
|
||||
turnover: number | null
|
||||
conclusion: string
|
||||
monthly_ic: MonthlyIcPoint[]
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface LeaderboardTiles {
|
||||
factors_total: number
|
||||
effective: number
|
||||
watch: number
|
||||
top_ls_annual: number | null
|
||||
top_ls_factor: string | null
|
||||
}
|
||||
|
||||
export interface EvalDetail {
|
||||
factor: string
|
||||
category: string
|
||||
expression: string
|
||||
metrics: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface BatchEvalSubmit {
|
||||
categories: string[]
|
||||
factors: string[]
|
||||
symbols: string[]
|
||||
start: string
|
||||
end: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export async function getEvalRuns(): Promise<EvalRun[]> {
|
||||
const { data } = await apiClient.get<{ runs: EvalRun[] }>('/factor/eval/runs')
|
||||
return data.runs
|
||||
}
|
||||
|
||||
export async function getLeaderboard(params: {
|
||||
run_id?: string
|
||||
period?: string
|
||||
category?: string
|
||||
search?: string
|
||||
sort?: string
|
||||
order?: string
|
||||
}): Promise<{ tiles: LeaderboardTiles; rows: LeaderboardRow[] }> {
|
||||
const { data } = await apiClient.get('/factor/eval/leaderboard', { params })
|
||||
return data
|
||||
}
|
||||
|
||||
export async function getEvalDetail(factor: string, runId?: string): Promise<EvalDetail> {
|
||||
const { data } = await apiClient.get<EvalDetail>('/factor/eval/detail', {
|
||||
params: { factor, ...(runId ? { run_id: runId } : {}) },
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function submitBatchEval(req: BatchEvalSubmit): Promise<string> {
|
||||
const { data } = await apiClient.post<{ task_id: string }>('/factor/eval/submit', req)
|
||||
return data.task_id
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ const routes: RouteRecordRaw[] = [
|
||||
{ path: 'backtest/optimize', name: 'bt-optimize', component: () => import('@/views/backtest/Optimize.vue') },
|
||||
{ path: 'backtest/optimize-result/:id', name: 'bt-optimize-result', component: () => import('@/views/backtest/OptimizeResult.vue') },
|
||||
{ path: 'backtest/history', name: 'bt-history', component: () => import('@/views/backtest/History.vue') },
|
||||
{ path: 'factor/leaderboard', name: 'fc-leaderboard', component: () => import('@/views/factor/Leaderboard.vue') },
|
||||
{ path: 'factor/leaderboard/:factor', name: 'fc-leaderboard-detail', component: () => import('@/views/factor/LeaderboardDetail.vue') },
|
||||
{ path: 'factor/batch', name: 'fc-batch', component: () => import('@/views/factor/BatchEval.vue') },
|
||||
{ path: 'factor/new', name: 'fc-new', component: () => import('@/views/factor/New.vue') },
|
||||
{ path: 'factor/progress/:id', name: 'fc-progress', component: () => import('@/views/backtest/Progress.vue') },
|
||||
{ path: 'factor/result/:id', name: 'fc-result', component: () => import('@/views/factor/Result.vue') },
|
||||
|
||||
@@ -31,6 +31,9 @@ const PAGE_TITLE: Array<{ match: RegExp; group: string; title: string }> = [
|
||||
{ match: /^\/factor\/new$/, group: '投研', title: '因子分析' },
|
||||
{ match: /^\/factor\/progress\//, group: '投研', title: '因子分析进度' },
|
||||
{ match: /^\/factor\/result\//, group: '投研', title: '因子分析结果' },
|
||||
{ match: /^\/factor\/leaderboard\/.+$/, group: '投研', title: '因子详情' },
|
||||
{ match: /^\/factor\/leaderboard$/, group: '投研', title: 'IC 排行榜' },
|
||||
{ match: /^\/factor\/batch$/, group: '投研', title: '批量评估' },
|
||||
{ match: /^\/paper\/new$/, group: '模拟盘', title: '新建模拟盘' },
|
||||
{ match: /^\/paper\/result\//, group: '模拟盘', title: '模拟盘结果' },
|
||||
{ match: /^\/paper\/live\//, group: '模拟盘', title: '实走监控' },
|
||||
@@ -66,6 +69,8 @@ const CMD_ITEMS: CmdItem[] = [
|
||||
{ label: '组合回测', group: '回测', path: '/backtest/portfolio' },
|
||||
{ label: '参数优化', group: '回测', path: '/backtest/optimize' },
|
||||
{ label: '任务列表', group: '回测', path: '/backtest/history' },
|
||||
{ label: 'IC 排行榜', group: '投研', path: '/factor/leaderboard' },
|
||||
{ label: '批量评估', group: '投研', path: '/factor/batch' },
|
||||
{ label: '因子分析', group: '投研', path: '/factor/new' },
|
||||
{ label: '模拟盘列表', group: '模拟', path: '/paper' },
|
||||
{ label: '双轨对账', group: '模拟', path: '/paper/reconcile' },
|
||||
@@ -170,6 +175,8 @@ function onLogout(): void {
|
||||
</el-sub-menu>
|
||||
<el-sub-menu index="factor">
|
||||
<template #title><span class="nav-label">投研</span></template>
|
||||
<el-menu-item index="/factor/leaderboard">IC 排行榜</el-menu-item>
|
||||
<el-menu-item index="/factor/batch">批量评估</el-menu-item>
|
||||
<el-menu-item index="/factor/new">因子分析</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-sub-menu index="paper">
|
||||
|
||||
Reference in New Issue
Block a user