Jimin Huang commited on
Commit
ca3ff8a
·
1 Parent(s): b55b694

Change settings

Browse files
Files changed (1) hide show
  1. src/views/RequestView.vue +260 -142
src/views/RequestView.vue CHANGED
@@ -1,13 +1,12 @@
1
  <template>
2
  <div class="arena-page">
3
-
4
  <!-- ============== ASSETS ============== -->
5
  <section class="section">
6
  <header class="section-head">
7
  <h2 class="section-title">
8
  <span class="ama-gradient">Assets in the Arena</span>
9
  </h2>
10
- <p class="section-sub">Four-up gridtype1-month price sparkline quick stats</p>
11
  </header>
12
 
13
  <div class="grid grid-assets-4">
@@ -29,18 +28,73 @@
29
  </div>
30
 
31
  <div class="asset-stats">
32
-
33
- </div>
34
-
35
- <div class="stat-value mono" :class="g.winRate >= 0.5 ? 'pos' : 'neg'">
36
- {{ Math.round(g.winRate * 100) }}%
 
 
 
 
 
 
 
 
 
 
 
 
37
  </div>
38
  </div>
39
-
40
- <div class="stat-value mono">{{ g.trades }}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  </div>
42
-
43
- <div class="stat-value mono">{{ g.days }}</div>
 
 
 
 
 
44
  </div>
45
  </div>
46
 
@@ -51,7 +105,7 @@
51
  </article>
52
  </div>
53
 
54
- <!-- ================= Integration Guide (info-only, no external UI libs) ================= -->
55
  <div class="card integration-guide">
56
  <h3>Agent Integration Guide</h3>
57
 
@@ -62,7 +116,7 @@
62
 
63
  <div class="guide-section">
64
  <h4>Input (What we send to your agent)</h4>
65
- <pre class="code-block">{
66
  "date": "2025-10-24",
67
  "price": {"BTC": 67890.50},
68
  "news": {"BTC": "Bitcoin news..."},
@@ -85,35 +139,40 @@
85
 
86
  <div class="guide-section">
87
  <h4>Output (What we expect from your agent)</h4>
88
- <pre class="code-block">{
89
  "recommended_action": "BUY"
90
  }</pre>
91
  <p class="note"><strong>Valid actions:</strong> <code>"BUY"</code>, <code>"SELL"</code>, or <code>"HOLD"</code> (uppercase)</p>
92
  </div>
93
  </div>
94
 
95
- <!-- ================= Submit Agent (button only) ================= -->
96
- <div class="cta-row">
97
- <a :href="agentFormUrl" target="_blank" rel="noopener" class="btn-cta link-btn">
98
- <i class=\"pi pi-send mr-2\" /> Submit Agent (Google Form)
99
- </a>
100
- </div>
101
  </section>
102
-
103
  </div>
104
  </template>
105
 
106
  <script>
107
- /**
108
- * Minimal inline sparkline component (SVG) — no external deps.
109
- * NOTE: width/height handled via viewBox for responsive fit.
110
- */
 
 
 
111
  import * as echarts from 'echarts/core'
112
  import { LineChart } from 'echarts/charts'
113
  import { GridComponent, TooltipComponent } from 'echarts/components'
114
  import { CanvasRenderer } from 'echarts/renderers'
115
  echarts.use([LineChart, GridComponent, TooltipComponent, CanvasRenderer])
116
 
 
 
 
117
  const MiniEchart = {
118
  name: 'MiniEchart',
119
  props: {
@@ -123,29 +182,37 @@ const MiniEchart = {
123
  mounted() {
124
  const el = this.$refs.c
125
  this._chart = echarts.init(el)
126
- const d = Array.isArray(this.data) ? this.data : []
127
- this._chart.setOption({
128
- animation: true,
129
- grid: { left: 0, right: 0, top: 0, bottom: 0 },
130
- xAxis: { type: 'category', show: false, boundaryGap: false, data: d.map((_, i) => i) },
131
- yAxis: { type: 'value', show: false, scale: true },
132
- tooltip: { show: false },
133
- color: [this.color || '#22c55e'],
134
- series: [{
135
- type: 'line',
136
- data: d,
137
- showSymbol: false,
138
- smooth: 0.35,
139
- lineStyle: { width: 2 },
140
- areaStyle: { opacity: 0.15 }
141
- }]
142
- })
 
 
 
 
 
143
  },
144
  watch: {
145
  data(newVal) {
146
  if (!this._chart) return
147
  const d = Array.isArray(newVal) ? newVal : []
148
- this._chart.setOption({ xAxis: { data: d.map((_, i) => i) }, series: [{ data: d }] })
 
 
 
149
  }
150
  },
151
  beforeUnmount() { if (this._chart) { this._chart.dispose(); this._chart = null } },
@@ -158,59 +225,19 @@ export default {
158
  data() {
159
  return {
160
  assetFormUrl: 'https://forms.gle/your-asset-request-form',
161
-
162
  agentFormUrl: 'https://forms.gle/your-agent-submit-form',
163
 
164
- // ===== Demo data (replace with live) =====
165
- assets: [
166
- {
167
- code: 'BTC',
168
- name: 'Bitcoin',
169
- type: 'Crypto',
170
- icon: new URL('../assets/images/assets_images/BTC.png', import.meta.url).href,
171
- month: [62000, 63500, 62800, 64200, 65000, 66100, 65500, 67000, 67800, 67550, 68220, 69000],
172
- sparkColor: '#f59e0b',
173
- change1m: 0.112, runs: 86, agents: 4, eod: '10/25/2025',
174
- coveragePct: 92, desc: 'Blue-chip crypto asset; 24/7 trading.'
175
- },
176
- {
177
- code: 'ETH',
178
- name: 'Ethereum',
179
- type: 'Crypto',
180
- icon: new URL('../assets/images/assets_images/ETH.png', import.meta.url).href,
181
- month: [2500, 2480, 2525, 2600, 2580, 2630, 2680, 2700, 2660, 2720, 2735, 2790],
182
- sparkColor: '#6366f1',
183
- change1m: 0.116, runs: 84, agents: 3, eod: '10/25/2025',
184
- coveragePct: 88, desc: 'Smart-contract platform; high on-chain activity.'
185
- },
186
- {
187
- code: 'MSFT',
188
- name: 'Microsoft',
189
- type: 'Stock',
190
- icon: new URL('../assets/images/assets_images/MSFT.png', import.meta.url).href,
191
- month: [406, 409, 404, 415, 420, 422, 418, 425, 430, 432, 429, 435],
192
- sparkColor: '#22c55e',
193
- change1m: 0.071, runs: 60, agents: 4, eod: '10/25/2025',
194
- coveragePct: 77, desc: 'Megacap technology equity; NYSE trading days.'
195
- },
196
- {
197
- code: 'BMRN',
198
- name: 'BioMarin',
199
- type: 'Stock',
200
- icon: new URL('../assets/images/assets_images/BMRN.png', import.meta.url).href,
201
- month: [82, 83, 82.5, 84, 85, 84.4, 86.2, 85.8, 87.1, 88, 87.6, 89.2],
202
- sparkColor: '#06b6d4',
203
- change1m: 0.086, runs: 51, agents: 3, eod: '10/25/2025',
204
- coveragePct: 71, desc: 'Biotech equity; catalyst-driven volatility.'
205
- },
206
- ],
207
 
 
208
  agents: [
209
  {
210
  name: 'InvestorAgent',
211
  github: 'https://github.com/your-org/investor-agent',
212
  arxiv: 'https://arxiv.org/abs/xxxyyy',
213
- strategy: 'Aggressive Long Only', focus: 'BTC, MSFT',
 
214
  color: 'linear-gradient(90deg,#ffd700,#eab308)',
215
  logo: new URL('../assets/images/agents_images/investor.png', import.meta.url).href
216
  },
@@ -218,7 +245,8 @@ export default {
218
  name: 'TradeAgent',
219
  github: 'https://github.com/your-org/trade-agent',
220
  arxiv: 'https://arxiv.org/abs/aaabbb',
221
- strategy: 'Aggressive Long Only', focus: 'BTC, ETH',
 
222
  color: 'linear-gradient(90deg,#4338ca,#6d28d9)',
223
  logo: new URL('../assets/images/agents_images/tradeagent.png', import.meta.url).href
224
  },
@@ -226,7 +254,8 @@ export default {
226
  name: 'DeepFundAgent',
227
  github: 'https://github.com/your-org/deepfund-agent',
228
  arxiv: 'https://arxiv.org/abs/cccdde',
229
- strategy: 'Aggressive Long Only', focus: '—',
 
230
  color: 'linear-gradient(90deg,#0ea5e9,#22d3ee)',
231
  logo: new URL('../assets/images/agents_images/deepfund.png', import.meta.url).href
232
  },
@@ -234,32 +263,147 @@ export default {
234
  name: 'HedgeFundAgent',
235
  github: 'https://github.com/your-org/hedgefund-agent',
236
  arxiv: 'https://arxiv.org/abs/fffggg',
237
- strategy: 'Aggressive Long Only', focus: 'BMRN, TSLA',
 
238
  color: 'linear-gradient(90deg,#f43f5e,#ef4444)',
239
  logo: new URL('../assets/images/agents_images/hedgefund.png', import.meta.url).href
240
  }
241
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  }
 
 
 
 
 
 
 
 
 
 
 
243
  },
 
244
  methods: {
245
- signedMoney(n) {
246
- const v = Number(n || 0)
247
- const s = v >= 0 ? '+' : '−'
248
- return `${s}${Math.abs(v).toLocaleString(undefined, {
249
- style: 'currency', currency: 'USD', maximumFractionDigits: 2
250
- })}`
251
- },
252
  signedPct(p) {
253
  const v = Number(p || 0) * 100
254
  const s = v >= 0 ? '+' : '−'
255
  return `${s}${Math.abs(v).toFixed(2)}%`
256
- } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  }
258
  </script>
259
 
260
  <style scoped>
261
  /* ===== AMA Core ===== */
262
- :root{ --card-pad:12px; --radius:14px; }
263
  .ama-gradient{
264
  background: linear-gradient(90deg, rgb(0,0,185), #7b2cbf, #d946ef, rgb(240,0,15));
265
  -webkit-background-clip: text; background-clip: text; color: transparent;
@@ -267,14 +411,14 @@ export default {
267
 
268
  .arena-page{
269
  max-width:1280px; margin:0 auto;
270
- padding:16px 20px 120px; /* leave space for fixed footer */
271
- background:#fff;
272
  }
273
 
274
  /* ===== Helpers ===== */
275
  .truncate{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
276
  .clamp-2{ display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
277
  .w-full{ width:100%; }
 
278
 
279
  /* ===== Sections ===== */
280
  .section{ margin-top:18px; }
@@ -282,29 +426,26 @@ export default {
282
  .section-title{ font-size:clamp(1.2rem, 1.2rem + 0.4vw, 1.6rem); font-weight:800; letter-spacing:-0.02em; display:inline-block; }
283
  .section-sub{ margin-top:4px; color:#6b7280; }
284
 
285
- /* ===== Grid layouts ===== */
286
  .grid{ display:grid; gap:14px; min-width:0; }
287
  .grid-assets-4{ grid-template-columns: repeat(4, minmax(0,1fr)); }
288
  .grid-agents{ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
289
-
290
  @media (max-width: 1200px){ .grid-assets-4{ grid-template-columns: repeat(3, minmax(0,1fr)); } }
291
  @media (max-width: 900px){ .grid-assets-4{ grid-template-columns: repeat(2, minmax(0,1fr)); } }
292
  @media (max-width: 620px){ .grid-assets-4{ grid-template-columns: 1fr; } }
293
 
294
  /* ===== Card base ===== */
295
  .card{
296
- background:#ffffff; border:1px solid #E7ECF3; border-radius:var(--radius);
297
  box-shadow:0 1px 2px rgba(16,24,40,.03), 0 4px 12px rgba(16,24,40,.04);
298
  transition: transform .18s ease, box-shadow .2s ease, border-color .2s ease;
299
- min-width:0; /* allow children to shrink in grid */
300
  }
301
  .card:hover{ transform:translateY(-2px); box-shadow:0 10px 26px rgba(16,24,40,.08); border-color:#D9E2EF; }
302
 
303
  /* ===== Assets ===== */
304
- .asset-card{ padding:var(--card-pad); display:flex; flex-direction:column; gap:10px; }
305
- .asset-head{
306
- display:grid; grid-template-columns:44px 1fr auto; gap:10px; align-items:center; min-width:0;
307
- }
308
  .asset-logo-wrap{ width:44px; height:44px; border-radius:12px; background:#f3f4f6; display:grid; place-items:center; border:1px solid #E7ECF3; overflow:hidden; }
309
  .asset-logo{ width:70%; height:70%; object-fit:contain; }
310
  .asset-name{ min-width:0; }
@@ -312,19 +453,13 @@ export default {
312
  .asset-full{ font-size:12px; color:#64748b; }
313
  .type-badge{
314
  border-radius:999px; padding:4px 8px; font-size:12px; font-weight:700;
315
- border:1px solid #E7ECF3; background:#F6F8FB; color:#0f172a; white-space:nowrap; max-width:100%;
316
  }
317
  .type-crypto{ background:#f8fbff; border-color:#e0e7ff; color:#1e3a8a; }
318
  .type-stock { background:#f6fffb; border-color:#d9fbe6; color:#064e3b; }
319
- .type-etf { background:#fff7f7; border-color:#ffe4e6; color:#7f1d1d; }
320
 
321
- .asset-body{ display:flex; flex-direction:column; gap:10px; min-height:0; }
322
  .spark-wrap{ width:100%; height:90px; overflow:hidden; border-radius:8px; }
323
- .echart-mini{ width:100%; height:90px; }
324
- .spark-svg{ width:100%; height:100%; display:block; }
325
- .spark-up{ filter: drop-shadow(0 0 0 rgba(0,0,0,0)); }
326
- .spark-down{ opacity:.95; }
327
-
328
  .asset-stats{ display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); gap:10px; }
329
  .stat{ background:#F6F8FB; border:1px solid #E7ECF3; border-radius:10px; padding:8px; min-width:0; }
330
  .stat-label{ font-size:11px; color:#6b7280; margin-bottom:2px; }
@@ -339,27 +474,21 @@ export default {
339
  .asset-bar::after{ content:''; position:absolute; left:0; top:0; height:100%; width:var(--pct,0%); background:linear-gradient(90deg,#16a34a,#22c55e); }
340
  .asset-foot-row{ margin-top:6px; display:flex; align-items:center; justify-content:space-between; color:#6b7280; }
341
 
342
- /* ===== Agents ===== */
343
- .agent-card{ padding:var(--card-pad); display:flex; flex-direction:column; gap:10px; }
344
- .agent-top{
345
- display:grid; grid-template-columns:52px 1fr auto; gap:10px; align-items:center; min-width:0;
346
- }
347
  .agent-badge{ width:52px; height:52px; border-radius:16px; border:2px solid #e5e7eb; position:relative; display:grid; place-items:center; overflow:hidden; }
348
  .agent-ring{ position:absolute; inset:0; opacity:.18; }
349
  .agent-logo{ width:70%; height:70%; object-fit:contain; position:relative; z-index:1; }
350
-
351
  .agent-title{ min-width:0; }
352
  .agent-name{ font-weight:900; letter-spacing:.02em; color:#0f172a; text-transform:uppercase; }
353
  .agent-links{ display:flex; align-items:center; gap:8px; color:#64748b; flex-wrap:wrap; min-width:0; }
354
  .link{ color:#334155; font-weight:600; text-decoration:none; }
355
  .link:hover{ color:#0f172a; text-decoration:underline; }
356
- .dot{ color:#94a3b8; }.pos{ color:#0e7a3a; } .neg{ color:#B91C1C; }
357
-
358
  .agent-foot{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:2px; }
359
  .chip{ display:inline-flex; align-items:center; gap:6px; padding:4px 8px; border-radius:999px; font-size:12px; font-weight:700; background:#F6F8FB; color:#0f172a; border:1px solid #E7ECF3; white-space:nowrap; }
360
  .chip-outline{ background:#fff; }
361
- .chip-pos{ background:#E9F7EF; border-color:#d7f0e0; color:#0e7a3a; }
362
- .chip-neg{ background:#FBEAEA; border-color:#F3DADA; color:#B91C1C; }
363
 
364
  /* ===== Integration Guide ===== */
365
  .integration-guide{ margin-top:12px; padding:16px; }
@@ -368,21 +497,10 @@ export default {
368
  .code-block{ background:#0b1020; color:#E6EDF3; border-radius:10px; padding:12px; overflow:auto; font-size:12.5px; line-height:1.5; }
369
  .note{ color:#334155; margin-top:6px; }
370
 
371
- /* ===== Form (info-only) ===== */
372
- .form-container{ margin-top:12px; padding:16px; }
373
- .form-title{ font-weight:800; margin-bottom:8px; color:#0f172a; }
374
- .field{ display:flex; flex-direction:column; gap:6px; margin:10px 0; }
375
- .field input, .field textarea{
376
- border:1px solid #E7ECF3; border-radius:10px; padding:10px 12px; font-size:14px; width:100%;
377
- }
378
- .field input:focus, .field textarea:focus{ outline:none; border-color:#C7D2FE; box-shadow:0 0 0 3px rgba(59,130,246,.15); }
379
- .btn-cta{ background:linear-gradient(90deg, rgb(0,0,185), #7b2cbf, #d946ef, rgb(240,0,15)); border:none; color:#fff; font-weight:800; letter-spacing:.02em; padding:10px 16px; border-radius:12px; cursor:pointer; }
380
- .btn-cta:disabled{ opacity:.5; cursor:not-allowed; }
381
- .form-message{ margin-top:10px; padding:8px 10px; border-radius:10px; font-size:14px; }
382
- .form-message.is-success{ background:#E9F7EF; color:#0e7a3a; border:1px solid #d7f0e0; }
383
-
384
  /* ===== Buttons ===== */
385
  .cta-row{ margin-top:12px; display:flex; justify-content:center; }
386
  .link-btn{ display:inline-flex; align-items:center; justify-content:center; gap:.5rem; text-decoration:none; }
 
387
  .mr-2{ margin-right:.5rem; }
 
388
  </style>
 
1
  <template>
2
  <div class="arena-page">
 
3
  <!-- ============== ASSETS ============== -->
4
  <section class="section">
5
  <header class="section-head">
6
  <h2 class="section-title">
7
  <span class="ama-gradient">Assets in the Arena</span>
8
  </h2>
9
+ <p class="section-sub">Live from dataService B&amp;H sparkline (ECharts LineChart) 1M changecoverage</p>
10
  </header>
11
 
12
  <div class="grid grid-assets-4">
 
28
  </div>
29
 
30
  <div class="asset-stats">
31
+ <div class="stat">
32
+ <div class="stat-label">1M</div>
33
+ <div class="stat-value mono" :class="a.change1m >= 0 ? 'pos' : 'neg'">
34
+ {{ signedPct(a.change1m) }}
35
+ </div>
36
+ </div>
37
+ <div class="stat">
38
+ <div class="stat-label">Runs</div>
39
+ <div class="stat-value mono">{{ a.runs }}</div>
40
+ </div>
41
+ <div class="stat">
42
+ <div class="stat-label">Agents</div>
43
+ <div class="stat-value mono">{{ a.agents }}</div>
44
+ </div>
45
+ <div class="stat">
46
+ <div class="stat-label">Days</div>
47
+ <div class="stat-value mono">{{ a.days }}</div>
48
  </div>
49
  </div>
50
+
51
+ <p class="asset-desc muted clamp-2" :title="a.desc">{{ a.desc }}</p>
52
+ </div>
53
+
54
+ <div class="asset-foot">
55
+ <div class="asset-bar" :style="{ '--pct': a.coveragePct + '%' }"></div>
56
+ <div class="asset-foot-row">
57
+ <span class="muted">Coverage</span>
58
+ <span class="mono">{{ Math.round(a.coveragePct) }}%</span>
59
+ </div>
60
+ <div class="asset-foot-row">
61
+ <span class="muted">EOD</span>
62
+ <span class="mono">{{ a.eod || '—' }}</span>
63
+ </div>
64
+ </div>
65
+ </article>
66
+ </div>
67
+
68
+ <div class="cta-row">
69
+ <a :href="assetFormUrl" target="_blank" rel="noopener" class="btn-cta link-btn">
70
+ <i class="pi pi-plus mr-2"></i> Vote to Add Asset
71
+ </a>
72
+ </div>
73
+ </section>
74
+
75
+ <!-- ============== AGENTS (no performance) ============== -->
76
+ <section class="section">
77
+ <header class="section-head">
78
+ <h2 class="section-title">
79
+ <span class="ama-gradient">Agents in the Arena</span>
80
+ </h2>
81
+ <p class="section-sub">Tournament cards • GitHub &amp; arXiv links (no performance)</p>
82
+ </header>
83
+
84
+ <div class="grid grid-agents">
85
+ <article v-for="g in agents" :key="g.name" class="card agent-card">
86
+ <div class="agent-top">
87
+ <div class="agent-badge" :style="{ borderColor: g.color }">
88
+ <div class="agent-ring" :style="{ background: g.color }"></div>
89
+ <img :src="g.logo" :alt="g.name" class="agent-logo" />
90
  </div>
91
+ <div class="agent-title">
92
+ <div class="agent-name truncate" :title="g.name">{{ g.name }}</div>
93
+ <div class="agent-links">
94
+ <a :href="g.github" target="_blank" rel="noopener" class="link">GitHub</a>
95
+ <span class="dot">•</span>
96
+ <a :href="g.arxiv" target="_blank" rel="noopener" class="link">arXiv</a>
97
+ </div>
98
  </div>
99
  </div>
100
 
 
105
  </article>
106
  </div>
107
 
108
+ <!-- Info-only integration guide -->
109
  <div class="card integration-guide">
110
  <h3>Agent Integration Guide</h3>
111
 
 
116
 
117
  <div class="guide-section">
118
  <h4>Input (What we send to your agent)</h4>
119
+ <pre class="code-block">{
120
  "date": "2025-10-24",
121
  "price": {"BTC": 67890.50},
122
  "news": {"BTC": "Bitcoin news..."},
 
139
 
140
  <div class="guide-section">
141
  <h4>Output (What we expect from your agent)</h4>
142
+ <pre class="code-block">{
143
  "recommended_action": "BUY"
144
  }</pre>
145
  <p class="note"><strong>Valid actions:</strong> <code>"BUY"</code>, <code>"SELL"</code>, or <code>"HOLD"</code> (uppercase)</p>
146
  </div>
147
  </div>
148
 
149
+ <!-- Submit Agent button (Google Form link) -->
150
+ <div class="cta-row">
151
+ <a :href="agentFormUrl" target="_blank" rel="noopener" class="btn-cta link-btn">
152
+ <i class="pi pi-send mr-2"></i> Submit Agent (Google Form)
153
+ </a>
154
+ </div>
155
  </section>
 
156
  </div>
157
  </template>
158
 
159
  <script>
160
+ import { dataService } from '../lib/dataService'
161
+ import { getAllDecisions } from '../lib/dataCache'
162
+ import { readAllRawDecisions } from '../lib/idb'
163
+ import { filterRowsToNyseTradingDays } from '../lib/marketCalendar'
164
+ import { computeBuyHoldEquity } from '../lib/perf'
165
+
166
+ /* ---- ECharts (LineChart) ---- */
167
  import * as echarts from 'echarts/core'
168
  import { LineChart } from 'echarts/charts'
169
  import { GridComponent, TooltipComponent } from 'echarts/components'
170
  import { CanvasRenderer } from 'echarts/renderers'
171
  echarts.use([LineChart, GridComponent, TooltipComponent, CanvasRenderer])
172
 
173
+ /**
174
+ * Minimal ECharts line spark (B&H equity)
175
+ */
176
  const MiniEchart = {
177
  name: 'MiniEchart',
178
  props: {
 
182
  mounted() {
183
  const el = this.$refs.c
184
  this._chart = echarts.init(el)
185
+ this.render(this.data)
186
+ },
187
+ methods: {
188
+ render(arr) {
189
+ const d = Array.isArray(arr) ? arr : []
190
+ this._chart.setOption({
191
+ animation: true,
192
+ grid: { left: 0, right: 0, top: 0, bottom: 0 },
193
+ xAxis: { type: 'category', show: false, boundaryGap: false, data: d.map((_, i) => i) },
194
+ yAxis: { type: 'value', show: false, scale: true },
195
+ tooltip: { show: false },
196
+ color: [this.color || '#22c55e'],
197
+ series: [{
198
+ type: 'line',
199
+ data: d,
200
+ showSymbol: false,
201
+ smooth: 0.35,
202
+ lineStyle: { width: 2 },
203
+ areaStyle: { opacity: 0.15 }
204
+ }]
205
+ })
206
+ }
207
  },
208
  watch: {
209
  data(newVal) {
210
  if (!this._chart) return
211
  const d = Array.isArray(newVal) ? newVal : []
212
+ this._chart.setOption({
213
+ xAxis: { data: d.map((_, i) => i) },
214
+ series: [{ data: d }]
215
+ })
216
  }
217
  },
218
  beforeUnmount() { if (this._chart) { this._chart.dispose(); this._chart = null } },
 
225
  data() {
226
  return {
227
  assetFormUrl: 'https://forms.gle/your-asset-request-form',
 
228
  agentFormUrl: 'https://forms.gle/your-agent-submit-form',
229
 
230
+ // dynamic, filled from dataService
231
+ assets: [],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
+ // agents section (no performance)
234
  agents: [
235
  {
236
  name: 'InvestorAgent',
237
  github: 'https://github.com/your-org/investor-agent',
238
  arxiv: 'https://arxiv.org/abs/xxxyyy',
239
+ strategy: 'Aggressive Long Only',
240
+ focus: 'BTC, MSFT',
241
  color: 'linear-gradient(90deg,#ffd700,#eab308)',
242
  logo: new URL('../assets/images/agents_images/investor.png', import.meta.url).href
243
  },
 
245
  name: 'TradeAgent',
246
  github: 'https://github.com/your-org/trade-agent',
247
  arxiv: 'https://arxiv.org/abs/aaabbb',
248
+ strategy: 'Aggressive Long Only',
249
+ focus: 'BTC, ETH',
250
  color: 'linear-gradient(90deg,#4338ca,#6d28d9)',
251
  logo: new URL('../assets/images/agents_images/tradeagent.png', import.meta.url).href
252
  },
 
254
  name: 'DeepFundAgent',
255
  github: 'https://github.com/your-org/deepfund-agent',
256
  arxiv: 'https://arxiv.org/abs/cccdde',
257
+ strategy: 'Aggressive Long Only',
258
+ focus: '—',
259
  color: 'linear-gradient(90deg,#0ea5e9,#22d3ee)',
260
  logo: new URL('../assets/images/agents_images/deepfund.png', import.meta.url).href
261
  },
 
263
  name: 'HedgeFundAgent',
264
  github: 'https://github.com/your-org/hedgefund-agent',
265
  arxiv: 'https://arxiv.org/abs/fffggg',
266
+ strategy: 'Aggressive Long Only',
267
+ focus: 'BMRN, TSLA',
268
  color: 'linear-gradient(90deg,#f43f5e,#ef4444)',
269
  logo: new URL('../assets/images/agents_images/hedgefund.png', import.meta.url).href
270
  }
271
+ ],
272
+
273
+ // live state
274
+ rowsRef: [],
275
+ allDecisions: [],
276
+ unsubscribe: null,
277
+
278
+ ASSET_ICONS: {
279
+ BTC: new URL('../assets/images/assets_images/BTC.png', import.meta.url).href,
280
+ ETH: new URL('../assets/images/assets_images/ETH.png', import.meta.url).href,
281
+ MSFT: new URL('../assets/images/assets_images/MSFT.png', import.meta.url).href,
282
+ BMRN: new URL('../assets/images/assets_images/BMRN.png', import.meta.url).href,
283
+ TSLA: new URL('../assets/images/assets_images/TSLA.png', import.meta.url).href
284
+ }
285
+ }
286
+ },
287
+
288
+ mounted() {
289
+ this.unsubscribe = dataService.subscribe((state) => {
290
+ this.rowsRef = Array.isArray(state.tableRows) ? state.tableRows : []
291
+ this.rebuildAssets().catch(() => {})
292
+ })
293
+ this.rowsRef = Array.isArray(dataService.tableRows) ? dataService.tableRows : []
294
+ if (!dataService.loaded && !dataService.loading) {
295
+ dataService.load(false).catch(e => console.error('RequestView: load failed', e))
296
  }
297
+
298
+ // warm decision cache
299
+ this.allDecisions = getAllDecisions() || []
300
+ if (!this.allDecisions.length) {
301
+ readAllRawDecisions().then(cached => { if (cached?.length) this.allDecisions = cached })
302
+ }
303
+ this.rebuildAssets().catch(() => {})
304
+ },
305
+
306
+ beforeUnmount() {
307
+ if (this.unsubscribe) { this.unsubscribe(); this.unsubscribe = null }
308
  },
309
+
310
  methods: {
 
 
 
 
 
 
 
311
  signedPct(p) {
312
  const v = Number(p || 0) * 100
313
  const s = v >= 0 ? '+' : '−'
314
  return `${s}${Math.abs(v).toFixed(2)}%`
315
+ },
316
+ fmtUSD(n) {
317
+ return (n ?? 0).toLocaleString(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 2 })
318
+ },
319
+
320
+ /* ===== Dynamic assets built from B&H equity ===== */
321
+ async buildAssetSeq(assetCode) {
322
+ let seq = (this.allDecisions || []).filter(r => r.asset === assetCode)
323
+ seq.sort((a,b) => (a.date > b.date ? 1 : -1))
324
+ const isCrypto = assetCode === 'BTC' || assetCode === 'ETH'
325
+ if (!isCrypto) {
326
+ try { seq = await filterRowsToNyseTradingDays(seq) } catch {}
327
+ }
328
+ return seq
329
+ },
330
+ sampleSeries(values, n = 12) {
331
+ if (!Array.isArray(values) || !values.length) return []
332
+ if (values.length <= n) return values.slice(-n)
333
+ const step = (values.length - 1) / (n - 1)
334
+ const out = []
335
+ for (let i = 0; i < n; i++) out.push(values[Math.round(i * step)])
336
+ return out
337
+ },
338
+ pctChange(a, b) {
339
+ if (a == null || b == null || a === 0) return 0
340
+ return (b / a) - 1
341
+ },
342
+
343
+ async rebuildAssets() {
344
+ const assetsInRows = Array.from(new Set((this.rowsRef || []).map(r => r.asset))).filter(Boolean)
345
+ if (!assetsInRows.length) { this.assets = []; return }
346
+
347
+ const cards = []
348
+ for (const code of assetsInRows) {
349
+ const seq = await this.buildAssetSeq(code)
350
+ if (!seq.length) continue
351
+
352
+ // B&H equity as normalized series for spark and change
353
+ let bh = []
354
+ try { bh = computeBuyHoldEquity(seq, 100000) || [] } catch { bh = [] }
355
+ const lastIdx = bh.length - 1
356
+ if (lastIdx < 0) continue
357
+
358
+ const lastDate = seq[lastIdx]?.date || null
359
+ const spark = this.sampleSeries(bh.map(v => v), 12)
360
+ const change1m = this.pctChange(spark[0], spark[spark.length - 1])
361
+
362
+ // trading day span
363
+ let days = 0
364
+ try {
365
+ const start = new Date(seq[0].date)
366
+ const end = new Date(seq[lastIdx].date)
367
+ days = Math.max(1, Math.round((end - start) / 86400000) + 1)
368
+ } catch {}
369
+
370
+ // runs & agents from rows table
371
+ const rowsForAsset = (this.rowsRef || []).filter(r => r.asset === code)
372
+ const runs = rowsForAsset.length
373
+ const agents = new Set(rowsForAsset.map(r => r.agent_name)).size
374
+
375
+ // coverage ~ unique dates present / span
376
+ const uniqueDates = new Set(seq.map(r => r.date)).size
377
+ const coveragePct = Math.max(0, Math.min(100, Math.round((uniqueDates / Math.max(1, days)) * 100)))
378
+
379
+ cards.push({
380
+ code,
381
+ name: code,
382
+ type: (code === 'BTC' || code === 'ETH') ? 'Crypto' : 'Stock',
383
+ icon: this.ASSET_ICONS[code] || null,
384
+ month: spark,
385
+ sparkColor: (code === 'BTC') ? '#f59e0b' : (code === 'ETH' ? '#6366f1' : '#22c55e'),
386
+ change1m,
387
+ runs, agents, days,
388
+ eod: lastDate,
389
+ coveragePct,
390
+ desc: (code === 'BTC') ? 'Blue-chip crypto asset; 24/7 trading.'
391
+ : (code === 'ETH') ? 'Smart-contract platform; high on-chain activity.'
392
+ : 'Equity; regular trading days.'
393
+ })
394
+ }
395
+
396
+ // Stable display order
397
+ const order = ['BTC','ETH','MSFT','BMRN','TSLA']
398
+ cards.sort((a,b) => (order.indexOf(a.code) - order.indexOf(b.code)))
399
+ this.assets = cards
400
+ }
401
+ }
402
  }
403
  </script>
404
 
405
  <style scoped>
406
  /* ===== AMA Core ===== */
 
407
  .ama-gradient{
408
  background: linear-gradient(90deg, rgb(0,0,185), #7b2cbf, #d946ef, rgb(240,0,15));
409
  -webkit-background-clip: text; background-clip: text; color: transparent;
 
411
 
412
  .arena-page{
413
  max-width:1280px; margin:0 auto;
414
+ padding:16px 20px 120px; background:#fff;
 
415
  }
416
 
417
  /* ===== Helpers ===== */
418
  .truncate{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
419
  .clamp-2{ display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
420
  .w-full{ width:100%; }
421
+ .echart-mini{ width:100%; height:90px; }
422
 
423
  /* ===== Sections ===== */
424
  .section{ margin-top:18px; }
 
426
  .section-title{ font-size:clamp(1.2rem, 1.2rem + 0.4vw, 1.6rem); font-weight:800; letter-spacing:-0.02em; display:inline-block; }
427
  .section-sub{ margin-top:4px; color:#6b7280; }
428
 
429
+ /* ===== Grids ===== */
430
  .grid{ display:grid; gap:14px; min-width:0; }
431
  .grid-assets-4{ grid-template-columns: repeat(4, minmax(0,1fr)); }
432
  .grid-agents{ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
 
433
  @media (max-width: 1200px){ .grid-assets-4{ grid-template-columns: repeat(3, minmax(0,1fr)); } }
434
  @media (max-width: 900px){ .grid-assets-4{ grid-template-columns: repeat(2, minmax(0,1fr)); } }
435
  @media (max-width: 620px){ .grid-assets-4{ grid-template-columns: 1fr; } }
436
 
437
  /* ===== Card base ===== */
438
  .card{
439
+ background:#ffffff; border:1px solid #E7ECF3; border-radius:14px;
440
  box-shadow:0 1px 2px rgba(16,24,40,.03), 0 4px 12px rgba(16,24,40,.04);
441
  transition: transform .18s ease, box-shadow .2s ease, border-color .2s ease;
442
+ min-width:0;
443
  }
444
  .card:hover{ transform:translateY(-2px); box-shadow:0 10px 26px rgba(16,24,40,.08); border-color:#D9E2EF; }
445
 
446
  /* ===== Assets ===== */
447
+ .asset-card{ padding:12px; display:flex; flex-direction:column; gap:10px; }
448
+ .asset-head{ display:grid; grid-template-columns:44px 1fr auto; gap:10px; align-items:center; min-width:0; }
 
 
449
  .asset-logo-wrap{ width:44px; height:44px; border-radius:12px; background:#f3f4f6; display:grid; place-items:center; border:1px solid #E7ECF3; overflow:hidden; }
450
  .asset-logo{ width:70%; height:70%; object-fit:contain; }
451
  .asset-name{ min-width:0; }
 
453
  .asset-full{ font-size:12px; color:#64748b; }
454
  .type-badge{
455
  border-radius:999px; padding:4px 8px; font-size:12px; font-weight:700;
456
+ border:1px solid #E7ECF3; background:#F6F8FB; color:#0f172a; white-space:nowrap;
457
  }
458
  .type-crypto{ background:#f8fbff; border-color:#e0e7ff; color:#1e3a8a; }
459
  .type-stock { background:#f6fffb; border-color:#d9fbe6; color:#064e3b; }
 
460
 
461
+ .asset-body{ display:flex; flex-direction:column; gap:10px; }
462
  .spark-wrap{ width:100%; height:90px; overflow:hidden; border-radius:8px; }
 
 
 
 
 
463
  .asset-stats{ display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); gap:10px; }
464
  .stat{ background:#F6F8FB; border:1px solid #E7ECF3; border-radius:10px; padding:8px; min-width:0; }
465
  .stat-label{ font-size:11px; color:#6b7280; margin-bottom:2px; }
 
474
  .asset-bar::after{ content:''; position:absolute; left:0; top:0; height:100%; width:var(--pct,0%); background:linear-gradient(90deg,#16a34a,#22c55e); }
475
  .asset-foot-row{ margin-top:6px; display:flex; align-items:center; justify-content:space-between; color:#6b7280; }
476
 
477
+ /* ===== Agents (perf-free) ===== */
478
+ .agent-card{ padding:12px; display:flex; flex-direction:column; gap:8px; }
479
+ .agent-top{ display:grid; grid-template-columns:52px 1fr; gap:10px; align-items:center; min-width:0; }
 
 
480
  .agent-badge{ width:52px; height:52px; border-radius:16px; border:2px solid #e5e7eb; position:relative; display:grid; place-items:center; overflow:hidden; }
481
  .agent-ring{ position:absolute; inset:0; opacity:.18; }
482
  .agent-logo{ width:70%; height:70%; object-fit:contain; position:relative; z-index:1; }
 
483
  .agent-title{ min-width:0; }
484
  .agent-name{ font-weight:900; letter-spacing:.02em; color:#0f172a; text-transform:uppercase; }
485
  .agent-links{ display:flex; align-items:center; gap:8px; color:#64748b; flex-wrap:wrap; min-width:0; }
486
  .link{ color:#334155; font-weight:600; text-decoration:none; }
487
  .link:hover{ color:#0f172a; text-decoration:underline; }
488
+ .dot{ color:#94a3b8; }
 
489
  .agent-foot{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:2px; }
490
  .chip{ display:inline-flex; align-items:center; gap:6px; padding:4px 8px; border-radius:999px; font-size:12px; font-weight:700; background:#F6F8FB; color:#0f172a; border:1px solid #E7ECF3; white-space:nowrap; }
491
  .chip-outline{ background:#fff; }
 
 
492
 
493
  /* ===== Integration Guide ===== */
494
  .integration-guide{ margin-top:12px; padding:16px; }
 
497
  .code-block{ background:#0b1020; color:#E6EDF3; border-radius:10px; padding:12px; overflow:auto; font-size:12.5px; line-height:1.5; }
498
  .note{ color:#334155; margin-top:6px; }
499
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500
  /* ===== Buttons ===== */
501
  .cta-row{ margin-top:12px; display:flex; justify-content:center; }
502
  .link-btn{ display:inline-flex; align-items:center; justify-content:center; gap:.5rem; text-decoration:none; }
503
+ .btn-cta{ background:linear-gradient(90deg, rgb(0,0,185), #7b2cbf, #d946ef, rgb(240,0,15)); border:none; color:#fff; font-weight:800; letter-spacing:.02em; padding:10px 16px; border-radius:12px; cursor:pointer; }
504
  .mr-2{ margin-right:.5rem; }
505
+ .pos{ color:#0e7a3a; } .neg{ color:#B91C1C; }
506
  </style>