fix(live): 监控页三修——①收益率改首快照基线(与列表同口径,治共用QMT账户下cap兜底的假900%)②组合实盘成交落库_sync_trades(轮询broker当日成交→live_trades,此前完全没人写成交表)③updated_at北京时间显示+策略参数JSON改表格 [vps]
This commit is contained in:
@@ -47,21 +47,27 @@ const settingParsed = computed<Record<string, unknown>>(() => {
|
||||
})
|
||||
|
||||
const totalReturnPct = computed(() => {
|
||||
const total = balance.value.total
|
||||
const cap = account.value?.initial_capital
|
||||
if (total == null || !cap) return null
|
||||
return (total - cap) / cap
|
||||
// 后端算好的首快照基线收益率(与列表页同口径)。
|
||||
// 不再用 initial_capital 兜底:共用 QMT 账户时账户总额≠实例资金 → 假 900%。
|
||||
const r = account.value?.total_return
|
||||
if (r != null && Number.isFinite(r)) return r
|
||||
return null
|
||||
})
|
||||
|
||||
/** UTC ISO(库内存的是 +00:00) → 北京时间显示 */
|
||||
function fmtTime(v: string | null | undefined): string {
|
||||
if (!v) return '—'
|
||||
const d = new Date(v)
|
||||
if (Number.isNaN(d.getTime())) return v
|
||||
return new Date(d.getTime() + 8 * 3600 * 1000)
|
||||
.toISOString().slice(0, 19).replace('T', ' ')
|
||||
}
|
||||
|
||||
const todayStr = new Date().toISOString().slice(0, 10)
|
||||
const todayTrades = computed(() =>
|
||||
trades.value.filter((t) => String(t.traded_at).slice(0, 10) === todayStr),
|
||||
)
|
||||
|
||||
function orDash(v: string | null | undefined): string {
|
||||
return v && v !== 'None' ? v : '—'
|
||||
}
|
||||
|
||||
function num(v: number | null | undefined): string {
|
||||
if (v == null || !Number.isFinite(v)) return '—'
|
||||
return Math.round(v).toLocaleString('zh-CN')
|
||||
@@ -191,7 +197,7 @@ async function onStop(): Promise<void> {
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">上次更新</span>
|
||||
<span class="stat-value num">{{ orDash(status?.updated_at ?? account?.updated_at) }}</span>
|
||||
<span class="stat-value num">{{ fmtTime(status?.updated_at ?? account?.updated_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -231,7 +237,9 @@ async function onStop(): Promise<void> {
|
||||
<el-table-column prop="volume" label="总持仓" width="100" class-name="num" />
|
||||
<el-table-column prop="frozen" label="冻结" width="90" class-name="num" />
|
||||
<el-table-column prop="avg_price" label="均价" width="100" class-name="num" />
|
||||
<el-table-column prop="updated_at" label="更新时间" min-width="160" />
|
||||
<el-table-column prop="updated_at" label="更新时间" min-width="160">
|
||||
<template #default="{ row }">{{ fmtTime(row.updated_at) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
@@ -278,7 +286,11 @@ async function onStop(): Promise<void> {
|
||||
<!-- 策略参数 -->
|
||||
<el-card shadow="never">
|
||||
<template #header><span class="section-title">策略参数</span></template>
|
||||
<pre class="setting-pre mono">{{ JSON.stringify(settingParsed, null, 2) }}</pre>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item
|
||||
v-for="(v, k) in settingParsed" :key="k" :label="String(k)"
|
||||
>{{ typeof v === 'object' ? JSON.stringify(v) : String(v) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
@@ -312,18 +324,6 @@ async function onStop(): Promise<void> {
|
||||
.metric-label { font-size: 12px; color: var(--text-3); margin-bottom: 6px; }
|
||||
.metric-value { font-size: 22px; font-weight: 700; color: var(--text); }
|
||||
|
||||
.setting-pre {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border-2);
|
||||
border-radius: var(--r-sm);
|
||||
padding: 10px 14px;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
color: var(--text-2);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.stat-row { grid-template-columns: repeat(3, 1fr); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user