EricSam commited on
Commit
1545499
Β·
verified Β·
1 Parent(s): 8949611

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +114 -53
index.html CHANGED
@@ -2,6 +2,7 @@
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>Nakhoda4X Pro - Portfolio Dashboard</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
@@ -14,7 +15,7 @@
14
  theme: {
15
  extend: {
16
  colors: {
17
- primary: '#1E90FF',
18
  secondary: '#8b5cf6',
19
  darkBg: '#111827',
20
  darkCard: '#1f2937',
@@ -24,11 +25,33 @@
24
  }
25
  </script>
26
  <style>
27
- .trading-card:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); }
28
- .animate-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
29
- @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
30
- .coin-animation { animation: float 3s ease-in-out infinite; }
31
- @keyframes float { 0% { transform: translateY(0px); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0px); } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  </style>
33
  </head>
34
  <body class="bg-gray-100 dark:bg-darkBg transition-colors duration-300">
@@ -204,7 +227,7 @@
204
  <!-- API Connection Section -->
205
  <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
206
  <div class="bg-gradient-to-r from-primary to-secondary rounded-2xl p-6">
207
- <div class="flex items-center">
208
  <div class="mr-4">
209
  <div class="h-12 w-12 rounded-xl bg-white/30 flex items-center justify-center">
210
  <i class="fas fa-plug text-white text-xl"></i>
@@ -215,7 +238,16 @@
215
  <p id="last-sync" class="text-blue-100">Last synced: Just now</p>
216
  </div>
217
  </div>
218
- <button id="sync-now" class="mt-6 w-full py-3 bg-white rounded-xl text-primary font-bold flex items-center justify-center">
 
 
 
 
 
 
 
 
 
219
  <i class="fas fa-sync mr-2"></i> Sync Now
220
  </button>
221
  </div>
@@ -249,57 +281,88 @@
249
  const performanceCtx = document.getElementById('performanceChart').getContext('2d');
250
  const performanceChart = new Chart(performanceCtx, {
251
  type: 'line',
252
- data: { labels: [], datasets: [{ label: 'Profit/Loss', data: [], borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', tension: 0.4, fill: true }] },
253
  options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { grid: { color: 'rgba(0, 0, 0, 0.05)', borderDash: [5] }, ticks: { callback: value => '$' + value } }, x: { grid: { display: false } } } }
254
  });
255
 
256
  const allocationCtx = document.getElementById('allocationChart').getContext('2d');
257
  const allocationChart = new Chart(allocationCtx, {
258
  type: 'doughnut',
259
- data: { labels: [], datasets: [{ data: [], backgroundColor: ['#3b82f6', '#10b981', '#8b5cf6', '#f59e0b'], borderWidth: 0 }] },
260
  options: { responsive: true, cutout: '65%', plugins: { legend: { display: false }, tooltip: { callbacks: { label: context => `${context.label}: ${context.parsed}%` } } } }
261
  });
262
 
263
  // API Configuration
264
- const API_KEY = 'EXW9448ytbIhsoaEIqomKqVziRmWCAZ88OhLKmyMoPb7jFTtDnWjO1DFATt3sizsKi2hgn7SxQ06ALTDmU9w'; // Replace with your BingX API key
265
- const API_SECRET = 'PCEzaYObtvi1DB3fmQy3XfFGl9ybRlFI6eVjpjSWykE1mJQ5mEDYlPsuJJ9mXfr2wjaxprMKzBCeitcY7xGdKQ'; // Replace with your BingX secret key
266
  const API_BASE_URL = 'https://open-api.bingx.com';
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  // Generate Signature
269
  function generateSignature(params) {
270
- const sortedParams = Object.fromEntries(Object.entries(params).sort());
 
 
 
 
271
  const queryString = new URLSearchParams(sortedParams).toString();
272
  return CryptoJS.HmacSHA256(queryString, API_SECRET).toString(CryptoJS.enc.Hex);
273
  }
274
 
275
  // Fetch from API
276
  async function fetchFromAPI(endpoint, params = {}) {
277
- params.timestamp = Date.now().toString();
278
- params.recvWindow = '5000'; // 5-second window, per BingX requirements
 
279
  const signature = generateSignature(params);
280
- const url = `${API_BASE_URL}${endpoint}?${new URLSearchParams({ ...params, signature })}`;
281
- const response = await fetch(url, {
282
- method: 'GET',
283
- headers: { 'X-BX-APIKEY': API_KEY }
284
- });
285
- if (!response.ok) throw new Error(`API Error: ${response.status} - ${await response.text()}`);
286
  return response.json();
287
  }
288
 
289
  // Fetch Specific Data
290
  async function fetchBalance() {
291
- const data = await fetchFromAPI('/openApi/swap/v2/account/balance');
292
- return data.data.find(b => b.asset === 'USDT')?.balance || 0; // Adjust based on actual response
 
293
  }
294
 
295
  async function fetchOpenPositions() {
296
- const data = await fetchFromAPI('/openApi/swap/v2/position');
297
- return data.data || []; // Adjust based on actual response structure
 
298
  }
299
 
300
  async function fetchTradeHistory() {
301
- const data = await fetchFromAPI('/openApi/swap/v2/trade/history', { limit: 100 });
302
- return data.data || []; // Adjust based on actual response structure
 
303
  }
304
 
305
  // Helper Functions
@@ -336,12 +399,12 @@
336
  positions.forEach(pos => {
337
  tbody.innerHTML += `
338
  <tr class="border-b border-gray-200 dark:border-gray-700">
339
- <td class="py-4">${pos.symbol || 'N/A'}</td>
340
- <td class="py-4"><span class="${pos.side === 'LONG' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'} px-2 py-1 rounded">${pos.side || 'N/A'}</span></td>
341
- <td class="py-4">${pos.quantity || 0}</td>
342
- <td class="py-4">$${parseFloat(pos.entryPrice || 0).toFixed(2)}</td>
343
- <td class="py-4 font-medium">$${parseFloat(pos.markPrice || 0).toFixed(2)}</td>
344
- <td class="py-4 font-bold ${parseFloat(pos.unrealizedProfit || 0) > 0 ? 'text-green-500' : 'text-red-500'}">$${parseFloat(pos.unrealizedProfit || 0).toFixed(2)}</td>
345
  <td class="py-4"><span class="px-2 py-1 rounded bg-blue-100 text-blue-800">Open</span></td>
346
  <td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td>
347
  </tr>`;
@@ -349,12 +412,12 @@
349
  trades.slice(0, 5).forEach(trade => {
350
  tbody.innerHTML += `
351
  <tr class="border-b border-gray-200 dark:border-gray-700">
352
- <td class="py-4">${trade.symbol || 'N/A'}</td>
353
- <td class="py-4"><span class="${trade.side === 'LONG' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'} px-2 py-1 rounded">${trade.side || 'N/A'}</span></td>
354
- <td class="py-4">${trade.quantity || 0}</td>
355
- <td class="py-4">$${parseFloat(trade.entryPrice || 0).toFixed(2)}</td>
356
- <td class="py-4 font-medium">$${parseFloat(trade.exitPrice || 0).toFixed(2)}</td>
357
- <td class="py-4 font-bold ${parseFloat(trade.realizedProfit || 0) > 0 ? 'text-green-500' : 'text-red-500'}">$${parseFloat(trade.realizedProfit || 0).toFixed(2)}</td>
358
  <td class="py-4"><span class="px-2 py-1 rounded bg-gray-100 text-gray-800">Closed</span></td>
359
  <td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td>
360
  </tr>`;
@@ -375,7 +438,7 @@
375
 
376
  function updatePerformanceChart(trades) {
377
  const monthlyPL = trades.reduce((acc, trade) => {
378
- const month = new Date(trade.closeTime || Date.now()).toLocaleString('default', { month: 'short' });
379
  acc[month] = (acc[month] || 0) + (trade.realizedProfit || 0);
380
  return acc;
381
  }, {});
@@ -385,21 +448,21 @@
385
  }
386
 
387
  function updateAllocationChart(allocation) {
388
- allocationChart.data.labels = allocation.labels.length ? allocation.labels : ['No Data'];
389
- allocationChart.data.datasets[0].data = allocation.data.length ? allocation.data : [100];
390
  allocationChart.update();
391
  const legend = document.getElementById('allocation-legend');
392
  legend.innerHTML = allocation.labels.map((label, i) => `
393
  <div class="mb-3">
394
  <div class="flex justify-between mb-1">
395
  <span class="text-gray-500 dark:text-gray-400 flex items-center">
396
- <span class="h-3 w-3 bg-[${allocationChart.data.datasets[0].backgroundColor[i % 4]}] rounded-full mr-2"></span>
397
- ${label || 'N/A'}
398
  </span>
399
- <span class="font-medium dark:text-white">${allocation.data[i] ? allocation.data[i].toFixed(1) : 0}%</span>
400
  </div>
401
  <div class="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700">
402
- <div class="bg-[${allocationChart.data.datasets[0].backgroundColor[i % 4]}] h-2 rounded-full" style="width: ${allocation.data[i] || 0}%"></div>
403
  </div>
404
  </div>`).join('');
405
  }
@@ -416,9 +479,9 @@
416
  fetchTradeHistory()
417
  ]);
418
 
419
- document.getElementById('total-balance').textContent = `$${parseFloat(balance).toFixed(2)}`;
420
  document.getElementById('open-trades').textContent = positions.length;
421
- const longCount = positions.filter(p => p.side === 'LONG').length;
422
  document.getElementById('trade-types').innerHTML = `<span class="font-medium">${longCount} Long</span><span class="text-gray-500 mx-2 dark:text-gray-400">β€’</span><span class="font-medium">${positions.length - longCount} Short</span>`;
423
  const todayProfit = calculateTodayProfit(trades);
424
  document.getElementById('today-profit').textContent = `$${todayProfit.toFixed(2)}`;
@@ -433,12 +496,11 @@
433
  const allocation = calculatePortfolioAllocation(positions);
434
  updateAllocationChart(allocation);
435
 
436
- const now = new Date().toLocaleString('en-US', { timeZone: 'Asia/Singapore', hour12: false });
437
- document.getElementById('last-sync').textContent = `Last synced: ${now}`;
438
- document.getElementById('allocation-update').textContent = `Last updated: ${now}`;
439
  } catch (error) {
440
  console.error('Error fetching data:', error);
441
- alert(`Failed to sync with BingX API. Please check your API credentials, IP whitelisting, or network. Details: ${error.message}`);
442
  }
443
  }
444
 
@@ -448,6 +510,5 @@
448
  window.addEventListener('load', fetchData);
449
  setInterval(fetchData, 120000); // Refresh every 2 minutes to avoid rate limiting
450
  </script>
451
- <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px; position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle; display:inline-block; margin-right:3px; filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff; text-decoration: underline;" target="_blank">DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=EricSam/bingx-monitoring" style="color: #fff; text-decoration: underline;" target="_blank">Remix</a></p>
452
  </body>
453
  </html>
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=1024"> <!-- Force desktop width -->
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <title>Nakhoda4X Pro - Portfolio Dashboard</title>
8
  <script src="https://cdn.tailwindcss.com"></script>
 
15
  theme: {
16
  extend: {
17
  colors: {
18
+ primary: '#1E90FF', // Updated to Dodger Blue to match image
19
  secondary: '#8b5cf6',
20
  darkBg: '#111827',
21
  darkCard: '#1f2937',
 
25
  }
26
  </script>
27
  <style>
28
+ .trading-card:hover {
29
+ transform: translateY(-5px);
30
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
31
+ }
32
+ .animate-pulse {
33
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
34
+ }
35
+ @keyframes pulse {
36
+ 0%, 100% { opacity: 1; }
37
+ 50% { opacity: 0.5; }
38
+ }
39
+ .coin-animation {
40
+ animation: float 3s ease-in-out infinite;
41
+ }
42
+ @keyframes float {
43
+ 0% { transform: translateY(0px); }
44
+ 50% { transform: translateY(-10px); }
45
+ 100% { transform: translateY(0px); }
46
+ }
47
+ .api-form {
48
+ max-height: 0;
49
+ overflow: hidden;
50
+ transition: max-height 0.3s ease-in-out;
51
+ }
52
+ .api-form.open {
53
+ max-height: 200px; /* Adjust based on content height */
54
+ }
55
  </style>
56
  </head>
57
  <body class="bg-gray-100 dark:bg-darkBg transition-colors duration-300">
 
227
  <!-- API Connection Section -->
228
  <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
229
  <div class="bg-gradient-to-r from-primary to-secondary rounded-2xl p-6">
230
+ <div class="flex items-center mb-4">
231
  <div class="mr-4">
232
  <div class="h-12 w-12 rounded-xl bg-white/30 flex items-center justify-center">
233
  <i class="fas fa-plug text-white text-xl"></i>
 
238
  <p id="last-sync" class="text-blue-100">Last synced: Just now</p>
239
  </div>
240
  </div>
241
+ <!-- API Credentials Form -->
242
+ <button id="toggle-api-form" class="w-full py-2 bg-white rounded-xl text-primary font-bold flex items-center justify-center mb-2">
243
+ <i class="fas fa-cog mr-2"></i> Manage API Credentials
244
+ </button>
245
+ <div id="api-form" class="api-form">
246
+ <input id="api-key-input" type="text" placeholder="Enter API Key" class="w-full p-2 mb-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-white">
247
+ <input id="api-secret-input" type="text" placeholder="Enter API Secret" class="w-full p-2 mb-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-800 dark:text-white">
248
+ <button id="save-api-btn" class="w-full py-2 bg-primary text-white rounded-lg">Save Credentials</button>
249
+ </div>
250
+ <button id="sync-now" class="mt-2 w-full py-3 bg-white rounded-xl text-primary font-bold flex items-center justify-center">
251
  <i class="fas fa-sync mr-2"></i> Sync Now
252
  </button>
253
  </div>
 
281
  const performanceCtx = document.getElementById('performanceChart').getContext('2d');
282
  const performanceChart = new Chart(performanceCtx, {
283
  type: 'line',
284
+ data: { labels: [], datasets: [{ label: 'Profit/Loss', data: [], borderColor: '#1E90FF', backgroundColor: 'rgba(30, 144, 255, 0.1)', tension: 0.4, fill: true }] },
285
  options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { grid: { color: 'rgba(0, 0, 0, 0.05)', borderDash: [5] }, ticks: { callback: value => '$' + value } }, x: { grid: { display: false } } } }
286
  });
287
 
288
  const allocationCtx = document.getElementById('allocationChart').getContext('2d');
289
  const allocationChart = new Chart(allocationCtx, {
290
  type: 'doughnut',
291
+ data: { labels: [], datasets: [{ data: [], backgroundColor: ['#1E90FF', '#10b981', '#8b5cf6', '#f59e0b'], borderWidth: 0 }] },
292
  options: { responsive: true, cutout: '65%', plugins: { legend: { display: false }, tooltip: { callbacks: { label: context => `${context.label}: ${context.parsed}%` } } } }
293
  });
294
 
295
  // API Configuration
296
+ let API_KEY = localStorage.getItem('bingxApiKey') || 'your-actual-api-key'; // Default or stored value
297
+ let API_SECRET = localStorage.getItem('bingxApiSecret') || 'your-actual-secret-key'; // Default or stored value
298
  const API_BASE_URL = 'https://open-api.bingx.com';
299
 
300
+ // Toggle API Form
301
+ const toggleApiFormBtn = document.getElementById('toggle-api-form');
302
+ const apiForm = document.getElementById('api-form');
303
+ toggleApiFormBtn.addEventListener('click', () => {
304
+ apiForm.classList.toggle('open');
305
+ });
306
+
307
+ // Save API Credentials
308
+ const saveApiBtn = document.getElementById('save-api-btn');
309
+ saveApiBtn.addEventListener('click', () => {
310
+ const newApiKey = document.getElementById('api-key-input').value.trim();
311
+ const newApiSecret = document.getElementById('api-secret-input').value.trim();
312
+ if (newApiKey && newApiSecret) {
313
+ API_KEY = newApiKey;
314
+ API_SECRET = newApiSecret;
315
+ localStorage.setItem('bingxApiKey', API_KEY);
316
+ localStorage.setItem('bingxApiSecret', API_SECRET);
317
+ alert('API credentials saved successfully! Syncing data...');
318
+ fetchData(); // Reload data with new credentials
319
+ apiForm.classList.remove('open'); // Close form
320
+ } else {
321
+ alert('Please enter both API Key and Secret.');
322
+ }
323
+ });
324
+
325
  // Generate Signature
326
  function generateSignature(params) {
327
+ // Sort params by key
328
+ const sortedParams = Object.keys(params).sort().reduce((obj, key) => {
329
+ obj[key] = params[key];
330
+ return obj;
331
+ }, {});
332
  const queryString = new URLSearchParams(sortedParams).toString();
333
  return CryptoJS.HmacSHA256(queryString, API_SECRET).toString(CryptoJS.enc.Hex);
334
  }
335
 
336
  // Fetch from API
337
  async function fetchFromAPI(endpoint, params = {}) {
338
+ params.timestamp = Date.now();
339
+ params.recvWindow = 5000; // Optional, check if needed
340
+ // Do not include apiKey in params
341
  const signature = generateSignature(params);
342
+ params.signature = signature;
343
+ const url = `${API_BASE_URL}${endpoint}?${new URLSearchParams(params)}`;
344
+ const response = await fetch(url, { method: 'GET', headers: { 'X-BX-APIKEY': API_KEY } });
345
+ if (!response.ok) throw new Error(`API Error: ${response.statusText}`);
 
 
346
  return response.json();
347
  }
348
 
349
  // Fetch Specific Data
350
  async function fetchBalance() {
351
+ const data = await fetchFromAPI('/openApi/swap/v1/account/balance');
352
+ // Adjust parsing based on actual response structure
353
+ return data.data.balance.find(b => b.asset === 'USDT').free;
354
  }
355
 
356
  async function fetchOpenPositions() {
357
+ const data = await fetchFromAPI('/openApi/swap/v1/position');
358
+ // Adjust parsing based on actual response structure
359
+ return data.data;
360
  }
361
 
362
  async function fetchTradeHistory() {
363
+ const data = await fetchFromAPI('/openApi/swap/v1/trade/history', { limit: 100 });
364
+ // Adjust parsing based on actual response structure
365
+ return data.data;
366
  }
367
 
368
  // Helper Functions
 
399
  positions.forEach(pos => {
400
  tbody.innerHTML += `
401
  <tr class="border-b border-gray-200 dark:border-gray-700">
402
+ <td class="py-4">${pos.symbol}</td>
403
+ <td class="py-4"><span class="${pos.side === 'Long' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'} px-2 py-1 rounded">${pos.side}</span></td>
404
+ <td class="py-4">${pos.quantity}</td>
405
+ <td class="py-4">$${pos.entryPrice.toFixed(2)}</td>
406
+ <td class="py-4 font-medium">$${pos.markPrice.toFixed(2)}</td>
407
+ <td class="py-4 font-bold ${pos.unrealizedProfit > 0 ? 'text-green-500' : 'text-red-500'}">$${pos.unrealizedProfit.toFixed(2)}</td>
408
  <td class="py-4"><span class="px-2 py-1 rounded bg-blue-100 text-blue-800">Open</span></td>
409
  <td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td>
410
  </tr>`;
 
412
  trades.slice(0, 5).forEach(trade => {
413
  tbody.innerHTML += `
414
  <tr class="border-b border-gray-200 dark:border-gray-700">
415
+ <td class="py-4">${trade.symbol}</td>
416
+ <td class="py-4"><span class="${trade.side === 'Long' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'} px-2 py-1 rounded">${trade.side}</span></td>
417
+ <td class="py-4">${trade.quantity}</td>
418
+ <td class="py-4">$${trade.entryPrice.toFixed(2)}</td>
419
+ <td class="py-4 font-medium">$${trade.exitPrice.toFixed(2)}</td>
420
+ <td class="py-4 font-bold ${trade.realizedProfit > 0 ? 'text-green-500' : 'text-red-500'}">$${trade.realizedProfit.toFixed(2)}</td>
421
  <td class="py-4"><span class="px-2 py-1 rounded bg-gray-100 text-gray-800">Closed</span></td>
422
  <td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td>
423
  </tr>`;
 
438
 
439
  function updatePerformanceChart(trades) {
440
  const monthlyPL = trades.reduce((acc, trade) => {
441
+ const month = new Date(trade.closeTime).toLocaleString('default', { month: 'short' });
442
  acc[month] = (acc[month] || 0) + (trade.realizedProfit || 0);
443
  return acc;
444
  }, {});
 
448
  }
449
 
450
  function updateAllocationChart(allocation) {
451
+ allocationChart.data.labels = allocation.labels;
452
+ allocationChart.data.datasets[0].data = allocation.data;
453
  allocationChart.update();
454
  const legend = document.getElementById('allocation-legend');
455
  legend.innerHTML = allocation.labels.map((label, i) => `
456
  <div class="mb-3">
457
  <div class="flex justify-between mb-1">
458
  <span class="text-gray-500 dark:text-gray-400 flex items-center">
459
+ <span class="h-3 w-3 bg-[${allocationChart.data.datasets[0].backgroundColor[i]}] rounded-full mr-2"></span>
460
+ ${label}
461
  </span>
462
+ <span class="font-medium dark:text-white">${allocation.data[i].toFixed(1)}%</span>
463
  </div>
464
  <div class="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700">
465
+ <div class="bg-[${allocationChart.data.datasets[0].backgroundColor[i]}] h-2 rounded-full" style="width: ${allocation.data[i]}%"></div>
466
  </div>
467
  </div>`).join('');
468
  }
 
479
  fetchTradeHistory()
480
  ]);
481
 
482
+ document.getElementById('total-balance').textContent = `$${balance.toFixed(2)}`;
483
  document.getElementById('open-trades').textContent = positions.length;
484
+ const longCount = positions.filter(p => p.side === 'Long').length;
485
  document.getElementById('trade-types').innerHTML = `<span class="font-medium">${longCount} Long</span><span class="text-gray-500 mx-2 dark:text-gray-400">β€’</span><span class="font-medium">${positions.length - longCount} Short</span>`;
486
  const todayProfit = calculateTodayProfit(trades);
487
  document.getElementById('today-profit').textContent = `$${todayProfit.toFixed(2)}`;
 
496
  const allocation = calculatePortfolioAllocation(positions);
497
  updateAllocationChart(allocation);
498
 
499
+ document.getElementById('last-sync').textContent = `Last synced: ${new Date().toLocaleTimeString()}`;
500
+ document.getElementById('allocation-update').textContent = `Last updated: ${new Date().toLocaleTimeString()}`;
 
501
  } catch (error) {
502
  console.error('Error fetching data:', error);
503
+ alert('Failed to sync with BingX API. Please check your API credentials or network.');
504
  }
505
  }
506
 
 
510
  window.addEventListener('load', fetchData);
511
  setInterval(fetchData, 120000); // Refresh every 2 minutes to avoid rate limiting
512
  </script>
 
513
  </body>
514
  </html>