0dd81d709f
①后端tears序列化:sanguo_factor/tears_data.py新模块,alphalens已算分层序列(日度IC/月度聚合/十分组累计净值/多空Q10−Q1净值+最大回撤/去重叠年化/因子秩自相关)在分析时序列化为{factor}_tears.json,与tearsheet同源;FactorReport加tears_paths,GET /task/{id}/tears/{factor}端点(token header),_persist_factor同步落DB
②前端tears页:TearsPanel.vue按设计稿tab①——指标条7格+月度IC柱(红正绿负)/累计IC线双轴+月度IC热力图(年×月,CSS格)+分组累计净值Q1/Q5/Q10+多空净值(琥珀+面积+○最大回撤标注)+十分组年化(±5%虚线)+IC衰减(1/5/10D),1/5/10D全页联动;Result.vue的iframe→原生渲染;旧任务404自动回退iframe旧alphalens报告
③加入对比(设计稿tab②纯前端):factorCompare store(localStorage持久化,2~6个)+排行榜行内「+对比/✓已选」列+详情页死按钮做实(选中青色态)+全局底部托盘CompareTray(chips可删/清空/对比N因子→)+对比页Compare.vue(指标并排·行最优青色高亮/累计IC叠加多线/月度IC序列Pearson相关性矩阵前端算/十分组小倍数SVG)
测试:tears_data纯函数5+全链真实alphalens6(合成因子IC>0/分层单调/JSON可序列化)+analyzer写盘/容错2+端点401/404/200共3;factor+api+orchestrator 317全绿;npm run build绿
55 lines
3.8 KiB
TypeScript
55 lines
3.8 KiB
TypeScript
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
|
|
const routes: RouteRecordRaw[] = [
|
|
{ path: '/login', name: 'login', component: () => import('@/views/Login.vue') },
|
|
{
|
|
path: '/',
|
|
component: () => import('@/views/Layout.vue'),
|
|
children: [
|
|
{ path: '', redirect: '/dashboard' },
|
|
{ path: 'dashboard', name: 'dashboard', component: () => import('@/views/Dashboard.vue') },
|
|
{ path: 'strategy', name: 'strategy-list', component: () => import('@/views/strategy/List.vue') },
|
|
{ path: 'strategy/new', name: 'strategy-new', component: () => import('@/views/strategy/Edit.vue') },
|
|
{ path: 'strategy/code', name: 'strategy-code', component: () => import('@/views/strategy/Code.vue') },
|
|
{ path: 'strategy/instance/new', name: 'instance-new', component: () => import('@/views/strategy/InstanceEdit.vue') },
|
|
{ path: 'strategy/instance/:id', name: 'instance-edit', component: () => import('@/views/strategy/InstanceEdit.vue') },
|
|
{ path: 'account', name: 'account', component: () => import('@/views/account/List.vue') },
|
|
{ path: 'backtest/new', name: 'bt-new', component: () => import('@/views/backtest/New.vue') },
|
|
{ path: 'backtest/portfolio', name: 'bt-portfolio', component: () => import('@/views/backtest/PortfolioBacktest.vue') },
|
|
{ path: 'backtest/progress/:id', name: 'bt-progress', component: () => import('@/views/backtest/Progress.vue') },
|
|
{ path: 'backtest/result/:id', name: 'bt-result', component: () => import('@/views/backtest/Result.vue') },
|
|
{ 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/compare', name: 'fc-compare', component: () => import('@/views/factor/Compare.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') },
|
|
{ path: 'paper/new', name: 'paper-new', component: () => import('@/views/paper/New.vue') },
|
|
{ path: 'paper', name: 'paper-list', component: () => import('@/views/paper/List.vue') },
|
|
{ path: 'paper/reconcile', name: 'paper-reconcile', component: () => import('@/views/paper/Reconcile.vue') },
|
|
{ path: 'paper/result/:id', name: 'paper-result', component: () => import('@/views/paper/Result.vue') },
|
|
{ path: 'paper/live/:aid', name: 'paper-live', component: () => import('@/views/paper/Live.vue') },
|
|
{ path: 'live/new', name: 'live-new', component: () => import('@/views/live/New.vue') },
|
|
{ path: 'live', name: 'live-list', component: () => import('@/views/live/List.vue') },
|
|
{ path: 'live/monitor/:id', name: 'live-monitor', component: () => import('@/views/live/Monitor.vue') },
|
|
],
|
|
},
|
|
]
|
|
|
|
export const router = createRouter({
|
|
history: createWebHistory(),
|
|
routes,
|
|
})
|
|
|
|
router.beforeEach((to) => {
|
|
const auth = useAuthStore()
|
|
if (to.name !== 'login' && !auth.isAuthenticated) {
|
|
return { name: 'login' }
|
|
}
|
|
})
|