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>