feat(s2): 投研核心端到端跑通(IC 表 + tears 报告)
- Task 加 raw_result 字段;orchestrator get_raw_result(内存存 FactorReport)
- 路由 /factor/list、/task/{id}/ic-summary、/task/{id}/report/{factor}(query token 给 iframe)
- analyzer cfg=None 时加载 data_platform.yaml(修 API 路径 read_db_daily 崩)
- get_status 返回 error_msg(调试+前端 failed 展示)
- 前端 投研-新建(多因子/多标的/日期)+ 结果页(IC 表 + tears iframe)
- factor 冒烟通过:ma5 → IC 1D/5D/10D 真实数据
This commit is contained in:
@@ -1,4 +1,82 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getFactors, submitFactor, type FactorItem } from '@/api/factor'
|
||||
|
||||
const router = useRouter()
|
||||
const factors = ref<FactorItem[]>([])
|
||||
const loading = ref(false)
|
||||
const submitting = ref(false)
|
||||
|
||||
const form = reactive({
|
||||
factor_names: [] as string[],
|
||||
symbolsText: '600000\n000001\n300750',
|
||||
start: '2024-01-01',
|
||||
end: '2024-06-30',
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
factors.value = await getFactors()
|
||||
} catch {
|
||||
ElMessage.error('因子列表加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
async function onSubmit(): Promise<void> {
|
||||
const symbols = form.symbolsText
|
||||
.split(/[\s,,]+/)
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
if (!form.factor_names.length || symbols.length < 2) {
|
||||
ElMessage.warning('至少选 1 个因子 + 2 个标的(IC 横截面需多标的)')
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
const tid = await submitFactor({
|
||||
symbols,
|
||||
factor_names: form.factor_names,
|
||||
start: form.start,
|
||||
end: form.end,
|
||||
})
|
||||
ElMessage.success('因子分析已提交')
|
||||
router.push(`/factor/progress/${tid}`)
|
||||
} catch {
|
||||
ElMessage.error('提交失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-empty description="投研 - 新建因子分析(S2 切片实现)" />
|
||||
<el-card v-loading="loading">
|
||||
<template #header>
|
||||
<h3>新建因子分析</h3>
|
||||
</template>
|
||||
<el-form label-width="140px">
|
||||
<el-form-item label="因子">
|
||||
<el-select v-model="form.factor_names" multiple placeholder="选择因子" style="width: 380px">
|
||||
<el-option v-for="f in factors" :key="f.name" :label="f.name" :value="f.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="标的(≥2,每行一个)">
|
||||
<el-input v-model="form.symbolsText" type="textarea" :rows="3" placeholder="600000 000001 300750" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期">
|
||||
<el-date-picker v-model="form.start" type="date" value-format="YYYY-MM-DD" style="width: 220px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期">
|
||||
<el-date-picker v-model="form.end" type="date" value-format="YYYY-MM-DD" style="width: 220px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="submitting" @click="onSubmit">提交分析</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,89 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getIcSummary, reportUrl } from '@/api/factor'
|
||||
|
||||
interface IcStats {
|
||||
mean?: number
|
||||
std?: number
|
||||
icir?: number
|
||||
t_stat?: number
|
||||
count?: number
|
||||
error?: string
|
||||
}
|
||||
interface FactorInfo {
|
||||
status?: string
|
||||
ic?: Record<string, IcStats>
|
||||
error?: string
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const taskId = String(route.params.id)
|
||||
const loading = ref(true)
|
||||
const icSummary = ref<Record<string, FactorInfo>>({})
|
||||
|
||||
const rows = computed(() => {
|
||||
const out: Array<Record<string, unknown>> = []
|
||||
for (const [factor, info] of Object.entries(icSummary.value)) {
|
||||
const ic = info?.ic || {}
|
||||
for (const [period, s] of Object.entries(ic)) {
|
||||
out.push({ factor, period, ...s })
|
||||
}
|
||||
}
|
||||
return out
|
||||
})
|
||||
|
||||
const factors = computed(() => Object.keys(icSummary.value))
|
||||
|
||||
function fmt(v: unknown): string {
|
||||
if (typeof v === 'number') return (Math.round(v * 10000) / 10000).toString()
|
||||
return v == null ? '' : String(v)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
icSummary.value = (await getIcSummary(taskId)) as Record<string, FactorInfo>
|
||||
} catch {
|
||||
ElMessage.error('IC 摘要加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-empty description="投研 - 结果(S2 切片实现)" />
|
||||
<div v-loading="loading">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<h3>IC 统计</h3>
|
||||
</template>
|
||||
<el-table :data="rows" stripe size="small" empty-text="无 IC 数据">
|
||||
<el-table-column prop="factor" label="因子" width="120" />
|
||||
<el-table-column prop="period" label="周期" width="80" />
|
||||
<el-table-column label="IC 均值">
|
||||
<template #default="{ row }">{{ fmt(row.mean) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="IC 标准差">
|
||||
<template #default="{ row }">{{ fmt(row.std) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="ICIR">
|
||||
<template #default="{ row }">{{ fmt(row.icir) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="t 统计">
|
||||
<template #default="{ row }">{{ fmt(row.t_stat) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="样本数">
|
||||
<template #default="{ row }">{{ row.count }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-card v-for="f in factors" :key="f" style="margin-top: 16px">
|
||||
<template #header>
|
||||
<h3>tears 报告 — {{ f }}</h3>
|
||||
</template>
|
||||
<iframe :src="reportUrl(taskId, f)" style="width: 100%; height: 600px; border: 0" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user