quant_test / web_development /frontend /src /views /FactorResearchView.vue
lucky-loster's picture
Upload folder using huggingface_hub
590a501 verified
Raw
History Blame Contribute Delete
6.35 kB
<template>
<div class="factor-research-view">
<div class="page-header">
<h2>因子研究</h2>
<p>对接 ML-Alpha-Research-System:因子注册表、算子库、策略框架</p>
</div>
<el-alert
v-if="!researchAvailable"
title="Qlib 研究模块未启动"
type="warning"
show-icon
:closable="false"
class="mb-16"
>
请先在「服务控制台」启动 <strong>Qlib 因子研究</strong> 模块
</el-alert>
<el-tabs v-model="activeTab" @tab-change="onTabChange">
<el-tab-pane label="注册因子" name="factors">
<el-table :data="factors" v-loading="loadingFactors" stripe max-height="520">
<el-table-column prop="name" label="名称" width="160" />
<el-table-column prop="expression" label="表达式" min-width="280" show-overflow-tooltip />
<el-table-column prop="category" label="分类" width="100" />
<el-table-column prop="description" label="说明" min-width="180" show-overflow-tooltip />
</el-table>
</el-tab-pane>
<el-tab-pane label="算子库" name="operators">
<el-row :gutter="16">
<el-col :span="12">
<el-card shadow="never">
<template #header>内置算子</template>
<el-table :data="operators" size="small" max-height="400">
<el-table-column prop="name" label="算子" width="120" />
<el-table-column prop="category" label="类别" width="90" />
<el-table-column prop="description" label="说明" show-overflow-tooltip />
</el-table>
</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="never">
<template #header>构建表达式</template>
<el-form label-width="80px" size="small">
<el-form-item label="因子名">
<el-input v-model="buildForm.name" placeholder="my_factor" />
</el-form-item>
<el-form-item label="算子树">
<el-input
v-model="buildForm.treeJson"
type="textarea"
:rows="6"
placeholder='{"op":"rank","args":[{"op":"$close"}]}'
/>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="building" @click="buildExpression">生成表达式</el-button>
</el-form-item>
</el-form>
<el-input v-if="builtExpression" v-model="builtExpression" readonly type="textarea" :rows="3" />
</el-card>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane label="策略" name="strategies">
<el-table :data="strategies" v-loading="loadingStrategies" stripe>
<el-table-column prop="name" label="策略 ID" width="180" />
<el-table-column prop="description" label="说明" min-width="260" />
<el-table-column prop="class" label="实现类" width="200" show-overflow-tooltip />
</el-table>
</el-tab-pane>
<el-tab-pane label="快速命令" name="commands">
<el-card shadow="never">
<pre class="command-block">{{ commandHints }}</pre>
</el-card>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { researchApi } from '../api'
const activeTab = ref('factors')
const researchAvailable = ref(true)
const factors = ref([])
const operators = ref([])
const strategies = ref([])
const loadingFactors = ref(false)
const loadingStrategies = ref(false)
const building = ref(false)
const builtExpression = ref('')
const buildForm = ref({
name: 'custom_factor',
treeJson: '{"op":"rank","args":[{"field":"$close"}]}',
})
const commandHints = `# 因子列表与计算
python scripts/run_factor.py list
python scripts/run_factor.py compute --name momentum_20
# 单因子回测
python scripts/run_factor.py backtest --name momentum_20
# 策略回测
python scripts/run_backtest.py --strategy topk_dropout --signal-path artifacts/factors/momentum_20.pkl
# Qlib 实验
python scripts/run_qrun.py --config config/workflows/workflow_alpha158_baseline.yaml
`
async function loadFactors() {
loadingFactors.value = true
try {
const res = await researchApi.listFactors()
factors.value = res.data.factors || []
researchAvailable.value = true
} catch (e) {
researchAvailable.value = false
factors.value = []
} finally {
loadingFactors.value = false
}
}
async function loadOperators() {
try {
const res = await researchApi.listOperators()
operators.value = res.data.operators || []
} catch (e) {
operators.value = []
}
}
async function loadStrategies() {
loadingStrategies.value = true
try {
const res = await researchApi.listStrategies()
strategies.value = res.data.strategies || []
} catch (e) {
strategies.value = []
} finally {
loadingStrategies.value = false
}
}
async function buildExpression() {
building.value = true
builtExpression.value = ''
try {
let tree
try {
tree = JSON.parse(buildForm.value.treeJson)
} catch {
ElMessage.error('算子树 JSON 格式错误')
return
}
const res = await researchApi.buildExpression({
name: buildForm.value.name,
tree,
})
builtExpression.value = res.data.expression || ''
ElMessage.success('表达式已生成')
} catch (e) {
ElMessage.error(e.response?.data?.detail || '构建失败')
} finally {
building.value = false
}
}
function onTabChange(name) {
if (name === 'operators' && !operators.value.length) loadOperators()
if (name === 'strategies' && !strategies.value.length) loadStrategies()
}
onMounted(() => {
loadFactors()
})
</script>
<style scoped>
.factor-research-view { padding: 0 4px; }
.page-header { margin-bottom: 20px; }
.page-header h2 { margin: 0 0 8px; font-size: 22px; }
.page-header p { margin: 0; color: #909399; font-size: 14px; }
.mb-16 { margin-bottom: 16px; }
.command-block {
margin: 0;
padding: 16px;
background: #1e1e1e;
color: #d4d4d4;
border-radius: 6px;
font-size: 13px;
line-height: 1.6;
overflow-x: auto;
}
</style>