Jimin Huang commited on
Commit
b440fb9
·
1 Parent(s): 34afdb3

Change settings

Browse files
src/assets/images/agents_images/hedgefund.png ADDED

Git LFS Details

  • SHA256: a5f9e89b4b86cc7183c381c34749d4ca867510b705b3347c7763313b0a76f4e4
  • Pointer size: 129 Bytes
  • Size of remote file: 5.27 kB
src/assets/images/models_images/claude.png ADDED

Git LFS Details

  • SHA256: 2cb80401ba514060120928df9000c758402dabc131c4ebbe8d08c72a655e5c4b
  • Pointer size: 130 Bytes
  • Size of remote file: 58.2 kB
src/assets/images/models_images/gemini.png ADDED

Git LFS Details

  • SHA256: 9427daecfd682e90fae4ebad8793d24beefa53cca08516df32135f0139ddc619
  • Pointer size: 130 Bytes
  • Size of remote file: 20.8 kB
src/assets/images/models_images/gpt.png ADDED

Git LFS Details

  • SHA256: b7dcf6e78404ff3b14a3a8a3511288f0381a854e0c7be4a342f5fcd01f742cbc
  • Pointer size: 130 Bytes
  • Size of remote file: 28.2 kB
src/components/CompareChartE.vue CHANGED
@@ -34,6 +34,111 @@ function toPct(points){
34
  return points.map(([t, y]) => [t, ((y / y0) - 1) * 100])
35
  }
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  export default defineComponent({
38
  name: 'CompareChartE',
39
  components: { VChart },
@@ -117,18 +222,34 @@ export default defineComponent({
117
  lineStyle: { width: 2, color: getStrategyColor(sel.strategy || '', false, idx) },
118
  data: points
119
  })
120
- const last = points[points.length - 1]
121
  if (last && Number.isFinite(last[1])) {
 
 
122
  series.push({
123
- name: name + ' •',
124
  type: 'scatter',
125
  data: [ last ],
126
- symbol: 'circle',
127
- symbolSize: 8,
128
- itemStyle: { color: getStrategyColor(sel.strategy || '', false, idx) },
129
- tooltip: { show: false }, // avoid duplicate tooltip
130
- z: 10
131
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  }
133
  }
134
 
@@ -152,16 +273,32 @@ export default defineComponent({
152
  })
153
  const lastBH = bhPoints[bhPoints.length - 1]
154
  if (lastBH && Number.isFinite(lastBH[1])) {
 
 
155
  series.push({
156
- name: `${asset} · Buy&Hold •`,
157
  type: 'scatter',
158
  data: [ lastBH ],
159
- symbol: 'circle',
160
- symbolSize: 8,
161
- itemStyle: { color: getStrategyColor('', true, 0) },
162
  tooltip: { show: false },
163
- z: 9
164
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  }
166
  legend.push(`${asset} · Buy&Hold`)
167
  }
 
34
  return points.map(([t, y]) => [t, ((y / y0) - 1) * 100])
35
  }
36
 
37
+ // --- Agent & Model logo registries (fill with your actual assets)
38
+ const AGENT_LOGOS = {
39
+ 'HedgeFundAgent': new URL('../assets/images/agents_images/hedgefund.png', import.meta.url).href,
40
+ 'DeepFundAgent': new URL('../assets/images/agents_images/deepfund.png', import.meta.url).href,
41
+ 'TradeAgent': new URL('../assets/images/agents_images/trade.png', import.meta.url).href,
42
+ 'InvestorAgent': new URL('../assets/images/agents_images/investor.png', import.meta.url).href,
43
+ // 'InvestorAgent': new URL('../assets/images/agents_images/investor.png', import.meta.url).href,
44
+ };
45
+ const MODEL_LOGOS = {
46
+ 'claude_3_5_haiku_20241022': new URL('../assets/images/models_images/claude.png', import.meta.url).href,
47
+ 'claude_sonnet_4_2025051': new URL('../assets/images/models_images/claude.png', import.meta.url).href,
48
+ 'gpt_4o': new URL('../assets/images/models_images/gpt.png', import.meta.url).href,
49
+ 'gpt_4.1': new URL('../assets/images/models_images/gpt.png', import.meta.url).href,
50
+ 'gemini_2.0_flash': new URL('../assets/images/models_images/gemini.png', import.meta.url).href,
51
+ };
52
+
53
+ // Canvas badge cache: key = `${agent}|${model}|${color}`
54
+ const BADGE_CACHE = new Map();
55
+
56
+ const loadImg = (url) => new Promise((resolve, reject) => {
57
+ if (!url) return resolve(null);
58
+ const img = new Image();
59
+ img.crossOrigin = 'anonymous';
60
+ img.onload = () => resolve(img);
61
+ img.onerror = () => resolve(null); // fail soft
62
+ img.src = url;
63
+ });
64
+
65
+ // Compose a badge: [ colored circle with agent logo ] + [ white rounded square with model logo ]
66
+ async function composeBadge(agent, model, color = '#666') {
67
+ const key = `${agent}|${model ?? ''}|${color}`;
68
+ if (BADGE_CACHE.has(key)) return BADGE_CACHE.get(key);
69
+
70
+ const aUrl = AGENT_LOGOS[agent];
71
+ const mUrl = MODEL_LOGOS[model];
72
+ if (!aUrl && !mUrl) return null; // fall back to dot later
73
+
74
+ // layout
75
+ const H = 26; // badge height
76
+ const circleD = 22; // agent chip diameter
77
+ const pad = 2; // internal spacing
78
+ const square = 20; // model chip size
79
+ const gap = 4; // space between chips
80
+ const W = aUrl && mUrl ? circleD + gap + square : (aUrl ? circleD : square);
81
+
82
+ const canvas = document.createElement('canvas');
83
+ canvas.width = W + pad * 2;
84
+ canvas.height = H + pad * 2;
85
+ const ctx = canvas.getContext('2d');
86
+
87
+ // background is transparent; draw chips
88
+ let x = pad, y = pad + (H - circleD) / 2;
89
+
90
+ // agent circle
91
+ if (aUrl) {
92
+ ctx.fillStyle = color;
93
+ ctx.beginPath();
94
+ ctx.arc(x + circleD / 2, y + circleD / 2, circleD / 2, 0, Math.PI * 2);
95
+ ctx.fill();
96
+ const aImg = await loadImg(aUrl);
97
+ if (aImg) {
98
+ const inset = 4;
99
+ ctx.save();
100
+ ctx.beginPath();
101
+ ctx.arc(x + circleD / 2, y + circleD / 2, circleD / 2 - 0.5, 0, Math.PI * 2);
102
+ ctx.clip();
103
+ const iw = circleD - inset * 2, ih = circleD - inset * 2;
104
+ ctx.globalAlpha = 0.95;
105
+ ctx.drawImage(aImg, x + inset, y + inset, iw, ih);
106
+ ctx.restore();
107
+ }
108
+ x += circleD + (mUrl ? gap : 0);
109
+ }
110
+
111
+ // model rounded square
112
+ if (mUrl) {
113
+ const r = 6;
114
+ y = pad + (H - square) / 2;
115
+ ctx.fillStyle = '#ffffff';
116
+ ctx.beginPath();
117
+ ctx.moveTo(x + r, y);
118
+ ctx.lineTo(x + square - r, y);
119
+ ctx.quadraticCurveTo(x + square, y, x + square, y + r);
120
+ ctx.lineTo(x + square, y + square - r);
121
+ ctx.quadraticCurveTo(x + square, y + square, x + square - r, y + square);
122
+ ctx.lineTo(x + r, y + square);
123
+ ctx.quadraticCurveTo(x, y + square, x, y + square - r);
124
+ ctx.lineTo(x, y + r);
125
+ ctx.quadraticCurveTo(x, y, x + r, y);
126
+ ctx.fill();
127
+
128
+ const mImg = await loadImg(mUrl);
129
+ if (mImg) {
130
+ const inset = 4;
131
+ const iw = square - inset * 2, ih = square - inset * 2;
132
+ ctx.globalAlpha = 0.96;
133
+ ctx.drawImage(mImg, x + inset, y + inset, iw, ih);
134
+ }
135
+ }
136
+
137
+ const url = canvas.toDataURL('image/png');
138
+ BADGE_CACHE.set(key, url);
139
+ return url;
140
+ }
141
+
142
  export default defineComponent({
143
  name: 'CompareChartE',
144
  components: { VChart },
 
222
  lineStyle: { width: 2, color: getStrategyColor(sel.strategy || '', false, idx) },
223
  data: points
224
  })
225
+ const last = points?.[points.length - 1];
226
  if (last && Number.isFinite(last[1])) {
227
+ const lineColor = getStrategyColor(sel.strategy || '', false, idx);
228
+ const badgeUrl = await composeBadge(agent, sel.model, lineColor); // ← NEW
229
  series.push({
230
+ name: name + ' •badge',
231
  type: 'scatter',
232
  data: [ last ],
233
+ symbol: badgeUrl ? `image://${badgeUrl}` : 'circle',
234
+ symbolSize: badgeUrl ? 26 : 8,
235
+ z: 20,
236
+ tooltip: { show: false },
237
+ label: {
238
+ show: true,
239
+ position: 'right',
240
+ padding: [4,8],
241
+ borderRadius: 10,
242
+ backgroundColor: lineColor,
243
+ color: '#fff',
244
+ fontWeight: 700,
245
+ formatter: (p) => {
246
+ const v = p.value?.[1];
247
+ if (this.mode === 'pct') return (v >= 0 ? '+' : '') + (Number(v).toFixed(2)) + '%';
248
+ return Number(v ?? 0).toLocaleString(undefined, { style:'currency', currency:'USD', maximumFractionDigits: 2 });
249
+ }
250
+ },
251
+ itemStyle: { color: lineColor }
252
+ });
253
  }
254
  }
255
 
 
273
  })
274
  const lastBH = bhPoints[bhPoints.length - 1]
275
  if (lastBH && Number.isFinite(lastBH[1])) {
276
+ const baseColor = getStrategyColor('', true, 0);
277
+ const badgeUrl = new URL(`../assets/images/assets_images/${asset}.png`, import.meta.url).href;
278
  series.push({
279
+ name: `${asset} · Buy&Hold •badge`,
280
  type: 'scatter',
281
  data: [ lastBH ],
282
+ symbol: badgeUrl ? `image://${badgeUrl}` : 'circle',
283
+ symbolSize: badgeUrl ? 26 : 8,
284
+ z: 19,
285
  tooltip: { show: false },
286
+ label: {
287
+ show: true,
288
+ position: 'right',
289
+ padding: [4,8],
290
+ borderRadius: 10,
291
+ backgroundColor: baseColor,
292
+ color: '#fff',
293
+ fontWeight: 700,
294
+ formatter: (p) => {
295
+ const v = p.value?.[1];
296
+ if (this.mode === 'pct') return (v >= 0 ? '+' : '') + (Number(v).toFixed(2)) + '%';
297
+ return Number(v ?? 0).toLocaleString(undefined, { style:'currency', currency:'USD', maximumFractionDigits: 2 });
298
+ }
299
+ },
300
+ itemStyle: { color: baseColor }
301
+ });
302
  }
303
  legend.push(`${asset} · Buy&Hold`)
304
  }