quant_test / web_development /frontend /src /views /PositionsView.vue
lucky-loster's picture
Upload folder using huggingface_hub
590a501 verified
Raw
History Blame Contribute Delete
6.26 kB
<template>
<div>
<el-row :gutter="16" style="margin-bottom: 16px;">
<el-col :span="8">
<el-card shadow="never" style="border: 1px solid var(--border-color);">
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 8px;">持仓数量</div>
<div style="font-size: 28px; font-weight: 700; color: var(--accent-blue);">{{ positions.length }}</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="never" style="border: 1px solid var(--border-color);">
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 8px;">持仓盈亏</div>
<div :style="{ fontSize: '28px', fontWeight: 700, color: totalPnl >= 0 ? 'var(--accent-green)' : 'var(--accent-red)' }">
{{ totalPnl >= 0 ? '+' : '' }}{{ totalPnl.toFixed(2) }}
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="never" style="border: 1px solid var(--border-color);">
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 8px;">占用保证金</div>
<div style="font-size: 28px; font-weight: 700; color: var(--accent-yellow);">{{ totalMargin.toFixed(2) }}</div>
</el-card>
</el-col>
</el-row>
<el-card shadow="never" style="border: 1px solid var(--border-color); margin-bottom: 16px;">
<template #header>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="font-weight: 600;">当前持仓</span>
<el-button size="small" text @click="loadPositions">
<el-icon><Refresh /></el-icon> 刷新
</el-button>
</div>
</template>
<div v-if="positions.length === 0" style="text-align: center; padding: 50px; color: var(--text-secondary);">
暂无持仓
</div>
<el-table v-else :data="positions" stripe style="width: 100%">
<el-table-column prop="symbol" label="合约" width="100" />
<el-table-column label="方向" width="80">
<template #default="{ row }">
<el-tag :type="row.side === 'LONG' ? 'success' : 'danger'" effect="dark">
{{ row.side === 'LONG' ? '多' : '空' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="quantity" label="数量" width="80" />
<el-table-column label="开仓均价" width="120">
<template #default="{ row }">{{ row.avg_price?.toFixed(2) }}</template>
</el-table-column>
<el-table-column label="当前价" width="120">
<template #default="{ row }">{{ row.current_price?.toFixed(2) }}</template>
</el-table-column>
<el-table-column label="浮动盈亏" width="140">
<template #default="{ row }">
<span :class="row.unrealized_pnl >= 0 ? 'price-up' : 'price-down'" style="font-weight: 600;">
{{ row.unrealized_pnl >= 0 ? '+' : '' }}{{ row.unrealized_pnl?.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column label="保证金" width="120">
<template #default="{ row }">{{ row.margin?.toFixed(2) }}</template>
</el-table-column>
<el-table-column prop="leverage" label="杠杆" width="80">
<template #default="{ row }">{{ row.leverage }}x</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button size="small" type="danger" @click="closePosition(row)">平仓</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-card shadow="never" style="border: 1px solid var(--border-color);">
<template #header><span style="font-weight: 600;">成交历史</span></template>
<el-table :data="trades" stripe style="width: 100%" size="small" :max-height="300">
<el-table-column prop="trade_id" label="成交号" width="140" />
<el-table-column prop="symbol" label="合约" width="90" />
<el-table-column label="方向" width="70">
<template #default="{ row }">
<el-tag :type="row.side === 'BUY' ? 'success' : 'danger'" size="small">{{ row.side === 'BUY' ? '买' : '卖' }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="quantity" label="数量" width="60" />
<el-table-column label="成交价" width="100">
<template #default="{ row }">{{ row.price?.toFixed(2) }}</template>
</el-table-column>
<el-table-column label="盈亏" width="100">
<template #default="{ row }">
<span :class="row.pnl >= 0 ? 'price-up' : 'price-down'">{{ row.pnl?.toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column prop="strategy_id" label="策略" width="140" />
<el-table-column prop="timestamp" label="时间" />
</el-table>
</el-card>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { positionApi, tradeApi, orderApi } from '../api'
import { useTradingStore } from '../stores/trading'
const store = useTradingStore()
const positions = ref([])
const trades = ref([])
const totalPnl = computed(() => positions.value.reduce((sum, p) => sum + (p.unrealized_pnl || 0), 0))
const totalMargin = computed(() => positions.value.reduce((sum, p) => sum + (p.margin || 0), 0))
async function loadPositions() {
try {
const { data } = await positionApi.getPositions()
positions.value = data
} catch (e) { console.error(e) }
}
async function loadTrades() {
try {
const { data } = await tradeApi.getTrades()
trades.value = data
} catch (e) { console.error(e) }
}
async function closePosition(pos) {
try {
await orderApi.placeOrder({
symbol: pos.symbol,
side: pos.side === 'LONG' ? 'SELL' : 'BUY',
order_type: 'MARKET',
quantity: pos.quantity,
})
ElMessage.success('平仓成功')
loadPositions()
loadTrades()
store.fetchAccount()
} catch (e) { ElMessage.error('平仓失败') }
}
onMounted(() => { loadPositions(); loadTrades() })
</script>