Spaces:
Runtime error
Runtime error
Update index.html
Browse files- index.html +43 -40
index.html
CHANGED
@@ -15,7 +15,7 @@
|
|
15 |
theme: {
|
16 |
extend: {
|
17 |
colors: {
|
18 |
-
primary: '#1E90FF',
|
19 |
secondary: '#8b5cf6',
|
20 |
darkBg: '#111827',
|
21 |
darkCard: '#1f2937',
|
@@ -50,7 +50,7 @@
|
|
50 |
transition: max-height 0.3s ease-in-out;
|
51 |
}
|
52 |
.api-form.open {
|
53 |
-
max-height: 200px;
|
54 |
}
|
55 |
</style>
|
56 |
</head>
|
@@ -315,22 +315,24 @@
|
|
315 |
localStorage.setItem('bingxApiKey', API_KEY);
|
316 |
localStorage.setItem('bingxApiSecret', API_SECRET);
|
317 |
alert('API credentials saved successfully! Syncing data...');
|
318 |
-
fetchData();
|
319 |
-
apiForm.classList.remove('open');
|
320 |
} else {
|
321 |
alert('Please enter both API Key and Secret.');
|
322 |
}
|
323 |
});
|
324 |
|
325 |
// Generate Signature
|
326 |
-
function generateSignature(
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
const
|
333 |
-
|
|
|
|
|
334 |
}
|
335 |
|
336 |
// Fetch from API
|
@@ -338,13 +340,17 @@
|
|
338 |
if (!API_KEY || !API_SECRET) {
|
339 |
throw new Error('API Key and Secret are not set. Please enter your credentials.');
|
340 |
}
|
341 |
-
params.
|
342 |
-
|
343 |
-
|
344 |
-
const
|
345 |
-
|
346 |
-
|
347 |
-
|
|
|
|
|
|
|
|
|
348 |
if (!response.ok) {
|
349 |
const errorText = await response.text();
|
350 |
throw new Error(`API Error ${response.status}: ${errorText}`);
|
@@ -354,34 +360,31 @@
|
|
354 |
|
355 |
// Fetch Specific Data
|
356 |
async function fetchBalance() {
|
357 |
-
const data = await fetchFromAPI('/openApi/swap/
|
358 |
-
|
359 |
-
return data.data.assets.find(b => b.asset === 'USDT')?.walletBalance || 0;
|
360 |
}
|
361 |
|
362 |
async function fetchOpenPositions() {
|
363 |
-
const data = await fetchFromAPI('/openApi/swap/v2/
|
364 |
-
// Updated for V2 response structure
|
365 |
return data.data || [];
|
366 |
}
|
367 |
|
368 |
async function fetchTradeHistory() {
|
369 |
-
const data = await fetchFromAPI('/openApi/swap/v2/
|
370 |
-
// Updated for V2 response structure
|
371 |
return data.data || [];
|
372 |
}
|
373 |
|
374 |
// Helper Functions
|
375 |
function calculateTodayProfit(trades) {
|
376 |
const today = new Date().toDateString();
|
377 |
-
return trades.filter(trade => new Date(trade.
|
378 |
-
.reduce((sum, trade) => sum + (trade.
|
379 |
}
|
380 |
|
381 |
function calculateAdvancedStats(trades) {
|
382 |
let totalProfit = 0, totalLoss = 0, wins = 0;
|
383 |
trades.forEach(trade => {
|
384 |
-
const pl = trade.
|
385 |
if (pl > 0) { totalProfit += pl; wins++; } else { totalLoss += Math.abs(pl); }
|
386 |
});
|
387 |
const profitFactor = totalLoss ? (totalProfit / totalLoss) : 0;
|
@@ -406,7 +409,7 @@
|
|
406 |
tbody.innerHTML += `
|
407 |
<tr class="border-b border-gray-200 dark:border-gray-700">
|
408 |
<td class="py-4">${pos.symbol}</td>
|
409 |
-
<td class="py-4"><span class="${pos.
|
410 |
<td class="py-4">${pos.quantity}</td>
|
411 |
<td class="py-4">$${pos.entryPrice.toFixed(2)}</td>
|
412 |
<td class="py-4 font-medium">$${pos.markPrice.toFixed(2)}</td>
|
@@ -419,11 +422,11 @@
|
|
419 |
tbody.innerHTML += `
|
420 |
<tr class="border-b border-gray-200 dark:border-gray-700">
|
421 |
<td class="py-4">${trade.symbol}</td>
|
422 |
-
<td class="py-4"><span class="${trade.
|
423 |
-
<td class="py-4">${trade.quantity}</td>
|
424 |
-
<td class="py-4">$${trade.entryPrice
|
425 |
-
<td class="py-4 font-medium">$${trade.exitPrice
|
426 |
-
<td class="py-4 font-bold ${trade.
|
427 |
<td class="py-4"><span class="px-2 py-1 rounded bg-gray-100 text-gray-800">Closed</span></td>
|
428 |
<td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td>
|
429 |
</tr>`;
|
@@ -444,8 +447,8 @@
|
|
444 |
|
445 |
function updatePerformanceChart(trades) {
|
446 |
const monthlyPL = trades.reduce((acc, trade) => {
|
447 |
-
const month = new Date(trade.
|
448 |
-
acc[month] = (acc[month] || 0) + (trade.
|
449 |
return acc;
|
450 |
}, {});
|
451 |
performanceChart.data.labels = Object.keys(monthlyPL);
|
@@ -491,7 +494,7 @@
|
|
491 |
|
492 |
document.getElementById('total-balance').textContent = `$${balance.toFixed(2)}`;
|
493 |
document.getElementById('open-trades').textContent = positions.length;
|
494 |
-
const longCount = positions.filter(p => p.
|
495 |
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>`;
|
496 |
const todayProfit = calculateTodayProfit(trades);
|
497 |
document.getElementById('today-profit').textContent = `$${todayProfit.toFixed(2)}`;
|
@@ -510,7 +513,7 @@
|
|
510 |
document.getElementById('allocation-update').textContent = `Last updated: ${new Date().toLocaleTimeString()}`;
|
511 |
} catch (error) {
|
512 |
console.error('Error fetching data:', error);
|
513 |
-
alert(`Failed to sync with BingX API: ${error.message}. Please check your credentials, network, or BingX API documentation.`);
|
514 |
}
|
515 |
}
|
516 |
|
@@ -518,7 +521,7 @@
|
|
518 |
document.getElementById('refresh-btn').addEventListener('click', fetchData);
|
519 |
document.getElementById('sync-now').addEventListener('click', fetchData);
|
520 |
window.addEventListener('load', fetchData);
|
521 |
-
setInterval(fetchData, 120000); // Refresh every 2 minutes
|
522 |
</script>
|
523 |
</body>
|
524 |
</html>
|
|
|
15 |
theme: {
|
16 |
extend: {
|
17 |
colors: {
|
18 |
+
primary: '#1E90FF',
|
19 |
secondary: '#8b5cf6',
|
20 |
darkBg: '#111827',
|
21 |
darkCard: '#1f2937',
|
|
|
50 |
transition: max-height 0.3s ease-in-out;
|
51 |
}
|
52 |
.api-form.open {
|
53 |
+
max-height: 200px;
|
54 |
}
|
55 |
</style>
|
56 |
</head>
|
|
|
315 |
localStorage.setItem('bingxApiKey', API_KEY);
|
316 |
localStorage.setItem('bingxApiSecret', API_SECRET);
|
317 |
alert('API credentials saved successfully! Syncing data...');
|
318 |
+
fetchData();
|
319 |
+
apiForm.classList.remove('open');
|
320 |
} else {
|
321 |
alert('Please enter both API Key and Secret.');
|
322 |
}
|
323 |
});
|
324 |
|
325 |
// Generate Signature
|
326 |
+
function generateSignature(apiSecret, paramsStr) {
|
327 |
+
return CryptoJS.HmacSHA256(paramsStr, apiSecret).toString(CryptoJS.enc.Hex);
|
328 |
+
}
|
329 |
+
|
330 |
+
// Parse Parameters
|
331 |
+
function parseParams(params) {
|
332 |
+
const sortedKeys = Object.keys(params).sort();
|
333 |
+
const paramPairs = sortedKeys.map(key => `${key}=${params[key]}`);
|
334 |
+
const paramsStr = paramPairs.join('&');
|
335 |
+
return paramsStr ? `${paramsStr}×tamp=${Date.now()}` : `timestamp=${Date.now()}`;
|
336 |
}
|
337 |
|
338 |
// Fetch from API
|
|
|
340 |
if (!API_KEY || !API_SECRET) {
|
341 |
throw new Error('API Key and Secret are not set. Please enter your credentials.');
|
342 |
}
|
343 |
+
params.recvWindow = params.recvWindow || 5000; // Default to 5000ms if not provided
|
344 |
+
const paramsStr = parseParams(params);
|
345 |
+
const signature = generateSignature(API_SECRET, paramsStr);
|
346 |
+
const url = `${API_BASE_URL}${endpoint}?${paramsStr}&signature=${signature}`;
|
347 |
+
const response = await fetch(url, {
|
348 |
+
method: 'GET',
|
349 |
+
headers: {
|
350 |
+
'X-BX-APIKEY': API_KEY,
|
351 |
+
'Content-Type': 'application/json'
|
352 |
+
}
|
353 |
+
});
|
354 |
if (!response.ok) {
|
355 |
const errorText = await response.text();
|
356 |
throw new Error(`API Error ${response.status}: ${errorText}`);
|
|
|
360 |
|
361 |
// Fetch Specific Data
|
362 |
async function fetchBalance() {
|
363 |
+
const data = await fetchFromAPI('/openApi/swap/v3/user/balance');
|
364 |
+
return data.data.find(b => b.asset === 'USDT')?.walletBalance || 0;
|
|
|
365 |
}
|
366 |
|
367 |
async function fetchOpenPositions() {
|
368 |
+
const data = await fetchFromAPI('/openApi/swap/v2/user/positions', { symbol: 'BTC-USDT' }); // Default symbol, adjust as needed
|
|
|
369 |
return data.data || [];
|
370 |
}
|
371 |
|
372 |
async function fetchTradeHistory() {
|
373 |
+
const data = await fetchFromAPI('/openApi/swap/v2/user/income', { limit: 100 });
|
|
|
374 |
return data.data || [];
|
375 |
}
|
376 |
|
377 |
// Helper Functions
|
378 |
function calculateTodayProfit(trades) {
|
379 |
const today = new Date().toDateString();
|
380 |
+
return trades.filter(trade => new Date(trade.time).toDateString() === today)
|
381 |
+
.reduce((sum, trade) => sum + (trade.income || 0), 0);
|
382 |
}
|
383 |
|
384 |
function calculateAdvancedStats(trades) {
|
385 |
let totalProfit = 0, totalLoss = 0, wins = 0;
|
386 |
trades.forEach(trade => {
|
387 |
+
const pl = trade.income || 0;
|
388 |
if (pl > 0) { totalProfit += pl; wins++; } else { totalLoss += Math.abs(pl); }
|
389 |
});
|
390 |
const profitFactor = totalLoss ? (totalProfit / totalLoss) : 0;
|
|
|
409 |
tbody.innerHTML += `
|
410 |
<tr class="border-b border-gray-200 dark:border-gray-700">
|
411 |
<td class="py-4">${pos.symbol}</td>
|
412 |
+
<td class="py-4"><span class="${pos.positionSide === 'LONG' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'} px-2 py-1 rounded">${pos.positionSide}</span></td>
|
413 |
<td class="py-4">${pos.quantity}</td>
|
414 |
<td class="py-4">$${pos.entryPrice.toFixed(2)}</td>
|
415 |
<td class="py-4 font-medium">$${pos.markPrice.toFixed(2)}</td>
|
|
|
422 |
tbody.innerHTML += `
|
423 |
<tr class="border-b border-gray-200 dark:border-gray-700">
|
424 |
<td class="py-4">${trade.symbol}</td>
|
425 |
+
<td class="py-4"><span class="${trade.positionSide === 'LONG' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'} px-2 py-1 rounded">${trade.positionSide}</span></td>
|
426 |
+
<td class="py-4">${trade.quantity || 0}</td>
|
427 |
+
<td class="py-4">$${trade.entryPrice?.toFixed(2) || '0.00'}</td>
|
428 |
+
<td class="py-4 font-medium">$${trade.exitPrice?.toFixed(2) || '0.00'}</td>
|
429 |
+
<td class="py-4 font-bold ${trade.income > 0 ? 'text-green-500' : 'text-red-500'}">$${trade.income?.toFixed(2) || '0.00'}</td>
|
430 |
<td class="py-4"><span class="px-2 py-1 rounded bg-gray-100 text-gray-800">Closed</span></td>
|
431 |
<td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td>
|
432 |
</tr>`;
|
|
|
447 |
|
448 |
function updatePerformanceChart(trades) {
|
449 |
const monthlyPL = trades.reduce((acc, trade) => {
|
450 |
+
const month = new Date(trade.time).toLocaleString('default', { month: 'short' });
|
451 |
+
acc[month] = (acc[month] || 0) + (trade.income || 0);
|
452 |
return acc;
|
453 |
}, {});
|
454 |
performanceChart.data.labels = Object.keys(monthlyPL);
|
|
|
494 |
|
495 |
document.getElementById('total-balance').textContent = `$${balance.toFixed(2)}`;
|
496 |
document.getElementById('open-trades').textContent = positions.length;
|
497 |
+
const longCount = positions.filter(p => p.positionSide === 'LONG').length;
|
498 |
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>`;
|
499 |
const todayProfit = calculateTodayProfit(trades);
|
500 |
document.getElementById('today-profit').textContent = `$${todayProfit.toFixed(2)}`;
|
|
|
513 |
document.getElementById('allocation-update').textContent = `Last updated: ${new Date().toLocaleTimeString()}`;
|
514 |
} catch (error) {
|
515 |
console.error('Error fetching data:', error);
|
516 |
+
alert(`Failed to sync with BingX API: ${error.message}. Please check your credentials, permissions, network, or BingX API documentation.`);
|
517 |
}
|
518 |
}
|
519 |
|
|
|
521 |
document.getElementById('refresh-btn').addEventListener('click', fetchData);
|
522 |
document.getElementById('sync-now').addEventListener('click', fetchData);
|
523 |
window.addEventListener('load', fetchData);
|
524 |
+
setInterval(fetchData, 120000); // Refresh every 2 minutes, respecting rate limit of 5/s
|
525 |
</script>
|
526 |
</body>
|
527 |
</html>
|