Skip to content

Commit 416dad7

Browse files
committed
feat: show lifetime strategy pnl in live operations
1 parent df071e5 commit 416dad7

4 files changed

Lines changed: 44 additions & 8 deletions

File tree

‎src/locales/lang/strategy-live-risk.js‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ const locale = {
9393
'strategyCenter.console.performanceOverview': 'Runtime performance',
9494
'strategyCenter.console.winRateSample': '{wins}/{total} profitable',
9595
'strategyCenter.console.completedTrades': 'Completed trades',
96+
'strategyCenter.console.strategyTotalPnl': 'Strategy total P&L',
9697
'strategyCenter.console.todayPnlEstimated': 'Opening baseline estimated',
9798
'strategyCenter.console.todayPnlEstimatedHint': 'No equity snapshot was available near local midnight. This value uses the trade ledger as a temporary opening baseline and becomes exact after continuous snapshots cover a day boundary.',
9899
'strategyCenter.console.netPnlBasis': 'Realized and unrealized P&L, net of recorded fees',
@@ -244,6 +245,7 @@ const enUSFallback = locale
244245

245246
const zhCN = {
246247
...enUSFallback,
248+
'strategyCenter.console.strategyTotalPnl': '策略总盈亏',
247249
'strategyRuntime.fillContractMetadataUnavailable': '合约规格暂不可用,成交入账将重试,不会猜测成交数量。',
248250
'strategyRuntime.unsupportedFillContract': '当前入账模型不支持此合约单位。',
249251
'strategyRuntime.invalidFillQuantity': '交易所成交数量无效,已暂缓入账。',
@@ -484,6 +486,7 @@ const zhCN = {
484486

485487
const zhTW = {
486488
...zhCN,
489+
'strategyCenter.console.strategyTotalPnl': '策略總盈虧',
487490
'strategyCenter.editor.leverageAutoTitle': '永續合約槓桿',
488491
'strategyCenter.editor.leverageAutoHint': '已根據永續合約自動啟用。直接填寫 1× 至 {max}×;1× 表示不放大曝險。',
489492
'strategyRuntime.fillContractMetadataUnavailable': '合約規格暫不可用,成交入帳將重試,不會猜測成交數量。',
@@ -597,6 +600,7 @@ const zhTW = {
597600

598601
const ja = {
599602
...enUSFallback,
603+
'strategyCenter.console.strategyTotalPnl': '戦略累計損益',
600604
'trading-assistant.logs.level.all': 'すべて',
601605
'trading-assistant.logs.level.info': '情報',
602606
'trading-assistant.logs.level.warning': '警告',
@@ -648,6 +652,7 @@ const ja = {
648652

649653
const ko = {
650654
...enUSFallback,
655+
'strategyCenter.console.strategyTotalPnl': '전략 총 손익',
651656
'trading-assistant.logs.level.all': '전체',
652657
'trading-assistant.logs.level.info': '정보',
653658
'trading-assistant.logs.level.warning': '경고',
@@ -695,6 +700,7 @@ const ko = {
695700

696701
const de = {
697702
...enUSFallback,
703+
'strategyCenter.console.strategyTotalPnl': 'Gesamtgewinn/-verlust der Strategie',
698704
'trading-assistant.logs.level.all': 'Alle',
699705
'trading-assistant.logs.level.info': 'Info',
700706
'trading-assistant.logs.level.warning': 'Warnung',
@@ -713,6 +719,7 @@ const de = {
713719

714720
const fr = {
715721
...enUSFallback,
722+
'strategyCenter.console.strategyTotalPnl': 'P&L total de la stratégie',
716723
'trading-assistant.logs.level.all': 'Tous',
717724
'trading-assistant.logs.level.info': 'Information',
718725
'trading-assistant.logs.level.warning': 'Avertissement',
@@ -731,6 +738,7 @@ const fr = {
731738

732739
const ru = {
733740
...enUSFallback,
741+
'strategyCenter.console.strategyTotalPnl': 'Общая прибыль/убыток стратегии',
734742
'trading-assistant.logs.level.all': 'Все',
735743
'trading-assistant.logs.level.info': 'Информация',
736744
'trading-assistant.logs.level.warning': 'Предупреждение',
@@ -749,6 +757,7 @@ const ru = {
749757

750758
const vi = {
751759
...enUSFallback,
760+
'strategyCenter.console.strategyTotalPnl': 'Tổng lãi/lỗ chiến lược',
752761
'trading-assistant.logs.level.all': 'Tất cả',
753762
'trading-assistant.logs.level.info': 'Thông tin',
754763
'trading-assistant.logs.level.warning': 'Cảnh báo',
@@ -767,6 +776,7 @@ const vi = {
767776

768777
const th = {
769778
...enUSFallback,
779+
'strategyCenter.console.strategyTotalPnl': 'กำไรขาดทุนรวมของกลยุทธ์',
770780
'trading-assistant.logs.level.all': 'ทั้งหมด',
771781
'trading-assistant.logs.level.info': 'ข้อมูล',
772782
'trading-assistant.logs.level.warning': 'คำเตือน',
@@ -785,6 +795,7 @@ const th = {
785795

786796
const ar = {
787797
...enUSFallback,
798+
'strategyCenter.console.strategyTotalPnl': 'إجمالي ربح وخسارة الاستراتيجية',
788799
'trading-assistant.logs.level.all': 'الكل',
789800
'trading-assistant.logs.level.info': 'معلومات',
790801
'trading-assistant.logs.level.warning': 'تحذير',

‎src/views/strategy-center/components/LiveOperationsTable.vue‎

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
<div class="master-columns">
3636
<span>{{ $t('strategyCenter.console.strategyAndSymbol') }}</span>
3737
<span>{{ $t('systemOverview.colStatus') }}</span>
38-
<span>{{ $t('strategyCenter.console.todayPnl') }}</span>
38+
<span>{{ $t('strategyCenter.console.strategyTotalPnl') }}</span>
3939
</div>
4040

4141
<div class="strategy-list" :class="{ 'is-loading': loading }">
@@ -55,7 +55,10 @@
5555
<i :class="statusClass(strategy)"></i>{{ statusLabel(strategy) }}
5656
<em :class="executionMode(strategy)">{{ executionLabel(strategy) }}</em>
5757
</span>
58-
<span class="row-pnl" :class="pnlClass(strategyPnl(strategy))">{{ formatPnl(strategyPnl(strategy)) }}</span>
58+
<span class="row-pnl" :class="pnlClass(strategyTotalPnl(strategy))">
59+
<strong>{{ formatPnl(strategyTotalPnl(strategy)) }}</strong>
60+
<small>{{ formatPnlPercent(strategyTotalReturn(strategy)) }}</small>
61+
</span>
5962
</button>
6063
<div v-if="!loading && filteredStrategies.length === 0" class="list-empty">
6164
<a-icon type="inbox" />
@@ -254,6 +257,7 @@
254257
:market-type="tradingConfig(selectedStrategy).market_type || 'swap'"
255258
:leverage="tradingConfig(selectedStrategy).leverage || 1"
256259
:is-dark="dark"
260+
@ownership-reconciled="$emit('ownership-reconciled')"
257261
/>
258262
</section>
259263
<section class="equity-panel panel-section overview-equity-panel">
@@ -314,6 +318,7 @@ import AiDecisionRecords from './AiDecisionRecords.vue'
314318
import { getExchangeDisplayName } from '@/utils/exchangeCredential'
315319
import {
316320
normalizeTimestampMilliseconds,
321+
strategyCapital,
317322
strategyExchangeId,
318323
strategyExecutionMode,
319324
strategyLeverage,
@@ -551,11 +556,16 @@ export default {
551556
},
552557
statusClass (strategy) { return this.needsAttention(strategy) ? 'warning' : (this.isRunning(strategy) ? 'running' : 'stopped') },
553558
statusLabel (strategy) { return this.needsAttention(strategy) ? this.healthLabel(strategy) : (this.isRunning(strategy) ? this.$t('systemOverview.running') : this.$t('systemOverview.stopped')) },
554-
strategyPnl (strategy) {
555-
if (!strategy || strategy.today_pnl == null || strategy.today_pnl === '') return null
556-
const value = Number(strategy.today_pnl)
559+
strategyTotalPnl (strategy) {
560+
if (!strategy || strategy.total_pnl == null || strategy.total_pnl === '') return null
561+
const value = Number(strategy.total_pnl)
557562
return Number.isFinite(value) ? value : null
558563
},
564+
strategyTotalReturn (strategy) {
565+
const pnl = this.strategyTotalPnl(strategy)
566+
const capital = strategyCapital(strategy)
567+
return pnl == null || !Number.isFinite(capital) || capital <= 0 ? null : pnl / capital
568+
},
559569
lastActivity (strategy) {
560570
return this.health(strategy).last_heartbeat_at || strategyLastActivity(strategy)
561571
},
@@ -565,6 +575,11 @@ export default {
565575
const number = Number(value)
566576
return `${number > 0 ? '+' : ''}${number.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
567577
},
578+
formatPnlPercent (value) {
579+
if (value == null || value === '' || !Number.isFinite(Number(value))) return '—'
580+
const number = Number(value) * 100
581+
return `${number > 0 ? '+' : ''}${number.toFixed(2)}%`
582+
},
568583
formatPercent (value, signed = true) { const number = Number(value || 0) * 100; return `${signed && number > 0 ? '+' : ''}${number.toFixed(2)}%` },
569584
formatLiveMoney (value, signed = false) {
570585
if (!this.hasFinancialLedger) return '—'
@@ -627,7 +642,9 @@ export default {
627642
.row-status > i { position: absolute; left: 0; top: 4px; width: 6px; height: 6px; border-radius: 50%; }
628643
.row-status > i.running { background: #28a85f; }.row-status > i.warning { background: #e16b47; }.row-status > i.stopped { background: #858c96; }
629644
.row-status em { width: max-content; padding: 1px 5px; border: 1px solid #dfe3e8; border-radius: 3px; color: #697382; font-size: 11px; font-style: normal; font-weight: 500; }.row-status em.live { border-color: rgba(222, 164, 24, .35); color: #a66f00; }
630-
.row-pnl { text-align: right; color: #596273; font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
645+
.row-pnl { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; text-align: right; color: #596273; font-variant-numeric: tabular-nums; }
646+
.row-pnl strong { font-size: 13px; font-weight: 650; }
647+
.row-pnl small { color: currentColor; font-size: 11px; font-weight: 500; opacity: .82; }
631648
.profit { color: #24a25a !important; }.loss { color: #d95656 !important; }
632649
.list-empty, .section-empty { display: flex; align-items: center; justify-content: center; min-height: 180px; color: #949ca7; }.section-empty.compact { min-height: 110px; }
633650
.strategy-master footer { padding: 12px 16px; border-top: 1px solid #e7eaf0; color: #7d8794; font-size: 12px; }

‎src/views/strategy-center/components/PositionRecords.vue‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,10 @@ export default {
428428
methods: {
429429
async openOwnershipRepair () {
430430
this.ownershipVisible = true
431-
await this.loadOwnership()
431+
if (await this.loadOwnership()) {
432+
await this.loadPositions()
433+
this.$emit('ownership-reconciled')
434+
}
432435
},
433436
async loadOwnership () {
434437
if (!this.strategyId) return
@@ -443,10 +446,12 @@ export default {
443446
...row,
444447
rowKey: `${row.symbol || ''}:${row.side || ''}`
445448
}))
449+
return true
446450
} catch (error) {
447451
this.ownershipExchangeId = ''
448452
this.ownershipRows = []
449453
this.$message.error((error && error.message) || this.$t('strategyCenter.positionOwnership.loadFailed'))
454+
return false
450455
} finally {
451456
this.ownershipLoading = false
452457
}
@@ -462,7 +467,9 @@ export default {
462467
})
463468
if (res.code !== 1) throw new Error(res.msg ? this.$t(res.msg) : this.$t('strategyCenter.positionOwnership.repairFailed'))
464469
this.$message.success(this.$t('strategyCenter.positionOwnership.repairSuccess'))
465-
await Promise.all([this.loadOwnership(), this.loadPositions()])
470+
await this.loadOwnership()
471+
await this.loadPositions()
472+
this.$emit('ownership-reconciled')
466473
} catch (error) {
467474
this.$message.error((error && error.message) || this.$t('strategyCenter.positionOwnership.repairFailed'))
468475
} finally {

‎src/views/strategy-center/index.vue‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
@edit="openEditLive"
3131
@delete="handleDelete"
3232
@open-workspace="openStrategyWorkspace"
33+
@ownership-reconciled="loadStrategies({ force: true })"
3334
/>
3435

3536
<live-strategy-editor

0 commit comments

Comments
 (0)