Spaces:
Runtime error
Runtime error
Update index.html
Browse files- 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 {
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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: '#
|
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: ['#
|
260 |
options: { responsive: true, cutout: '65%', plugins: { legend: { display: false }, tooltip: { callbacks: { label: context => `${context.label}: ${context.parsed}%` } } } }
|
261 |
});
|
262 |
|
263 |
// API Configuration
|
264 |
-
|
265 |
-
|
266 |
const API_BASE_URL = 'https://open-api.bingx.com';
|
267 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
// Generate Signature
|
269 |
function generateSignature(params) {
|
270 |
-
|
|
|
|
|
|
|
|
|
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()
|
278 |
-
params.recvWindow =
|
|
|
279 |
const signature = generateSignature(params);
|
280 |
-
|
281 |
-
const
|
282 |
-
|
283 |
-
|
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/
|
292 |
-
|
|
|
293 |
}
|
294 |
|
295 |
async function fetchOpenPositions() {
|
296 |
-
const data = await fetchFromAPI('/openApi/swap/
|
297 |
-
|
|
|
298 |
}
|
299 |
|
300 |
async function fetchTradeHistory() {
|
301 |
-
const data = await fetchFromAPI('/openApi/swap/
|
302 |
-
|
|
|
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
|
340 |
-
<td class="py-4"><span class="${pos.side === '
|
341 |
-
<td class="py-4">${pos.quantity
|
342 |
-
<td class="py-4">$${
|
343 |
-
<td class="py-4 font-medium">$${
|
344 |
-
<td class="py-4 font-bold ${
|
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
|
353 |
-
<td class="py-4"><span class="${trade.side === '
|
354 |
-
<td class="py-4">${trade.quantity
|
355 |
-
<td class="py-4">$${
|
356 |
-
<td class="py-4 font-medium">$${
|
357 |
-
<td class="py-4 font-bold ${
|
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
|
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
|
389 |
-
allocationChart.data.datasets[0].data = allocation.data
|
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
|
397 |
-
${label
|
398 |
</span>
|
399 |
-
<span class="font-medium dark:text-white">${allocation.data[i]
|
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
|
403 |
</div>
|
404 |
</div>`).join('');
|
405 |
}
|
@@ -416,9 +479,9 @@
|
|
416 |
fetchTradeHistory()
|
417 |
]);
|
418 |
|
419 |
-
document.getElementById('total-balance').textContent = `$${
|
420 |
document.getElementById('open-trades').textContent = positions.length;
|
421 |
-
const longCount = positions.filter(p => p.side === '
|
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 |
-
|
437 |
-
document.getElementById('
|
438 |
-
document.getElementById('allocation-update').textContent = `Last updated: ${now}`;
|
439 |
} catch (error) {
|
440 |
console.error('Error fetching data:', error);
|
441 |
-
alert(
|
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>
|