Jimin Huang commited on
Commit
049df43
·
1 Parent(s): 3c4f27f

Change settings

Browse files
Files changed (1) hide show
  1. src/views/LiveView.vue +35 -48
src/views/LiveView.vue CHANGED
@@ -91,20 +91,16 @@
91
  </template>
92
 
93
  <script setup>
94
- import { ref, computed, onMounted, watch } from 'vue'
95
  import AssetTabs from '../components/AssetTabs.vue'
96
  import CompareChartE from '../components/CompareChartE.vue'
97
  import { dataService } from '../lib/dataService'
98
- import { getAllDecisions } from '../lib/dataCache'
99
- import { readAllRawDecisions } from '../lib/idb'
100
- import { filterRowsToNyseTradingDays } from '../lib/marketCalendar'
101
- import { computeBuyHoldEquity } from '../lib/perf'
102
 
103
  /* ---------- config ---------- */
104
  const orderedAssets = ['BTC','ETH','MSFT','BMRN','TSLA'] // MRNA removed
105
  const EXCLUDED_AGENT_NAMES = new Set(['vanilla', 'vinilla']) // case-insensitive
106
 
107
- // Plug in your real logos here
108
  const AGENT_LOGOS = {
109
  // 'DeepFundAgent': new URL('../assets/images/agents/deepfund.png', import.meta.url).href,
110
  // 'InvestorAgent': new URL('../assets/images/agents/investor.png', import.meta.url).href,
@@ -123,8 +119,6 @@ const ASSET_ICONS = {
123
  const mode = ref('usd') // 'usd' | 'pct'
124
  const asset = ref('BTC')
125
  const rowsRef = ref([])
126
- const bhBalance = ref(null) // Buy&Hold final balance (100k start)
127
- const bhDate = ref('')
128
 
129
  /* ---------- bootstrap ---------- */
130
  onMounted(async () => {
@@ -157,6 +151,27 @@ const filteredRows = computed(() =>
157
  })
158
  )
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  /* Best model per agent (by balance) */
161
  const winners = computed(() => {
162
  const byAgent = new Map()
@@ -179,42 +194,6 @@ const winnersForChart = computed(() =>
179
  }))
180
  )
181
 
182
- /* === Buy&Hold: recompute from the SAME sequence the chart uses === */
183
- watch(
184
- () => winnersForChart.value,
185
- async (list) => { await recomputeBHFromSelection(list) },
186
- { immediate: true, deep: true }
187
- )
188
-
189
- async function recomputeBHFromSelection(list) {
190
- const sel = Array.isArray(list) && list.length ? list[0] : null
191
- if (!sel) { bhBalance.value = null; bhDate.value = ''; return }
192
-
193
- // fetch decisions once
194
- let all = getAllDecisions() || []
195
- if (!all.length) {
196
- try { const cached = await readAllRawDecisions(); if (cached?.length) all = cached } catch {}
197
- }
198
-
199
- const ids = Array.isArray(sel.decision_ids) ? sel.decision_ids : []
200
- let seq = ids.length
201
- ? all.filter(r => ids.includes(r.id))
202
- : all.filter(r => r.agent_name === sel.agent_name && r.asset === sel.asset && r.model === sel.model)
203
- seq.sort((a,b) => (a.date > b.date ? 1 : -1))
204
-
205
- const isCrypto = sel.asset === 'BTC' || sel.asset === 'ETH'
206
- if (!isCrypto) seq = await filterRowsToNyseTradingDays(seq)
207
-
208
- const bh = computeBuyHoldEquity(seq, 100000) || []
209
- if (bh.length) {
210
- bhBalance.value = bh[bh.length - 1]
211
- bhDate.value = seq[seq.length - 1]?.date || ''
212
- } else {
213
- bhBalance.value = null
214
- bhDate.value = ''
215
- }
216
- }
217
-
218
  /* Build 5 cards: Buy&Hold + top 4 agents */
219
  const cards = computed(() => {
220
  const base = Number(bhBalance.value ?? 100000)
@@ -253,7 +232,7 @@ const cards = computed(() => {
253
  return [bhCard, ...agentCards]
254
  })
255
 
256
- /* Dynamic font size for long names (no clipping) */
257
  function nameFontSize(name='') {
258
  const len = name.length
259
  if (len <= 12) return '20px'
@@ -348,12 +327,20 @@ function nameFontSize(name='') {
348
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
349
  }
350
 
351
- /* title shrinks (no ellipsis) */
352
  .card__title {
353
- font-weight: 800; color: #0F172A; white-space: nowrap;
 
 
 
 
 
 
 
 
 
354
  }
355
 
356
- .card__balance { font-weight: 900; color: #0F172A; font-size: 20px; }
357
  .card__sub { font-size: 12px; color: #5B6476; opacity: .85; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
358
  .card__meta { margin-top: 4px; align-items: center; }
359
  .card__date { margin-top: 2px; }
 
91
  </template>
92
 
93
  <script setup>
94
+ import { ref, computed, onMounted } from 'vue'
95
  import AssetTabs from '../components/AssetTabs.vue'
96
  import CompareChartE from '../components/CompareChartE.vue'
97
  import { dataService } from '../lib/dataService'
 
 
 
 
98
 
99
  /* ---------- config ---------- */
100
  const orderedAssets = ['BTC','ETH','MSFT','BMRN','TSLA'] // MRNA removed
101
  const EXCLUDED_AGENT_NAMES = new Set(['vanilla', 'vinilla']) // case-insensitive
102
 
103
+ // Plug in real logos if you have them
104
  const AGENT_LOGOS = {
105
  // 'DeepFundAgent': new URL('../assets/images/agents/deepfund.png', import.meta.url).href,
106
  // 'InvestorAgent': new URL('../assets/images/agents/investor.png', import.meta.url).href,
 
119
  const mode = ref('usd') // 'usd' | 'pct'
120
  const asset = ref('BTC')
121
  const rowsRef = ref([])
 
 
122
 
123
  /* ---------- bootstrap ---------- */
124
  onMounted(async () => {
 
151
  })
152
  )
153
 
154
+ /* Baseline (Buy & Hold) directly from tableRows */
155
+ const baselineRow = computed(() => {
156
+ const rows = (rowsRef.value || []).filter(r => r.asset === asset.value)
157
+
158
+ // heuristics: prefer explicit strategy markers, else name/model contains buy+hold
159
+ const candidates = rows.filter(r => {
160
+ const s = (r.strategy || '').toLowerCase()
161
+ const an = (r.agent_name || '').toLowerCase()
162
+ const m = (r.model || '').toLowerCase()
163
+ return s === 'buy_hold' || s === 'buy&hold' ||
164
+ (an.includes('buy') && an.includes('hold')) ||
165
+ (m.includes('buy') && m.includes('hold'))
166
+ })
167
+
168
+ if (!candidates.length) return null
169
+ const ts = (r) => new Date(r.end_date || r.last_nav_ts || 0).getTime()
170
+ return candidates.sort((a, b) => ts(b) - ts(a))[0]
171
+ })
172
+ const bhBalance = computed(() => baselineRow.value?.balance ?? null)
173
+ const bhDate = computed(() => baselineRow.value?.end_date || baselineRow.value?.last_nav_ts || '')
174
+
175
  /* Best model per agent (by balance) */
176
  const winners = computed(() => {
177
  const byAgent = new Map()
 
194
  }))
195
  )
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  /* Build 5 cards: Buy&Hold + top 4 agents */
198
  const cards = computed(() => {
199
  const base = Number(bhBalance.value ?? 100000)
 
232
  return [bhCard, ...agentCards]
233
  })
234
 
235
+ /* Dynamic font size for long names (no clipping). */
236
  function nameFontSize(name='') {
237
  const len = name.length
238
  if (len <= 12) return '20px'
 
327
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
328
  }
329
 
330
+ /* title shrinks and never pushes the number off the card */
331
  .card__title {
332
+ flex: 1 1 auto; /* take available space */
333
+ min-width: 0; /* allow shrink */
334
+ font-weight: 800; color: #0F172A;
335
+ white-space: nowrap;
336
+ }
337
+
338
+ .card__balance {
339
+ flex: 0 0 auto; /* never shrink */
340
+ white-space: nowrap;
341
+ font-weight: 900; color: #0F172A; font-size: 20px;
342
  }
343
 
 
344
  .card__sub { font-size: 12px; color: #5B6476; opacity: .85; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
345
  .card__meta { margin-top: 4px; align-items: center; }
346
  .card__date { margin-top: 2px; }