ZeroHedgeK commited on
Commit
397d289
·
verified ·
1 Parent(s): a65eb8f

Completely revamp the design

Browse files
Files changed (3) hide show
  1. components/bot-card.js +79 -58
  2. index.html +253 -174
  3. script.js +83 -59
components/bot-card.js CHANGED
@@ -1,4 +1,5 @@
1
- // Bot Card Web Component
 
2
  class BotCard extends HTMLElement {
3
  constructor() {
4
  super();
@@ -86,24 +87,25 @@ class BotCard extends HTMLElement {
86
 
87
  // Quotes
88
  quotes.innerHTML = `
89
- <div class="grid grid-cols-3 gap-2 text-xs">
90
- <div class="text-left text-gray-500">Bid</div>
91
- <div class="text-center font-semibold">${this._quotes.bid}</div>
92
- <div class="text-right"></div>
93
-
94
- <div class="text-left text-gray-500">Ask</div>
95
- <div class="text-center font-semibold">${this._quotes.ask}</div>
96
- <div class="text-right"></div>
97
-
98
- <div class="text-left text-gray-500">Spread</div>
99
- <div class="text-center font-semibold">${this._quotes.spread}</div>
100
- <div class="text-right"></div>
 
101
  </div>
102
  `;
103
 
104
  // Metrics
105
  const metricHtml = Array.from(this._metrics.entries())
106
- .map(([k, v]) => `<div class="text-xs text-gray-500">${k}: <span class="font-semibold text-gray-800">${v}</span></div>`)
107
  .join("");
108
  metrics.innerHTML = metricHtml;
109
 
@@ -114,7 +116,7 @@ class BotCard extends HTMLElement {
114
  }
115
 
116
  // Logs
117
- logs.innerHTML = this._logs.map((l) => `<div class="text-[11px] text-gray-600 truncate">${l}</div>`).join("");
118
  // Re-apply feather icons if needed
119
  if (typeof feather !== "undefined") feather.replace();
120
  }
@@ -122,30 +124,36 @@ class BotCard extends HTMLElement {
122
  render() {
123
  if (!this._bot) return;
124
  const colorMap = {
125
- green: "bg-green-500",
126
- blue: "bg-blue-500",
127
- purple: "bg-purple-500",
128
- amber: "bg-amber-500",
129
  };
130
- const color = colorMap[this._bot.color] || "bg-green-500";
131
  const paused = this._bot.paused;
132
 
133
  this.shadowRoot.innerHTML = `
134
  <style>
135
  :host { display: block; }
136
  .card {
137
- border: 1px solid #e5e7eb;
138
- border-radius: 0.5rem;
 
 
139
  overflow: hidden;
140
- background: #ffffff;
 
 
 
 
141
  }
142
  .header {
143
  display: flex;
144
  align-items: center;
145
  justify-content: space-between;
146
  gap: 8px;
147
- padding: 10px 12px;
148
- border-bottom: 1px solid #f3f4f6;
149
  }
150
  .left {
151
  display: flex;
@@ -153,82 +161,95 @@ class BotCard extends HTMLElement {
153
  gap: 8px;
154
  }
155
  .dot {
156
- width: 10px;
157
- height: 10px;
158
  border-radius: 50%;
 
159
  }
160
  .body {
161
- padding: 10px 12px;
162
  display: grid;
163
  grid-template-columns: 1fr;
164
- gap: 10px;
165
  }
166
- .row {
167
- display: flex;
168
- align-items: center;
169
- justify-content: space-between;
170
- gap: 10px;
171
  }
172
  .meta {
173
- font-size: 12px;
174
- color: #6b7280;
175
  }
176
  button {
177
- border: 1px solid #e5e7eb;
178
- background: #ffffff;
179
- border-radius: 0.375rem;
 
180
  padding: 4px 8px;
181
- font-size: 12px;
182
  cursor: pointer;
183
- transition: background 0.2s ease;
 
 
 
184
  }
185
  button:hover {
186
- background: #f9fafb;
 
187
  }
188
  #progress {
189
- height: 6px;
190
- background: #f3f4f6;
191
- border-radius: 9999px;
192
  overflow: hidden;
193
  }
194
  #progressFill {
195
  height: 100%;
196
- background: #22c55e;
197
  width: 0%;
 
198
  }
199
  #logs {
200
- background: #fafafa;
201
- border: 1px dashed #e5e7eb;
202
  padding: 8px;
203
  border-radius: 6px;
204
- max-height: 100px;
205
  overflow: hidden;
206
  }
207
  </style>
208
- <div class="card">
209
  <div class="header">
210
  <div class="left">
211
  <div class="dot ${color}"></div>
212
  <div>
213
- <div class="font-semibold text-sm">${this._bot.name}</div>
214
  <div class="meta">${this._bot.type} • ${this._bot.symbol}</div>
215
  </div>
216
  </div>
217
- <div class="row">
218
- <span id="pausedBadge" class="text-[10px] px-2 py-0.5 rounded-full bg-red-50 text-red-600 border border-red-200 ${paused ? "" : "hidden"}">PAUSED</span>
219
  <button id="pauseBtn" title="Pause/Resume">
220
  ${paused ? "Resume" : "Pause"}
221
  </button>
222
  </div>
223
  </div>
224
  <div class="body">
225
- <div id="quotes"></div>
226
- <div id="metrics"></div>
 
 
 
 
 
 
227
  <div id="progress" class="hidden">
228
  <div id="progressFill"></div>
229
  </div>
230
- <div>
231
- <div class="text-xs text-gray-500 mb-1">Logs</div>
232
  <div id="logs"></div>
233
  </div>
234
  </div>
@@ -241,4 +262,4 @@ class BotCard extends HTMLElement {
241
  }
242
  }
243
 
244
- customElements.define("bot-card", BotCard);
 
1
+
2
+ // Bot Card Web Component - Quantum Theme
3
  class BotCard extends HTMLElement {
4
  constructor() {
5
  super();
 
87
 
88
  // Quotes
89
  quotes.innerHTML = `
90
+ <div class="space-y-1">
91
+ <div class="flex justify-between text-xs">
92
+ <span class="text-gray-400">Bid</span>
93
+ <span class="font-mono font-semibold">${this._quotes.bid}</span>
94
+ </div>
95
+ <div class="flex justify-between text-xs">
96
+ <span class="text-gray-400">Ask</span>
97
+ <span class="font-mono font-semibold">${this._quotes.ask}</span>
98
+ </div>
99
+ <div class="flex justify-between text-xs">
100
+ <span class="text-gray-400">Spread</span>
101
+ <span class="font-mono font-semibold text-accent">${this._quotes.spread}</span>
102
+ </div>
103
  </div>
104
  `;
105
 
106
  // Metrics
107
  const metricHtml = Array.from(this._metrics.entries())
108
+ .map(([k, v]) => `<div class="flex justify-between text-xs"><span class="text-gray-400">${k}</span><span class="font-mono text-indigo-300">${v}</span></div>`)
109
  .join("");
110
  metrics.innerHTML = metricHtml;
111
 
 
116
  }
117
 
118
  // Logs
119
+ logs.innerHTML = this._logs.map((l) => `<div class="text-[11px] text-gray-500 truncate font-mono">${l}</div>`).join("");
120
  // Re-apply feather icons if needed
121
  if (typeof feather !== "undefined") feather.replace();
122
  }
 
124
  render() {
125
  if (!this._bot) return;
126
  const colorMap = {
127
+ green: "bg-gradient-to-br from-emerald-500 to-green-600",
128
+ blue: "bg-gradient-to-br from-blue-500 to-cyan-600",
129
+ purple: "bg-gradient-to-br from-purple-500 to-pink-600",
130
+ amber: "bg-gradient-to-br from-amber-500 to-orange-600",
131
  };
132
+ const color = colorMap[this._bot.color] || "bg-gradient-to-br from-indigo-500 to-purple-600";
133
  const paused = this._bot.paused;
134
 
135
  this.shadowRoot.innerHTML = `
136
  <style>
137
  :host { display: block; }
138
  .card {
139
+ background: rgba(15, 23, 42, 0.6);
140
+ backdrop-filter: blur(20px);
141
+ border: 1px solid rgba(99, 102, 241, 0.2);
142
+ border-radius: 12px;
143
  overflow: hidden;
144
+ transition: all 0.3s ease;
145
+ }
146
+ .card:hover {
147
+ border-color: rgba(99, 102, 241, 0.4);
148
+ box-shadow: 0 0 20px rgba(99, 102, 241, 0.2);
149
  }
150
  .header {
151
  display: flex;
152
  align-items: center;
153
  justify-content: space-between;
154
  gap: 8px;
155
+ padding: 12px;
156
+ border-bottom: 1px solid rgba(99, 102, 241, 0.1);
157
  }
158
  .left {
159
  display: flex;
 
161
  gap: 8px;
162
  }
163
  .dot {
164
+ width: 12px;
165
+ height: 12px;
166
  border-radius: 50%;
167
+ box-shadow: 0 0 10px rgba(99, 102, 241, 0.5);
168
  }
169
  .body {
170
+ padding: 12px;
171
  display: grid;
172
  grid-template-columns: 1fr;
173
+ gap: 12px;
174
  }
175
+ .section {
176
+ padding: 8px;
177
+ background: rgba(30, 41, 59, 0.5);
178
+ border-radius: 8px;
179
+ border: 1px solid rgba(99, 102, 241, 0.1);
180
  }
181
  .meta {
182
+ font-size: 11px;
183
+ color: #94a3b8;
184
  }
185
  button {
186
+ background: rgba(99, 102, 241, 0.1);
187
+ border: 1px solid rgba(99, 102, 241, 0.3);
188
+ color: #fff;
189
+ border-radius: 6px;
190
  padding: 4px 8px;
191
+ font-size: 11px;
192
  cursor: pointer;
193
+ transition: all 0.2s ease;
194
+ display: flex;
195
+ align-items: center;
196
+ gap: 4px;
197
  }
198
  button:hover {
199
+ background: rgba(99, 102, 241, 0.2);
200
+ border-color: rgba(99, 102, 241, 0.5);
201
  }
202
  #progress {
203
+ height: 8px;
204
+ background: rgba(30, 41, 59, 0.8);
205
+ border-radius: 4px;
206
  overflow: hidden;
207
  }
208
  #progressFill {
209
  height: 100%;
210
+ background: linear-gradient(to right, #6366f1, #a855f7);
211
  width: 0%;
212
+ transition: width 0.3s ease;
213
  }
214
  #logs {
215
+ background: rgba(15, 23, 42, 0.8);
216
+ border: 1px solid rgba(99, 102, 241, 0.1);
217
  padding: 8px;
218
  border-radius: 6px;
219
+ max-height: 80px;
220
  overflow: hidden;
221
  }
222
  </style>
223
+ <div class="card" id="root">
224
  <div class="header">
225
  <div class="left">
226
  <div class="dot ${color}"></div>
227
  <div>
228
+ <div class="font-semibold text-sm text-white">${this._bot.name}</div>
229
  <div class="meta">${this._bot.type} • ${this._bot.symbol}</div>
230
  </div>
231
  </div>
232
+ <div class="flex items-center gap-2">
233
+ <span id="pausedBadge" class="text-[10px] px-2 py-1 rounded-full bg-red-500/20 text-red-400 border border-red-500/30 ${paused ? "" : "hidden"}">PAUSED</span>
234
  <button id="pauseBtn" title="Pause/Resume">
235
  ${paused ? "Resume" : "Pause"}
236
  </button>
237
  </div>
238
  </div>
239
  <div class="body">
240
+ <div class="section">
241
+ <div class="text-xs font-semibold text-gray-300 mb-2">Quotes</div>
242
+ <div id="quotes"></div>
243
+ </div>
244
+ <div class="section">
245
+ <div class="text-xs font-semibold text-gray-300 mb-2">Metrics</div>
246
+ <div id="metrics"></div>
247
+ </div>
248
  <div id="progress" class="hidden">
249
  <div id="progressFill"></div>
250
  </div>
251
+ <div class="section">
252
+ <div class="text-xs font-semibold text-gray-300 mb-2">Activity Log</div>
253
  <div id="logs"></div>
254
  </div>
255
  </div>
 
262
  }
263
  }
264
 
265
+ customElements.define("bot-card", BotCard);
index.html CHANGED
@@ -1,9 +1,10 @@
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
- <title>Trading Terminal - Light Mode</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
9
  <script>
@@ -11,8 +12,17 @@
11
  theme: {
12
  extend: {
13
  colors: {
14
- primary: '#22c55e', // green-500
15
- secondary: '#ef4444', // red-500
 
 
 
 
 
 
 
 
 
16
  }
17
  }
18
  }
@@ -20,208 +30,277 @@
20
  </script>
21
  <link rel="stylesheet" href="style.css"/>
22
  </head>
23
- <body class="bg-gray-50 text-gray-900 selection:bg-green-100 selection:text-green-900">
24
- <div class="min-h-screen grid grid-cols-12">
25
- <!-- Sidebar -->
26
- <aside class="col-span-12 lg:col-span-3 xl:col-span-2 bg-white border-r border-gray-200 flex flex-col">
27
- <!-- Header -->
28
- <div class="px-4 py-3 border-b border-gray-200 flex items-center gap-3">
29
- <div class="h-8 w-8 rounded-md bg-green-500 flex items-center justify-center">
30
- <i data-feather="activity" class="text-white w-4 h-4"></i>
31
- </div>
32
- <div>
33
- <div class="font-semibold">Trade Terminal</div>
34
- <div class="text-xs text-gray-500">Light Mode • Mock Data</div>
35
- </div>
36
- </div>
37
-
38
- <!-- Controls -->
39
- <div class="px-4 py-3 border-b border-gray-100">
40
- <div class="flex items-center justify-between">
41
- <button id="pauseAllBtn" class="inline-flex items-center gap-2 text-sm px-3 py-1.5 rounded-md border border-gray-200 hover:bg-gray-50 transition">
42
- <i data-feather="pause-circle" class="w-4 h-4"></i>
43
- Pause All
44
- </button>
45
- <div class="text-xs text-gray-500">Update: ~50ms</div>
46
- </div>
47
- </div>
48
-
49
- <!-- Watchlist -->
50
- <div class="px-4 py-3">
51
- <div class="flex items-center justify-between mb-2">
52
- <div class="font-semibold text-sm">Watchlist</div>
53
- <div class="text-xs text-gray-500">Top markets</div>
54
- </div>
55
- <div id="watchlist" class="space-y-2">
56
- <div data-symbol="BTCUSD" class="wl-item flex items-center justify-between px-3 py-2 rounded-md border border-gray-200 hover:border-green-400 cursor-pointer transition">
57
- <div class="flex items-center gap-2">
58
- <span class="text-xs px-2 py-0.5 rounded-full bg-gray-100">BTC</span>
59
- <span class="text-sm font-medium">BTC/USD</span>
60
- </div>
61
- <div class="text-right">
62
- <div class="text-sm font-semibold" data-field="price">-</div>
63
- <div class="text-[11px]" data-field="change">-</div>
64
  </div>
65
  </div>
66
- <div data-symbol="ETHUSD" class="wl-item flex items-center justify-between px-3 py-2 rounded-md border border-gray-200 hover:border-green-400 cursor-pointer transition">
67
- <div class="flex items-center gap-2">
68
- <span class="text-xs px-2 py-0.5 rounded-full bg-gray-100">ETH</span>
69
- <span class="text-sm font-medium">ETH/USD</span>
70
- </div>
71
- <div class="text-right">
72
- <div class="text-sm font-semibold" data-field="price">-</div>
73
- <div class="text-[11px]" data-field="change">-</div>
74
- </div>
75
  </div>
76
- <div data-symbol="SOLUSD" class="wl-item flex items-center justify-between px-3 py-2 rounded-md border border-gray-200 hover:border-green-400 cursor-pointer transition">
77
- <div class="flex items-center gap-2">
78
- <span class="text-xs px-2 py-0.5 rounded-full bg-gray-100">SOL</span>
79
- <span class="text-sm font-medium">SOL/USD</span>
 
 
 
80
  </div>
81
- <div class="text-right">
82
- <div class="text-sm font-semibold" data-field="price">-</div>
83
- <div class="text-[11px]" data-field="change">-</div>
 
 
 
 
84
  </div>
85
  </div>
 
 
 
 
 
 
 
 
86
  </div>
87
  </div>
 
88
 
89
- <div class="mt-auto px-4 py-3 border-t border-gray-200">
90
- <div class="text-[11px] text-gray-500">
91
- Mock microstructure data. Not financial advice.
92
- </div>
93
- </div>
94
- </aside>
95
-
96
- <!-- Main -->
97
- <main class="col-span-12 lg:col-span-9 xl:col-span-7 flex flex-col">
98
- <!-- Header -->
99
- <header class="px-4 py-3 border-b border-gray-200 bg-white">
100
- <div class="flex items-center justify-between">
101
- <div class="flex items-center gap-4">
102
- <div class="flex items-center gap-2">
103
- <span class="text-lg font-semibold" id="symbolTitle">BTC/USD</span>
104
- <span class="text-xs px-2 py-0.5 rounded-full bg-gray-100" id="exchangeTitle">BINANCE</span>
105
- </div>
106
- <div class="hidden md:flex items-center gap-6">
107
- <div class="text-right">
108
- <div class="text-xs text-gray-500">Last Price</div>
109
- <div class="text-lg font-semibold" id="lastPrice">-</div>
110
- </div>
111
- <div class="text-right">
112
- <div class="text-xs text-gray-500">24h Change</div>
113
- <div class="text-sm font-semibold" id="dayChange">-</div>
 
 
 
114
  </div>
115
- <div class="text-right">
116
- <div class="text-xs text-gray-500">Spread</div>
117
- <div class="text-sm font-semibold" id="spread">-</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  </div>
119
- <div class="text-right">
120
- <div class="text-xs text-gray-500">Mid</div>
121
- <div class="text-sm font-semibold" id="midPrice">-</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  </div>
123
  </div>
124
  </div>
125
- <div class="flex items-center gap-2">
126
- <button id="refreshBtn" class="inline-flex items-center gap-2 text-sm px-3 py-1.5 rounded-md border border-gray-200 hover:bg-gray-50 transition">
127
- <i data-feather="refresh-ccw" class="w-4 h-4"></i>
128
- Refresh
129
- </button>
130
- <div class="text-xs text-gray-500 hidden sm:block">Latency: <span id="latency">2ms</span></div>
131
- </div>
132
  </div>
133
- </header>
134
 
135
- <!-- Panels -->
136
- <section class="p-4 grid grid-cols-12 gap-4">
137
- <!-- Order Book -->
138
- <div class="col-span-12 xl:col-span-7 bg-white rounded-lg border border-gray-200 overflow-hidden">
139
- <div class="px-4 py-2 border-b border-gray-100 flex items-center justify-between">
140
- <div class="font-semibold text-sm">Order Book</div>
141
- <div class="text-xs text-gray-500" id="bookTs">-</div>
142
- </div>
143
- <div class="grid grid-cols-3 text-xs text-gray-500 px-3 py-2 border-b border-gray-100">
144
- <div class="text-left">Bid Size</div>
145
- <div class="text-center">Price</div>
146
- <div class="text-right">Ask Size</div>
147
- </div>
148
- <div id="orderBook" class="h-[340px] overflow-y-auto text-xs">
149
- <!-- Bids -->
150
- <div id="bookBids" class="flex flex-col-reverse"></div>
151
- <!-- Center Spread -->
152
- <div id="bookSpread" class="flex items-center justify-center py-1 text-[11px] text-gray-500 border-y border-gray-100"></div>
153
- <!-- Asks -->
154
- <div id="bookAsks" class="flex flex-col"></div>
155
  </div>
156
  </div>
157
 
158
- <!-- Bots Panel -->
159
- <div class="col-span-12 xl:col-span-5 bg-white rounded-lg border border-gray-200 overflow-hidden">
160
- <div class="px-4 py-2 border-b border-gray-100 flex items-center justify-between">
161
- <div class="font-semibold text-sm">Algorithmic Trading Bots</div>
162
- <div class="text-xs text-gray-500">Microstructure-driven</div>
163
  </div>
164
- <div id="botsPanel" class="p-3 space-y-3 h-[340px] overflow-y-auto"></div>
165
  </div>
 
166
 
167
- <!-- Trade Tape -->
168
- <div class="col-span-12 bg-white rounded-lg border border-gray-200 overflow-hidden">
169
- <div class="px-4 py-2 border-b border-gray-100 flex items-center justify-between">
170
- <div class="font-semibold text-sm">Trade Tape (Real-time)</div>
171
- <div class="text-xs text-gray-500">Last 200 trades</div>
172
- </div>
173
- <div id="tradeTape" class="h-[220px] overflow-y-auto text-xs">
174
- <div class="grid grid-cols-12 px-3 py-2 text-gray-500 border-b border-gray-100">
175
- <div class="col-span-3">Time</div>
176
- <div class="col-span-3 text-right">Price</div>
177
- <div class="col-span-3 text-right">Size</div>
178
- <div class="col-span-3 text-right">Side</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  </div>
180
- <div id="tapeBody"></div>
181
  </div>
182
- </div>
183
- </section>
184
- </main>
185
 
186
- <!-- Right Sidebar -->
187
- <aside class="col-span-12 xl:col-span-3 bg-white border-l border-gray-200 flex flex-col">
188
- <!-- Portfolio -->
189
- <div class="px-4 py-3 border-b border-gray-200">
190
- <div class="font-semibold text-sm">Portfolio (Mock)</div>
191
- </div>
192
- <div class="px-4 py-3 space-y-2">
193
- <div class="flex items-center justify-between text-sm">
194
- <span class="text-gray-500">Cash</span>
195
- <span class="font-semibold" id="cash">$1,000,000.00</span>
196
- </div>
197
- <div class="flex items-center justify-between text-sm">
198
- <span class="text-gray-500">Margin Used</span>
199
- <span class="font-semibold" id="marginUsed">$0.00</span>
200
- </div>
201
- <div class="flex items-center justify-between text-sm">
202
- <span class="text-gray-500">Positions P&L</span>
203
- <span class="font-semibold" id="pnl">$0.00</span>
204
- </div>
205
- <div class="mt-2 pt-2 border-t border-gray-100">
206
- <div class="text-xs text-gray-500">Holdings</div>
207
- <ul class="mt-1 space-y-1 text-sm" id="holdings">
208
- <!-- filled by script -->
209
- </ul>
210
- </div>
211
- </div>
212
 
213
- <div class="mt-auto px-4 py-3 border-t border-gray-200">
214
- <div class="text-[11px] text-gray-500">
215
- © 2025 Trading Terminal Demo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  </div>
217
- </div>
218
- </aside>
219
  </div>
220
 
221
  <!-- Components -->
222
  <script src="components/bot-card.js"></script>
223
  <script src="script.js"></script>
224
  <script>feather.replace();</script>
225
- <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
226
  </body>
227
- </html>
 
1
+
2
  <!DOCTYPE html>
3
  <html lang="en">
4
  <head>
5
  <meta charset="UTF-8" />
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
7
+ <title>Quantum Trading Terminal</title>
8
  <script src="https://cdn.tailwindcss.com"></script>
9
  <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
  <script>
 
12
  theme: {
13
  extend: {
14
  colors: {
15
+ primary: '#6366f1', // indigo-500
16
+ secondary: '#a855f7', // purple-500
17
+ accent: '#14b8a6', // teal-500
18
+ dark: '#0f172a', // slate-900
19
+ 'dark-secondary': '#1e293b', // slate-800
20
+ 'dark-tertiary': '#334155', // slate-700
21
+ },
22
+ animation: {
23
+ 'glow': 'glow 2s ease-in-out infinite',
24
+ 'float': 'float 3s ease-in-out infinite',
25
+ 'pulse-slow': 'pulse 3s ease-in-out infinite',
26
  }
27
  }
28
  }
 
30
  </script>
31
  <link rel="stylesheet" href="style.css"/>
32
  </head>
33
+ <body class="bg-gradient-to-br from-dark via-dark-secondary to-dark text-white selection:bg-indigo-500/20 selection:text-indigo-200">
34
+ <!-- Background Effects -->
35
+ <div class="fixed inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-indigo-900/10 via-transparent to-transparent"></div>
36
+ <div class="fixed inset-0 bg-gradient-to-t from-transparent via-purple-900/5 to-transparent pointer-events-none"></div>
37
+
38
+ <div class="min-h-screen relative z-10">
39
+ <!-- Header Bar -->
40
+ <header class="fixed top-0 left-0 right-0 h-16 bg-dark/80 backdrop-blur-xl border-b border-indigo-500/20 z-50">
41
+ <div class="h-full flex items-center justify-between px-6">
42
+ <div class="flex items-center gap-4">
43
+ <div class="relative">
44
+ <div class="absolute inset-0 bg-indigo-500 blur-xl animate-pulse-slow"></div>
45
+ <div class="relative h-10 w-10 rounded-xl bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg">
46
+ <i data-feather="zap" class="text-white w-5 h-5"></i>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  </div>
48
  </div>
49
+ <div>
50
+ <div class="text-xl font-bold bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent">Quantum Terminal</div>
51
+ <div class="text-xs text-gray-400">Real-Time Trading Engine</div>
 
 
 
 
 
 
52
  </div>
53
+ </div>
54
+
55
+ <div class="flex items-center gap-6">
56
+ <div class="hidden lg:flex items-center gap-8">
57
+ <div class="text-center">
58
+ <div class="text-xs text-gray-500">Last Price</div>
59
+ <div class="text-lg font-bold" id="lastPrice">-</div>
60
  </div>
61
+ <div class="text-center">
62
+ <div class="text-xs text-gray-500">24h Change</div>
63
+ <div class="text-sm font-semibold" id="dayChange">-</div>
64
+ </div>
65
+ <div class="text-center">
66
+ <div class="text-xs text-gray-500">Volume 24h</div>
67
+ <div class="text-sm font-semibold" id="volume24h">$1.2B</div>
68
  </div>
69
  </div>
70
+
71
+ <div class="flex items-center gap-3">
72
+ <button id="pauseAllBtn" class="px-4 py-2 rounded-lg bg-indigo-500/10 hover:bg-indigo-500/20 border border-indigo-500/30 transition-all duration-200 flex items-center gap-2">
73
+ <i data-feather="pause-circle" class="w-4 h-4"></i>
74
+ <span class="text-sm">Pause</span>
75
+ </button>
76
+ <div class="text-xs text-gray-500">Latency: <span id="latency" class="text-indigo-400">2ms</span></div>
77
+ </div>
78
  </div>
79
  </div>
80
+ </header>
81
 
82
+ <div class="flex h-screen pt-16">
83
+ <!-- Left Sidebar -->
84
+ <aside class="w-72 bg-dark/50 backdrop-blur-lg border-r border-indigo-500/20 flex flex-col">
85
+ <!-- Markets Section -->
86
+ <div class="p-4 border-b border-indigo-500/10">
87
+ <div class="flex items-center justify-between mb-3">
88
+ <h2 class="text-sm font-semibold text-gray-300">Markets</h2>
89
+ <span class="text-xs px-2 py-1 rounded-full bg-accent/20 text-accent border border-accent/30">LIVE</span>
90
+ </div>
91
+ <div id="watchlist" class="space-y-2">
92
+ <div data-symbol="BTCUSD" class="wl-item group relative">
93
+ <div class="absolute inset-0 bg-gradient-to-r from-indigo-500/5 to-purple-500/5 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity"></div>
94
+ <div class="relative p-3 rounded-lg border border-indigo-500/20 hover:border-indigo-400/50 cursor-pointer transition-all duration-200">
95
+ <div class="flex items-center justify-between">
96
+ <div class="flex items-center gap-3">
97
+ <div class="w-8 h-8 rounded-lg bg-gradient-to-br from-orange-500 to-yellow-500 flex items-center justify-center">
98
+ <span class="text-xs font-bold">BTC</span>
99
+ </div>
100
+ <div>
101
+ <div class="text-sm font-medium">BTC/USD</div>
102
+ <div class="text-xs text-gray-500">Bitcoin</div>
103
+ </div>
104
+ </div>
105
+ <div class="text-right">
106
+ <div class="text-sm font-semibold" data-field="price">-</div>
107
+ <div class="text-xs" data-field="change">-</div>
108
+ </div>
109
+ </div>
110
  </div>
111
+ </div>
112
+ <div data-symbol="ETHUSD" class="wl-item group relative">
113
+ <div class="absolute inset-0 bg-gradient-to-r from-indigo-500/5 to-purple-500/5 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity"></div>
114
+ <div class="relative p-3 rounded-lg border border-indigo-500/20 hover:border-indigo-400/50 cursor-pointer transition-all duration-200">
115
+ <div class="flex items-center justify-between">
116
+ <div class="flex items-center gap-3">
117
+ <div class="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center">
118
+ <span class="text-xs font-bold">ETH</span>
119
+ </div>
120
+ <div>
121
+ <div class="text-sm font-medium">ETH/USD</div>
122
+ <div class="text-xs text-gray-500">Ethereum</div>
123
+ </div>
124
+ </div>
125
+ <div class="text-right">
126
+ <div class="text-sm font-semibold" data-field="price">-</div>
127
+ <div class="text-xs" data-field="change">-</div>
128
+ </div>
129
+ </div>
130
  </div>
131
+ </div>
132
+ <div data-symbol="SOLUSD" class="wl-item group relative">
133
+ <div class="absolute inset-0 bg-gradient-to-r from-indigo-500/5 to-purple-500/5 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity"></div>
134
+ <div class="relative p-3 rounded-lg border border-indigo-500/20 hover:border-indigo-400/50 cursor-pointer transition-all duration-200">
135
+ <div class="flex items-center justify-between">
136
+ <div class="flex items-center gap-3">
137
+ <div class="w-8 h-8 rounded-lg bg-gradient-to-br from-purple-500 to-pink-500 flex items-center justify-center">
138
+ <span class="text-xs font-bold">SOL</span>
139
+ </div>
140
+ <div>
141
+ <div class="text-sm font-medium">SOL/USD</div>
142
+ <div class="text-xs text-gray-500">Solana</div>
143
+ </div>
144
+ </div>
145
+ <div class="text-right">
146
+ <div class="text-sm font-semibold" data-field="price">-</div>
147
+ <div class="text-xs" data-field="change">-</div>
148
+ </div>
149
+ </div>
150
  </div>
151
  </div>
152
  </div>
 
 
 
 
 
 
 
153
  </div>
 
154
 
155
+ <!-- Quick Stats -->
156
+ <div class="p-4 border-b border-indigo-500/10">
157
+ <h3 class="text-xs font-semibold text-gray-400 mb-3">Market Stats</h3>
158
+ <div class="space-y-2">
159
+ <div class="flex justify-between items-center">
160
+ <span class="text-xs text-gray-500">Total Volume</span>
161
+ <span class="text-xs font-semibold">$2.4B</span>
162
+ </div>
163
+ <div class="flex justify-between items-center">
164
+ <span class="text-xs text-gray-500">Open Interest</span>
165
+ <span class="text-xs font-semibold">$856M</span>
166
+ </div>
167
+ <div class="flex justify-between items-center">
168
+ <span class="text-xs text-gray-500">Funding Rate</span>
169
+ <span class="text-xs font-semibold text-accent">0.012%</span>
170
+ </div>
 
 
 
 
171
  </div>
172
  </div>
173
 
174
+ <div class="mt-auto p-4 border-t border-indigo-500/20">
175
+ <div class="text-xs text-gray-500 text-center">
176
+ Quantum Trading Engine v2.0
 
 
177
  </div>
 
178
  </div>
179
+ </aside>
180
 
181
+ <!-- Main Content -->
182
+ <main class="flex-1 flex flex-col bg-gradient-to-br from-dark-secondary/50 to-dark-tertiary/30">
183
+ <div class="flex-1 p-6 space-y-6">
184
+ <!-- Top Row: Order Book & Trading Bots -->
185
+ <div class="grid grid-cols-12 gap-6">
186
+ <!-- Order Book -->
187
+ <div class="col-span-12 xl:col-span-8 relative group">
188
+ <div class="absolute inset-0 bg-gradient-to-br from-indigo-500/10 to-purple-500/10 rounded-2xl blur-xl group-hover:blur-2xl transition-all duration-500"></div>
189
+ <div class="relative bg-dark/60 backdrop-blur-xl rounded-2xl border border-indigo-500/20 overflow-hidden">
190
+ <div class="px-6 py-4 border-b border-indigo-500/20 flex items-center justify-between">
191
+ <div>
192
+ <h3 class="text-lg font-bold">Order Book</h3>
193
+ <p class="text-xs text-gray-400">Depth visualization</p>
194
+ </div>
195
+ <div class="flex items-center gap-4">
196
+ <span class="text-xs text-gray-500" id="bookTs">-</span>
197
+ <button class="p-2 rounded-lg bg-indigo-500/10 hover:bg-indigo-500/20 border border-indigo-500/30 transition-all">
198
+ <i data-feather="settings" class="w-4 h-4"></i>
199
+ </button>
200
+ </div>
201
+ </div>
202
+
203
+ <div class="p-4">
204
+ <div class="grid grid-cols-3 text-xs text-gray-400 mb-2">
205
+ <div class="text-left">Size</div>
206
+ <div class="text-center">Price</div>
207
+ <div class="text-right">Size</div>
208
+ </div>
209
+ <div id="orderBook" class="h-[400px] overflow-y-auto space-y-1">
210
+ <div id="bookBids" class="space-y-0.5"></div>
211
+ <div id="bookSpread" class="py-2 text-center text-xs text-gray-500 border-y border-indigo-500/20 my-2"></div>
212
+ <div id="bookAsks" class="space-y-0.5"></div>
213
+ </div>
214
+ </div>
215
+ </div>
216
+ </div>
217
+
218
+ <!-- Trading Bots -->
219
+ <div class="col-span-12 xl:col-span-4 relative group">
220
+ <div class="absolute inset-0 bg-gradient-to-br from-purple-500/10 to-pink-500/10 rounded-2xl blur-xl group-hover:blur-2xl transition-all duration-500"></div>
221
+ <div class="relative bg-dark/60 backdrop-blur-xl rounded-2xl border border-purple-500/20 overflow-hidden">
222
+ <div class="px-6 py-4 border-b border-purple-500/20">
223
+ <h3 class="text-lg font-bold">Trading Bots</h3>
224
+ <p class="text-xs text-gray-400">AI-powered strategies</p>
225
+ </div>
226
+ <div id="botsPanel" class="p-4 space-y-3 h-[400px] overflow-y-auto"></div>
227
+ </div>
228
  </div>
 
229
  </div>
 
 
 
230
 
231
+ <!-- Bottom Row: Trade Tape & Portfolio -->
232
+ <div class="grid grid-cols-12 gap-6">
233
+ <!-- Trade Tape -->
234
+ <div class="col-span-12 lg:col-span-8 relative group">
235
+ <div class="absolute inset-0 bg-gradient-to-br from-accent/10 to-indigo-500/10 rounded-2xl blur-xl group-hover:blur-2xl transition-all duration-500"></div>
236
+ <div class="relative bg-dark/60 backdrop-blur-xl rounded-2xl border border-accent/20 overflow-hidden">
237
+ <div class="px-6 py-4 border-b border-accent/20 flex items-center justify-between">
238
+ <div>
239
+ <h3 class="text-lg font-bold">Live Trades</h3>
240
+ <p class="text-xs text-gray-400">Real-time market activity</p>
241
+ </div>
242
+ <div class="text-xs text-gray-500">Last 200</div>
243
+ </div>
244
+ <div id="tradeTape" class="h-[250px] overflow-y-auto">
245
+ <div class="grid grid-cols-12 px-4 py-2 text-xs text-gray-400 border-b border-accent/20">
246
+ <div class="col-span-2">Time</div>
247
+ <div class="col-span-3 text-right">Price</div>
248
+ <div class="col-span-3 text-right">Size</div>
249
+ <div class="col-span-2 text-right">Side</div>
250
+ <div class="col-span-2 text-right">Value</div>
251
+ </div>
252
+ <div id="tapeBody"></div>
253
+ </div>
254
+ </div>
255
+ </div>
 
256
 
257
+ <!-- Portfolio -->
258
+ <div class="col-span-12 lg:col-span-4 relative group">
259
+ <div class="absolute inset-0 bg-gradient-to-br from-yellow-500/10 to-orange-500/10 rounded-2xl blur-xl group-hover:blur-2xl transition-all duration-500"></div>
260
+ <div class="relative bg-dark/60 backdrop-blur-xl rounded-2xl border border-yellow-500/20 overflow-hidden">
261
+ <div class="px-6 py-4 border-b border-yellow-500/20">
262
+ <h3 class="text-lg font-bold">Portfolio</h3>
263
+ <p class="text-xs text-gray-400">Performance metrics</p>
264
+ </div>
265
+ <div class="p-4 space-y-4">
266
+ <div class="space-y-3">
267
+ <div class="flex items-center justify-between">
268
+ <span class="text-sm text-gray-400">Cash Balance</span>
269
+ <span class="text-sm font-bold" id="cash">$1,000,000.00</span>
270
+ </div>
271
+ <div class="flex items-center justify-between">
272
+ <span class="text-sm text-gray-400">Margin Used</span>
273
+ <span class="text-sm font-bold" id="marginUsed">$0.00</span>
274
+ </div>
275
+ <div class="flex items-center justify-between">
276
+ <span class="text-sm text-gray-400">Unrealized P&L</span>
277
+ <span class="text-sm font-bold" id="pnl">$0.00</span>
278
+ </div>
279
+ <div class="h-px bg-gradient-to-r from-transparent via-gray-600 to-transparent"></div>
280
+ <div class="flex items-center justify-between">
281
+ <span class="text-sm text-gray-400">Total Value</span>
282
+ <span class="text-lg font-bold text-indigo-400" id="totalValue">$1,000,000.00</span>
283
+ </div>
284
+ </div>
285
+
286
+ <div class="space-y-2">
287
+ <h4 class="text-xs font-semibold text-gray-400">Holdings</h4>
288
+ <ul class="space-y-2" id="holdings">
289
+ <!-- filled by script -->
290
+ </ul>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ </div>
295
+ </div>
296
  </div>
297
+ </main>
298
+ </div>
299
  </div>
300
 
301
  <!-- Components -->
302
  <script src="components/bot-card.js"></script>
303
  <script src="script.js"></script>
304
  <script>feather.replace();</script>
 
305
  </body>
306
+ </html>
script.js CHANGED
@@ -148,7 +148,6 @@
148
  updatePortfolioUI();
149
  return { price: fillPrice, size: vol };
150
  }
151
-
152
  // UI: Order Book
153
  function renderOrderBook() {
154
  const bidsEl = document.getElementById("bookBids");
@@ -167,14 +166,14 @@
167
  for (const level of state.bids) {
168
  const intensity = level.size / maxBidVol;
169
  const row = document.createElement("div");
170
- row.className = "grid grid-cols-3 px-3 py-1 hover:bg-gray-50";
171
  row.innerHTML = `
172
- <div class="text-left text-green-600">${fmtSize(level.size)}</div>
173
- <div class="text-center font-semibold">${fmtNum(level.price, 1)}</div>
174
- <div class="text-right text-gray-400">-</div>
175
  `;
176
  // Gradient background bar
177
- row.style.background = `linear-gradient(to right, rgba(34,197,94,${0.10 * intensity}), rgba(34,197,94,0) 60%)`;
178
  bidsEl.appendChild(row);
179
  }
180
 
@@ -182,44 +181,46 @@
182
  for (const level of state.asks) {
183
  const intensity = level.size / maxAskVol;
184
  const row = document.createElement("div");
185
- row.className = "grid grid-cols-3 px-3 py-1 hover:bg-gray-50";
186
  row.innerHTML = `
187
- <div class="text-left text-gray-400">-</div>
188
- <div class="text-center font-semibold">${fmtNum(level.price, 1)}</div>
189
- <div class="text-right text-red-600">${fmtSize(level.size)}</div>
190
  `;
191
- row.style.background = `linear-gradient(to right, rgba(239,68,68,0) 40%, rgba(239,68,68,${0.10 * intensity}))`;
192
  asksEl.appendChild(row);
193
  }
194
 
195
  // Spread
196
  const bestBid = state.bids[state.bids.length - 1]?.price ?? state.lastPrice - state.spread / 2;
197
  const bestAsk = state.asks[0]?.price ?? state.lastPrice + state.spread / 2;
198
- spreadEl.textContent = `Spread: ${fmtNum(bestAsk - bestBid, 2)} | Best Bid: ${fmtNum(bestBid, 1)} | Best Ask: ${fmtNum(bestAsk, 1)}`;
199
- tsEl.textContent = "Updated: " + nowTs();
200
  }
201
-
202
  // UI: Trade Tape
203
  function renderTape() {
204
  const body = document.getElementById("tapeBody");
205
  body.innerHTML = state.tape
206
  .map(
207
- (t) => `
208
- <div class="grid grid-cols-12 px-3 py-1 border-b border-gray-50 hover:bg-gray-50">
209
- <div class="col-span-3">${t.time}</div>
210
- <div class="col-span-3 text-right font-semibold">${fmtNum(t.price, 2)}</div>
211
- <div class="col-span-3 text-right">${fmtSize(t.size)}</div>
212
- <div class="col-span-3 text-right">
213
- <span class="px-2 py-0.5 rounded-full text-white text-[10px] ${t.side === "buy" ? "bg-green-500" : "bg-red-500"}">
 
 
214
  ${t.side.toUpperCase()}
215
  </span>
216
  </div>
 
217
  </div>
218
- `
 
219
  )
220
  .join("");
221
  }
222
-
223
  // UI: Header stats
224
  function renderHeader() {
225
  const lastEl = document.getElementById("lastPrice");
@@ -228,21 +229,34 @@
228
  const midEl = document.getElementById("midPrice");
229
  const symEl = document.getElementById("symbolTitle");
230
  const exEl = document.getElementById("exchangeTitle");
 
231
 
232
  lastEl.textContent = fmtNum(state.lastPrice, 2);
233
  dayEl.textContent = `${state.dayChange >= 0 ? "+" : ""}${state.dayChange.toFixed(2)}%`;
234
- dayEl.classList.toggle("text-green-600", state.dayChange >= 0);
235
- dayEl.classList.toggle("text-red-600", state.dayChange < 0);
236
 
237
  const bestBid = state.bids[state.bids.length - 1]?.price ?? state.lastPrice - state.spread / 2;
238
  const bestAsk = state.asks[0]?.price ?? state.lastPrice + state.spread / 2;
239
  const mid = (bestBid + bestAsk) / 2;
240
  spreadEl.textContent = fmtNum(bestAsk - bestBid, 2);
241
- midEl.textContent = fmtNum(mid, 2);
242
- symEl.textContent = state.symbol.replace("USD", "/USD");
243
- exEl.textContent = state.exchange;
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  }
245
-
246
  // UI: Watchlist
247
  function renderWatchlist() {
248
  const items = document.querySelectorAll("#watchlist .wl-item");
@@ -261,16 +275,17 @@
261
  const changeEl = el.querySelector('[data-field="change"]');
262
  priceEl.textContent = fmtNum(price, 2);
263
  changeEl.textContent = `${change >= 0 ? "+" : ""}${change.toFixed(2)}%`;
264
- changeEl.classList.toggle("text-green-600", change >= 0);
265
- changeEl.classList.toggle("text-red-600", change < 0);
266
 
267
  // Visual selection
268
- el.classList.toggle("border-green-400", sym === state.symbol);
269
- el.classList.toggle("ring-1", sym === state.symbol);
270
- el.classList.toggle("ring-green-300", sym === state.symbol);
 
 
271
  });
272
  }
273
-
274
  // UI: Portfolio
275
  function updatePortfolioUI() {
276
  const pnlEl = document.getElementById("pnl");
@@ -286,24 +301,35 @@
286
  }
287
  }
288
  pnlEl.textContent = fmtUSD(unrealized);
289
- pnlEl.classList.toggle("text-green-600", unrealized >= 0);
290
- pnlEl.classList.toggle("text-red-600", unrealized < 0);
291
  cashEl.textContent = fmtUSD(state.portfolio.cash);
292
  marginEl.textContent = fmtUSD(state.portfolio.marginUsed);
293
 
294
  holdingsEl.innerHTML = Object.entries(state.portfolio.positions)
 
295
  .map(
296
- ([sym, pos]) => `
297
- <li class="flex items-center justify-between">
298
- <span class="text-gray-500">${sym}</span>
299
- <span class="font-semibold">${pos.qty.toFixed(4)} @ ${fmtNum(pos.avg, 2)}</span>
300
- </li>
301
- `
 
 
 
 
 
 
 
 
 
 
 
302
  )
303
  .join("");
304
  }
305
-
306
- function getSymbolPrice(sym) {
307
  if (sym === state.symbol) return state.lastPrice;
308
  // Mock other symbols around BTC with small offset
309
  const map = { ETHUSD: 3200, SOLUSD: 160 };
@@ -510,7 +536,6 @@
510
  }
511
  card.setMetric("Window", bot.params.window);
512
  }
513
-
514
  // Event: select symbol in watchlist
515
  function wireWatchlist() {
516
  document.querySelectorAll("#watchlist .wl-item").forEach((el) => {
@@ -527,8 +552,7 @@
527
  });
528
  });
529
  }
530
-
531
- // Top-level loop
532
  function tick() {
533
  if (!state.pausedAll) {
534
  tickMicrostructure();
@@ -539,27 +563,27 @@
539
  botEngineLoop();
540
  }
541
  }
542
-
543
  // Controls
544
  function wireControls() {
545
  const pauseBtn = document.getElementById("pauseAllBtn");
546
  pauseBtn.addEventListener("click", () => {
547
  state.pausedAll = !state.pausedAll;
548
  pauseBtn.innerHTML = state.pausedAll
549
- ? `<i data-feather="play-circle" class="w-4 h-4"></i> Resume All`
550
- : `<i data-feather="pause-circle" class="w-4 h-4"></i> Pause All`;
551
  feather.replace();
552
  });
553
 
554
- document.getElementById("refreshBtn").addEventListener("click", () => {
555
- // Reset dayOpen to current to avoid large 24h moves on refresh
556
- state.dayOpen = state.lastPrice;
557
- renderHeader();
558
- renderWatchlist();
559
- });
 
 
560
  }
561
-
562
- // Initialize
563
  function init() {
564
  wireWatchlist();
565
  wireControls();
 
148
  updatePortfolioUI();
149
  return { price: fillPrice, size: vol };
150
  }
 
151
  // UI: Order Book
152
  function renderOrderBook() {
153
  const bidsEl = document.getElementById("bookBids");
 
166
  for (const level of state.bids) {
167
  const intensity = level.size / maxBidVol;
168
  const row = document.createElement("div");
169
+ row.className = "grid grid-cols-3 px-3 py-1 hover:bg-indigo-500/10 rounded transition-all duration-200";
170
  row.innerHTML = `
171
+ <div class="text-left text-emerald-400 font-mono">${fmtSize(level.size)}</div>
172
+ <div class="text-center font-semibold text-white">${fmtNum(level.price, 1)}</div>
173
+ <div class="text-right text-gray-500">-</div>
174
  `;
175
  // Gradient background bar
176
+ row.style.background = `linear-gradient(to right, rgba(16,185,129,${0.15 * intensity}), rgba(16,185,129,0.02) 80%)`;
177
  bidsEl.appendChild(row);
178
  }
179
 
 
181
  for (const level of state.asks) {
182
  const intensity = level.size / maxAskVol;
183
  const row = document.createElement("div");
184
+ row.className = "grid grid-cols-3 px-3 py-1 hover:bg-indigo-500/10 rounded transition-all duration-200";
185
  row.innerHTML = `
186
+ <div class="text-left text-gray-500">-</div>
187
+ <div class="text-center font-semibold text-white">${fmtNum(level.price, 1)}</div>
188
+ <div class="text-right text-rose-400 font-mono">${fmtSize(level.size)}</div>
189
  `;
190
+ row.style.background = `linear-gradient(to right, rgba(244,63,94,0.02) 20%, rgba(244,63,94,${0.15 * intensity}))`;
191
  asksEl.appendChild(row);
192
  }
193
 
194
  // Spread
195
  const bestBid = state.bids[state.bids.length - 1]?.price ?? state.lastPrice - state.spread / 2;
196
  const bestAsk = state.asks[0]?.price ?? state.lastPrice + state.spread / 2;
197
+ spreadEl.innerHTML = `<span class="text-indigo-300">Spread:</span> <span class="font-mono text-accent">${fmtNum(bestAsk - bestBid, 2)}</span> | <span class="text-gray-400">Best:</span> <span class="text-emerald-400">${fmtNum(bestBid, 1)}</span> / <span class="text-rose-400">${fmtNum(bestAsk, 1)}</span>`;
198
+ tsEl.textContent = nowTs();
199
  }
 
200
  // UI: Trade Tape
201
  function renderTape() {
202
  const body = document.getElementById("tapeBody");
203
  body.innerHTML = state.tape
204
  .map(
205
+ (t) => {
206
+ const value = t.price * t.size;
207
+ return `
208
+ <div class="grid grid-cols-12 px-4 py-2 border-b border-accent/10 hover:bg-accent/5 transition-colors">
209
+ <div class="col-span-2 text-gray-400">${t.time}</div>
210
+ <div class="col-span-3 text-right font-mono font-semibold text-white">${fmtNum(t.price, 2)}</div>
211
+ <div class="col-span-3 text-right font-mono text-gray-300">${fmtSize(t.size)}</div>
212
+ <div class="col-span-2 text-right">
213
+ <span class="px-2 py-1 rounded-full text-white text-[10px] font-semibold ${t.side === "buy" ? "bg-emerald-500/80" : "bg-rose-500/80"}">
214
  ${t.side.toUpperCase()}
215
  </span>
216
  </div>
217
+ <div class="col-span-2 text-right font-mono text-indigo-300">${fmtNum(value, 0)}</div>
218
  </div>
219
+ `;
220
+ }
221
  )
222
  .join("");
223
  }
 
224
  // UI: Header stats
225
  function renderHeader() {
226
  const lastEl = document.getElementById("lastPrice");
 
229
  const midEl = document.getElementById("midPrice");
230
  const symEl = document.getElementById("symbolTitle");
231
  const exEl = document.getElementById("exchangeTitle");
232
+ const totalEl = document.getElementById("totalValue");
233
 
234
  lastEl.textContent = fmtNum(state.lastPrice, 2);
235
  dayEl.textContent = `${state.dayChange >= 0 ? "+" : ""}${state.dayChange.toFixed(2)}%`;
236
+ dayEl.classList.toggle("text-emerald-400", state.dayChange >= 0);
237
+ dayEl.classList.toggle("text-rose-400", state.dayChange < 0);
238
 
239
  const bestBid = state.bids[state.bids.length - 1]?.price ?? state.lastPrice - state.spread / 2;
240
  const bestAsk = state.asks[0]?.price ?? state.lastPrice + state.spread / 2;
241
  const mid = (bestBid + bestAsk) / 2;
242
  spreadEl.textContent = fmtNum(bestAsk - bestBid, 2);
243
+
244
+ if (midEl) midEl.textContent = fmtNum(mid, 2);
245
+ if (symEl) symEl.textContent = state.symbol.replace("USD", "/USD");
246
+ if (exEl) exEl.textContent = state.exchange;
247
+
248
+ // Update total portfolio value
249
+ if (totalEl) {
250
+ let totalValue = state.portfolio.cash;
251
+ for (const [sym, pos] of Object.entries(state.portfolio.positions)) {
252
+ const mark = getSymbolPrice(sym);
253
+ if (pos.qty !== 0) {
254
+ totalValue += mark * pos.qty;
255
+ }
256
+ }
257
+ totalEl.textContent = fmtUSD(totalValue);
258
+ }
259
  }
 
260
  // UI: Watchlist
261
  function renderWatchlist() {
262
  const items = document.querySelectorAll("#watchlist .wl-item");
 
275
  const changeEl = el.querySelector('[data-field="change"]');
276
  priceEl.textContent = fmtNum(price, 2);
277
  changeEl.textContent = `${change >= 0 ? "+" : ""}${change.toFixed(2)}%`;
278
+ changeEl.classList.toggle("text-emerald-400", change >= 0);
279
+ changeEl.classList.toggle("text-rose-400", change < 0);
280
 
281
  // Visual selection
282
+ const container = el.querySelector('.relative > div:last-child');
283
+ container.classList.toggle("border-indigo-400", sym === state.symbol);
284
+ container.classList.toggle("ring-1", sym === state.symbol);
285
+ container.classList.toggle("ring-indigo-300", sym === state.symbol);
286
+ container.classList.toggle("bg-indigo-500/10", sym === state.symbol);
287
  });
288
  }
 
289
  // UI: Portfolio
290
  function updatePortfolioUI() {
291
  const pnlEl = document.getElementById("pnl");
 
301
  }
302
  }
303
  pnlEl.textContent = fmtUSD(unrealized);
304
+ pnlEl.classList.toggle("text-emerald-400", unrealized >= 0);
305
+ pnlEl.classList.toggle("text-rose-400", unrealized < 0);
306
  cashEl.textContent = fmtUSD(state.portfolio.cash);
307
  marginEl.textContent = fmtUSD(state.portfolio.marginUsed);
308
 
309
  holdingsEl.innerHTML = Object.entries(state.portfolio.positions)
310
+ .filter(([_, pos]) => pos.qty !== 0)
311
  .map(
312
+ ([sym, pos]) => {
313
+ const mark = getSymbolPrice(sym);
314
+ const value = mark * pos.qty;
315
+ const pnl = (mark - pos.avg) * pos.qty;
316
+ return `
317
+ <li class="p-2 rounded-lg bg-dark/50 border border-indigo-500/10">
318
+ <div class="flex items-center justify-between mb-1">
319
+ <span class="text-xs text-gray-400">${sym}</span>
320
+ <span class="text-xs font-mono ${pnl >= 0 ? 'text-emerald-400' : 'text-rose-400'}">${fmtUSD(pnl)}</span>
321
+ </div>
322
+ <div class="flex items-center justify-between">
323
+ <span class="text-sm font-semibold">${pos.qty.toFixed(4)}</span>
324
+ <span class="text-xs text-gray-500">${fmtUSD(value)}</span>
325
+ </div>
326
+ </li>
327
+ `;
328
+ }
329
  )
330
  .join("");
331
  }
332
+ function getSymbolPrice(sym) {
 
333
  if (sym === state.symbol) return state.lastPrice;
334
  // Mock other symbols around BTC with small offset
335
  const map = { ETHUSD: 3200, SOLUSD: 160 };
 
536
  }
537
  card.setMetric("Window", bot.params.window);
538
  }
 
539
  // Event: select symbol in watchlist
540
  function wireWatchlist() {
541
  document.querySelectorAll("#watchlist .wl-item").forEach((el) => {
 
552
  });
553
  });
554
  }
555
+ // Top-level loop
 
556
  function tick() {
557
  if (!state.pausedAll) {
558
  tickMicrostructure();
 
563
  botEngineLoop();
564
  }
565
  }
 
566
  // Controls
567
  function wireControls() {
568
  const pauseBtn = document.getElementById("pauseAllBtn");
569
  pauseBtn.addEventListener("click", () => {
570
  state.pausedAll = !state.pausedAll;
571
  pauseBtn.innerHTML = state.pausedAll
572
+ ? `<i data-feather="play-circle" class="w-4 h-4"></i> <span class="text-sm">Resume</span>`
573
+ : `<i data-feather="pause-circle" class="w-4 h-4"></i> <span class="text-sm">Pause</span>`;
574
  feather.replace();
575
  });
576
 
577
+ const refreshBtn = document.getElementById("refreshBtn");
578
+ if (refreshBtn) {
579
+ refreshBtn.addEventListener("click", () => {
580
+ state.dayOpen = state.lastPrice;
581
+ renderHeader();
582
+ renderWatchlist();
583
+ });
584
+ }
585
  }
586
+ // Initialize
 
587
  function init() {
588
  wireWatchlist();
589
  wireControls();