新增日内方向分析功能:基于三层打分数据由 AI 批量生成下一个交易日方向判断
This commit is contained in:
159
web/frontend/src/views/DailyDirectionView.vue
Normal file
159
web/frontend/src/views/DailyDirectionView.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {
|
||||
listDailyDirections,
|
||||
runDailyDirection,
|
||||
type DailyDirection,
|
||||
type DailyDirectionRunResponse,
|
||||
} from '@/api/daily'
|
||||
|
||||
const items = ref<DailyDirection[]>([])
|
||||
const loading = ref(false)
|
||||
const running = ref(false)
|
||||
const runResult = ref<DailyDirectionRunResponse | null>(null)
|
||||
|
||||
const symbolNames: Record<string, string> = {
|
||||
FG: '玻璃', SA: '纯碱', RB: '螺纹钢', MA: '甲醇', CF: '棉花', M: '豆粕',
|
||||
}
|
||||
|
||||
const directionLabel = (d: string) => {
|
||||
switch (d) {
|
||||
case 'bullish': return '看多'
|
||||
case 'bearish': return '看空'
|
||||
case 'neutral': return '震荡'
|
||||
default: return d
|
||||
}
|
||||
}
|
||||
|
||||
const directionType = (d: string): '' | 'success' | 'danger' | 'warning' => {
|
||||
switch (d) {
|
||||
case 'bullish': return 'success'
|
||||
case 'bearish': return 'danger'
|
||||
case 'neutral': return 'warning'
|
||||
default: return ''
|
||||
}
|
||||
}
|
||||
|
||||
const runSummary = computed(() => {
|
||||
if (!runResult.value) return ''
|
||||
const parts: string[] = []
|
||||
for (const r of runResult.value.results ?? []) {
|
||||
parts.push(`${symbolNames[r.symbol] ?? r.symbol}: ${directionLabel(r.direction)}`)
|
||||
}
|
||||
return parts.join(' | ')
|
||||
})
|
||||
|
||||
async function fetchList() {
|
||||
loading.value = true
|
||||
try {
|
||||
items.value = await listDailyDirections({ limit: 50 })
|
||||
} catch {
|
||||
ElMessage.error('加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRun() {
|
||||
running.value = true
|
||||
runResult.value = null
|
||||
try {
|
||||
runResult.value = await runDailyDirection()
|
||||
const ok = runResult.value?.results?.length ?? 0
|
||||
const fail = runResult.value?.errors?.length ?? 0
|
||||
if (fail > 0) {
|
||||
ElMessage.warning(`分析完成:成功 ${ok} 个,失败 ${fail} 个`)
|
||||
} else {
|
||||
ElMessage.success(`已完成 ${ok} 个品种的方向分析`)
|
||||
}
|
||||
await fetchList()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.response?.data?.error || '分析失败')
|
||||
} finally {
|
||||
running.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function formatLevels(json: string): string {
|
||||
try {
|
||||
const arr = JSON.parse(json) as number[]
|
||||
return arr.join(', ')
|
||||
} catch {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(fetchList)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="daily-direction">
|
||||
<div class="toolbar">
|
||||
<h2>日内方向分析</h2>
|
||||
<el-button type="primary" :loading="running" @click="handleRun">
|
||||
{{ running ? 'AI 分析中...' : '执行分析' }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 运行结果摘要 -->
|
||||
<el-alert
|
||||
v-if="runResult"
|
||||
:title="`${runResult.trade_date} 分析结果`"
|
||||
:description="runSummary"
|
||||
type="info"
|
||||
show-icon
|
||||
closable
|
||||
style="margin-bottom: 16px"
|
||||
/>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table :data="items" stripe v-loading="loading" empty-text="暂无数据,请先执行分析">
|
||||
<el-table-column prop="symbol" label="品种" width="80">
|
||||
<template #default="{ row }">
|
||||
{{ symbolNames[row.symbol] ?? row.symbol }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="trade_date" label="分析日" width="110" />
|
||||
<el-table-column prop="target_date" label="目标日" width="110" />
|
||||
<el-table-column prop="direction" label="方向" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="directionType(row.direction)" size="small">
|
||||
{{ directionLabel(row.direction) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="confidence" label="置信度" width="90">
|
||||
<template #default="{ row }">
|
||||
<span :style="{ color: row.confidence >= 70 ? '#67c23a' : row.confidence >= 50 ? '#e6a23c' : '#f56c6c' }">
|
||||
{{ row.confidence }}%
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支撑位" width="120">
|
||||
<template #default="{ row }">{{ formatLevels(row.support) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="阻力位" width="120">
|
||||
<template #default="{ row }">{{ formatLevels(row.resistance) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="reasoning" label="分析逻辑" min-width="280" show-overflow-tooltip />
|
||||
<el-table-column prop="risk_note" label="风险提示" min-width="160" show-overflow-tooltip />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.daily-direction {
|
||||
max-width: 1400px;
|
||||
}
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.toolbar h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user