File size: 6,264 Bytes
590a501 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | <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>
|