EricSam commited on
Commit
0659cc1
Β·
verified Β·
1 Parent(s): 955299c

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -546
app.py DELETED
@@ -1,546 +0,0 @@
1
- import gradio as gr
2
- from transformers import pipeline
3
- import hmac
4
- import hashlib
5
- import requests
6
- import time
7
- from datetime import datetime
8
- import os
9
- from flask import Flask, request, jsonify
10
-
11
- # Initialize Flask app for proxy (embedded within Gradio)
12
- app = Flask(__name__)
13
-
14
- # Configuration (use environment variables for security)
15
- API_BASE_URL = "https://open-api.bingx.com"
16
- API_KEY = os.getenv('BINGX_API_KEY', "EXW9448ytbIhsoaEIqomKqVziRmWCAZ88OhLKmyMoPb7jFTtDnWjO1DFATt3sizsKi2hgn7SxQ06ALTDmU9w") # Replace with environment variable or secure storage
17
- API_SECRET = os.getenv('BINGX_API_SECRET', "PCEzaYObtvi1DB3fmQy3XfFGl9ybRlFI6eVjpjSWykE1mJQ5mEDYlPsuJJ9mXfr2wjaxprMKzBCeitcY7xGdKQ") # Replace with environment variable or secure storage
18
-
19
- # Generate Signature
20
- def generate_signature(api_secret, params_str):
21
- return hmac.new(api_secret.encode("utf-8"), params_str.encode("utf-8"), hashlib.sha256).hexdigest()
22
-
23
- # Parse Parameters
24
- def parse_params(params):
25
- sorted_keys = sorted(params.keys())
26
- param_pairs = [f"{key}={params[key]}" for key in sorted_keys]
27
- params_str = "&".join(param_pairs)
28
- return params_str + "&timestamp=" + str(int(time.time() * 1000)) if params_str else "timestamp=" + str(int(time.time() * 1000))
29
-
30
- # Proxy Endpoint for BingX API
31
- @app.route('/api/proxy', methods=['GET'])
32
- def proxy_api():
33
- try:
34
- endpoint = request.args.get('endpoint')
35
- params = {key: value for key, value in request.args.items() if key != 'endpoint'}
36
- params['recvWindow'] = params.get('recvWindow', '5000')
37
-
38
- params_str = parse_params(params)
39
- signature = generate_signature(API_SECRET, params_str)
40
- url = f"{API_BASE_URL}{endpoint}?{params_str}&signature={signature}"
41
-
42
- headers = {
43
- 'X-BX-APIKEY': API_KEY,
44
- 'Content-Type': 'application/json'
45
- }
46
-
47
- response = requests.get(url, headers=headers)
48
- response.raise_for_status()
49
- return jsonify(response.json())
50
-
51
- except requests.exceptions.RequestException as e:
52
- return jsonify({"error": str(e), "status_code": e.response.status_code if e.response else 500}), 500
53
- except Exception as e:
54
- return jsonify({"error": str(e)}), 500
55
-
56
- # Gradio Image Classification Setup
57
- pipeline = pipeline(task="image-classification", model="julien-c/hotdog-not-hotdog")
58
-
59
- def predict(input_img):
60
- predictions = pipeline(input_img)
61
- return input_img, {p["label"]: p["score"] for p in predictions}
62
-
63
- # Gradio Interface
64
- gradio_app = gr.Interface(
65
- predict,
66
- inputs=gr.Image(label="Select hot dog candidate", sources=['upload', 'webcam'], type="pil"),
67
- outputs=[gr.Image(label="Processed Image"), gr.Label(label="Result", num_top_classes=2)],
68
- title="Hot Dog? Or Not?",
69
- )
70
-
71
- # Custom JavaScript for BingX Dashboard
72
- bingx_html = """
73
- <!DOCTYPE html>
74
- <html lang="en">
75
- <head>
76
- <meta charset="UTF-8">
77
- <meta name="viewport" content="width=1024">
78
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
79
- <title>Nakhoda4X Pro - Portfolio Dashboard</title>
80
- <script src="https://cdn.tailwindcss.com"></script>
81
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
82
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
83
- <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
84
- <script>
85
- tailwind.config = {
86
- darkMode: 'class',
87
- theme: {
88
- extend: {
89
- colors: {
90
- primary: '#1E90FF',
91
- secondary: '#8b5cf6',
92
- darkBg: '#111827',
93
- darkCard: '#1f2937',
94
- }
95
- }
96
- }
97
- }
98
- </script>
99
- <style>
100
- .trading-card:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); }
101
- .animate-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
102
- @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
103
- .coin-animation { animation: float 3s ease-in-out infinite; }
104
- @keyframes float { 0% { transform: translateY(0px); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0px); } }
105
- .api-form { max-height: 0; overflow: hidden; transition: max-height 0.3s ease-in-out; }
106
- .api-form.open { max-height: 200px; }
107
- </style>
108
- </head>
109
- <body class="bg-gray-100 dark:bg-darkBg transition-colors duration-300">
110
- <header class="bg-white dark:bg-darkCard shadow-sm">
111
- <div class="container mx-auto px-4 py-4 flex justify-between items-center">
112
- <div class="flex items-center">
113
- <div class="bg-primary w-10 h-10 rounded-xl flex items-center justify-center mr-3 coin-animation">
114
- <i class="fas fa-chart-line text-white text-xl"></i>
115
- </div>
116
- <h1 class="text-2xl font-bold text-gray-800 dark:text-white">Nakhoda4X <span class="text-primary">Pro</span></h1>
117
- </div>
118
- <div class="flex items-center space-x-6">
119
- <button id="theme-toggle" class="text-gray-600 dark:text-gray-300">
120
- <i class="fas fa-moon dark:hidden"></i>
121
- <i class="fas fa-sun hidden dark:block"></i>
122
- </button>
123
- <div class="relative">
124
- <i class="fas fa-bell text-gray-600 dark:text-gray-300 text-xl"></i>
125
- <span class="absolute -top-2 -right-2 bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center">3</span>
126
- </div>
127
- <div class="flex items-center">
128
- <div class="mr-3 text-right">
129
- <p class="font-semibold text-gray-800 dark:text-white">SahabatPipHijau</p>
130
- <p class="text-sm text-gray-500">Premium Account</p>
131
- </div>
132
- <div class="h-10 w-10 rounded-full bg-gradient-to-r from-primary to-secondary flex items-center justify-center text-white font-bold">
133
- J
134
- </div>
135
- </div>
136
- </div>
137
- </div>
138
- </header>
139
- <main class="container mx-auto px-4 py-8">
140
- <div class="flex justify-between items-center mb-8">
141
- <div>
142
- <h2 class="text-3xl font-bold text-gray-800 dark:text-white">Trading Dashboard</h2>
143
- <p class="text-gray-500 mt-1 dark:text-gray-400">Connected to BingX via API</p>
144
- </div>
145
- <div class="flex space-x-4">
146
- <button id="refresh-btn" class="px-4 py-2 bg-white dark:bg-darkCard rounded-lg border border-gray-200 dark:border-gray-700 text-gray-700 dark:text-gray-300 flex items-center">
147
- <i class="fas fa-sync mr-2"></i> Refresh
148
- </button>
149
- <button class="px-4 py-2 bg-primary text-white rounded-lg flex items-center">
150
- <i class="fas fa-plus mr-2"></i> New Trade
151
- </button>
152
- </div>
153
- </div>
154
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
155
- <div class="trading-card bg-white dark:bg-darkCard rounded-2xl shadow-md p-6 transition-all duration-300">
156
- <div class="flex justify-between">
157
- <div>
158
- <p class="text-gray-500 dark:text-gray-400">Total Balance</p>
159
- <h3 id="total-balance" class="text-2xl font-bold mt-2 dark:text-white">$0.00</h3>
160
- </div>
161
- <div class="w-12 h-12 rounded-xl bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center">
162
- <i class="fas fa-wallet text-blue-500 text-xl"></i>
163
- </div>
164
- </div>
165
- <div id="balance-change" class="mt-4 flex items-center text-green-500">
166
- <i class="fas fa-caret-up mr-1"></i>
167
- <span class="font-medium">0.0%</span>
168
- <span class="text-gray-500 ml-2 dark:text-gray-400">last 24h</span>
169
- </div>
170
- </div>
171
- <div class="trading-card bg-white dark:bg-darkCard rounded-2xl shadow-md p-6 transition-all duration-300">
172
- <div class="flex justify-between">
173
- <div>
174
- <p class="text-gray-500 dark:text-gray-400">Open Trades</p>
175
- <h3 id="open-trades" class="text-2xl font-bold mt-2 dark:text-white">0</h3>
176
- </div>
177
- <div class="w-12 h-12 rounded-xl bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center">
178
- <i class="fas fa-exchange-alt text-purple-500 text-xl"></i>
179
- </div>
180
- </div>
181
- <div id="trade-types" class="mt-4 flex items-center text-blue-500">
182
- <span class="font-medium">0 Long</span>
183
- <span class="text-gray-500 mx-2 dark:text-gray-400">β€’</span>
184
- <span class="font-medium">0 Short</span>
185
- </div>
186
- </div>
187
- <div class="trading-card bg-white dark:bg-darkCard rounded-2xl shadow-md p-6 transition-all duration-300">
188
- <div class="flex justify-between">
189
- <div>
190
- <p class="text-gray-500 dark:text-gray-400">Today's Profit</p>
191
- <h3 id="today-profit" class="text-2xl font-bold mt-2 dark:text-white">$0.00</h3>
192
- </div>
193
- <div class="w-12 h-12 rounded-xl bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
194
- <i class="fas fa-chart-bar text-green-500 text-xl"></i>
195
- </div>
196
- </div>
197
- <div id="profit-change" class="mt-4 flex items-center text-green-500">
198
- <i class="fas fa-caret-up mr-1"></i>
199
- <span class="font-medium">0.0%</span>
200
- <span class="text-gray-500 ml-2 dark:text-gray-400">vs yesterday</span>
201
- </div>
202
- </div>
203
- <div class="trading-card bg-white dark:bg-darkCard rounded-2xl shadow-md p-6 transition-all duration-300">
204
- <div class="flex justify-between">
205
- <div>
206
- <p class="text-gray-500 dark:text-gray-400">Risk Exposure</p>
207
- <h3 id="risk-exposure" class="text-2xl font-bold mt-2 dark:text-white">Low</h3>
208
- </div>
209
- <div class="w-12 h-12 rounded-xl bg-yellow-100 dark:bg-yellow-900/30 flex items-center justify-center">
210
- <i class="fas fa-exclamation-triangle text-yellow-500 text-xl"></i>
211
- </div>
212
- </div>
213
- <div id="exposure-percent" class="mt-4 flex items-center text-gray-500 dark:text-gray-400">
214
- <span class="font-medium">0%</span>
215
- <span class="ml-2">of balance</span>
216
- </div>
217
- </div>
218
- </div>
219
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8">
220
- <div class="lg:col-span-2 bg-white dark:bg-darkCard rounded-2xl shadow-md p-6">
221
- <div class="flex justify-between items-center mb-6">
222
- <h3 class="text-lg font-bold text-gray-800 dark:text-white">Monthly Performance</h3>
223
- <div class="flex space-x-3">
224
- <button class="px-3 py-1 rounded-lg text-sm bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-white">1M</button>
225
- <button class="px-3 py-1 rounded-lg text-sm bg-primary text-white">3M</button>
226
- <button class="px-3 py-1 rounded-lg text-sm bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-white">6M</button>
227
- <button class="px-3 py-1 rounded-lg text-sm bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-white">1Y</button>
228
- </div>
229
- </div>
230
- <canvas id="performanceChart"></canvas>
231
- </div>
232
- <div class="bg-white dark:bg-darkCard rounded-2xl shadow-md p-6">
233
- <h3 class="text-lg font-bold text-gray-800 dark:text-white mb-6">Advanced Statistics</h3>
234
- <div id="advanced-stats" class="space-y-5">
235
- <!-- Dynamically populated -->
236
- </div>
237
- </div>
238
- </div>
239
- <div class="bg-white dark:bg-darkCard rounded-2xl shadow-md p-6 mb-8">
240
- <div class="flex justify-between items-center mb-6">
241
- <h3 class="text-lg font-bold text-gray-800 dark:text-white">Trading Activity</h3>
242
- <div class="flex space-x-3">
243
- <button class="px-3 py-1 rounded-lg text-sm bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-white">All</button>
244
- <button class="px-3 py-1 rounded-lg text-sm bg-primary text-white">Open</button>
245
- <button class="px-3 py-1 rounded-lg text-sm bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-white">Closed</button>
246
- </div>
247
- </div>
248
- <div class="overflow-x-auto">
249
- <table class="w-full">
250
- <thead>
251
- <tr class="text-left text-gray-500 dark:text-gray-400 text-sm">
252
- <th class="pb-4">Symbol</th>
253
- <th class="pb-4">Type</th>
254
- <th class="pb-4">Size</th>
255
- <th class="pb-4">Entry Price</th>
256
- <th class="pb-4">Current Price</th>
257
- <th class="pb-4">Profit/Loss</th>
258
- <th class="pb-4">Status</th>
259
- <th class="pb-4"></th>
260
- </tr>
261
- </thead>
262
- <tbody id="trading-table-body" class="text-sm">
263
- <!-- Dynamically populated -->
264
- </tbody>
265
- </table>
266
- </div>
267
- </div>
268
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
269
- <div class="bg-gradient-to-r from-primary to-secondary rounded-2xl p-6">
270
- <div class="flex items-center mb-4">
271
- <div class="mr-4">
272
- <div class="h-12 w-12 rounded-xl bg-white/30 flex items-center justify-center">
273
- <i class="fas fa-plug text-white text-xl"></i>
274
- </div>
275
- </div>
276
- <div>
277
- <h3 class="text-lg font-bold text-white">BingX API Connected</h3>
278
- <p id="last-sync" class="text-blue-100">Last synced: Just now</p>
279
- </div>
280
- </div>
281
- <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">
282
- <i class="fas fa-cog mr-2"></i> Manage API Credentials
283
- </button>
284
- <div id="api-form" class="api-form">
285
- <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">
286
- <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">
287
- <button id="save-api-btn" class="w-full py-2 bg-primary text-white rounded-lg">Save Credentials</button>
288
- </div>
289
- <button id="sync-now" class="mt-2 w-full py-3 bg-white rounded-xl text-primary font-bold flex items-center justify-center">
290
- <i class="fas fa-sync mr-2"></i> Sync Now
291
- </button>
292
- </div>
293
- <div class="lg:col-span-2 bg-white dark:bg-darkCard rounded-2xl shadow-md p-6">
294
- <div class="flex justify-between items-center mb-6">
295
- <h3 class="text-lg font-bold text-gray-800 dark:text-white">Portfolio Allocation</h3>
296
- <div>
297
- <span id="allocation-update" class="text-gray-500 dark:text-gray-400 text-sm">Last updated: Just now</span>
298
- </div>
299
- </div>
300
- <div class="flex items-center">
301
- <div class="w-40 h-40 mr-8">
302
- <canvas id="allocationChart"></canvas>
303
- </div>
304
- <div id="allocation-legend" class="flex-grow">
305
- <!-- Dynamically populated -->
306
- </div>
307
- </div>
308
- </div>
309
- </div>
310
- </main>
311
- <script>
312
- // Dark Mode Toggle
313
- const themeToggle = document.getElementById('theme-toggle');
314
- themeToggle.addEventListener('click', () => {
315
- document.documentElement.classList.toggle('dark');
316
- });
317
-
318
- // Chart Initialization
319
- const performanceCtx = document.getElementById('performanceChart').getContext('2d');
320
- const performanceChart = new Chart(performanceCtx, {
321
- type: 'line',
322
- data: { labels: [], datasets: [{ label: 'Profit/Loss', data: [], borderColor: '#1E90FF', backgroundColor: 'rgba(30, 144, 255, 0.1)', tension: 0.4, fill: true }] },
323
- 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 } } } }
324
- });
325
-
326
- const allocationCtx = document.getElementById('allocationChart').getContext('2d');
327
- const allocationChart = new Chart(allocationCtx, {
328
- type: 'doughnut',
329
- data: { labels: [], datasets: [{ data: [], backgroundColor: ['#1E90FF', '#10b981', '#8b5cf6', '#f59e0b'], borderWidth: 0 }] },
330
- options: { responsive: true, cutout: '65%', plugins: { legend: { display: false }, tooltip: { callbacks: { label: context => `${context.label}: ${context.parsed}%` } } } }
331
- });
332
-
333
- let API_KEY = localStorage.getItem('bingxApiKey') || '';
334
- let API_SECRET = localStorage.getItem('bingxApiSecret') || '';
335
- const PROXY_URL = '/api/proxy';
336
-
337
- const toggleApiFormBtn = document.getElementById('toggle-api-form');
338
- const apiForm = document.getElementById('api-form');
339
- toggleApiFormBtn.addEventListener('click', () => {
340
- apiForm.classList.toggle('open');
341
- });
342
-
343
- const saveApiBtn = document.getElementById('save-api-btn');
344
- saveApiBtn.addEventListener('click', () => {
345
- const newApiKey = document.getElementById('api-key-input').value.trim();
346
- const newApiSecret = document.getElementById('api-secret-input').value.trim();
347
- if (newApiKey && newApiSecret) {
348
- API_KEY = newApiKey;
349
- API_SECRET = newApiSecret;
350
- localStorage.setItem('bingxApiKey', API_KEY);
351
- localStorage.setItem('bingxApiSecret', API_SECRET);
352
- alert('API credentials saved successfully! Syncing data...');
353
- fetchData();
354
- apiForm.classList.remove('open');
355
- } else {
356
- alert('Please enter both API Key and Secret.');
357
- }
358
- });
359
-
360
- async function fetchFromProxy(endpoint, params = {}) {
361
- if (!API_KEY || !API_SECRET) {
362
- throw new Error('API Key and Secret are not set. Please enter your credentials.');
363
- }
364
- params.recvWindow = params.recvWindow || 5000;
365
- const url = `${PROXY_URL}?endpoint=${encodeURIComponent(endpoint)}&${new URLSearchParams(params).toString()}`;
366
- const response = await fetch(url);
367
- if (!response.ok) {
368
- const error = await response.json();
369
- throw new Error(`API Error ${response.status}: ${error.error || 'Unknown error'}`);
370
- }
371
- return response.json();
372
- }
373
-
374
- async function fetchBalance() {
375
- const data = await fetchFromProxy('/openApi/swap/v3/user/balance');
376
- return data.data.find(b => b.asset === 'USDT')?.walletBalance || 0;
377
- }
378
-
379
- async function fetchOpenPositions() {
380
- const data = await fetchFromProxy('/openApi/swap/v2/user/positions', { symbol: 'BTC-USDT' });
381
- return data.data || [];
382
- }
383
-
384
- async function fetchTradeHistory() {
385
- const data = await fetchFromProxy('/openApi/swap/v2/user/income', { limit: 100 });
386
- return data.data || [];
387
- }
388
-
389
- function calculateTodayProfit(trades) {
390
- const today = new Date().toDateString();
391
- return trades.filter(trade => new Date(trade.time).toDateString() === today)
392
- .reduce((sum, trade) => sum + (trade.income || 0), 0);
393
- }
394
-
395
- function calculateAdvancedStats(trades) {
396
- let totalProfit = 0, totalLoss = 0, wins = 0;
397
- trades.forEach(trade => {
398
- const pl = trade.income || 0;
399
- if (pl > 0) { totalProfit += pl; wins++; } else { totalLoss += Math.abs(pl); }
400
- });
401
- const profitFactor = totalLoss ? (totalProfit / totalLoss) : 0;
402
- const winRate = trades.length ? (wins / trades.length * 100) : 0;
403
- return { profitFactor: profitFactor.toFixed(2), winRate: winRate.toFixed(1) };
404
- }
405
-
406
- function calculatePortfolioAllocation(positions) {
407
- const totalValue = positions.reduce((sum, pos) => sum + (pos.positionValue || 0), 0);
408
- const bySymbol = positions.reduce((acc, pos) => {
409
- acc[pos.symbol] = (acc[pos.symbol] || 0) + (pos.positionValue || 0);
410
- return acc;
411
- }, {});
412
- return { labels: Object.keys(bySymbol), data: Object.values(bySymbol).map(val => totalValue ? (val / totalValue * 100) : 0) };
413
- }
414
-
415
- function updateTradingTable(positions, trades) {
416
- const tbody = document.getElementById('trading-table-body');
417
- tbody.innerHTML = '';
418
- positions.forEach(pos => {
419
- tbody.innerHTML += `
420
- <tr class="border-b border-gray-200 dark:border-gray-700">
421
- <td class="py-4">${pos.symbol}</td>
422
- <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>
423
- <td class="py-4">${pos.quantity}</td>
424
- <td class="py-4">$${pos.entryPrice.toFixed(2)}</td>
425
- <td class="py-4 font-medium">$${pos.markPrice.toFixed(2)}</td>
426
- <td class="py-4 font-bold ${pos.unrealizedProfit > 0 ? 'text-green-500' : 'text-red-500'}">$${pos.unrealizedProfit.toFixed(2)}</td>
427
- <td class="py-4"><span class="px-2 py-1 rounded bg-blue-100 text-blue-800">Open</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>`;
430
- });
431
- trades.slice(0, 5).forEach(trade => {
432
- tbody.innerHTML += `
433
- <tr class="border-b border-gray-200 dark:border-gray-700">
434
- <td class="py-4">${trade.symbol}</td>
435
- <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>
436
- <td class="py-4">${trade.quantity || 0}</td>
437
- <td class="py-4">$${trade.entryPrice?.toFixed(2) || '0.00'}</td>
438
- <td class="py-4 font-medium">$${trade.exitPrice?.toFixed(2) || '0.00'}</td>
439
- <td class="py-4 font-bold ${trade.income > 0 ? 'text-green-500' : 'text-red-500'}">$${trade.income?.toFixed(2) || '0.00'}</td>
440
- <td class="py-4"><span class="px-2 py-1 rounded bg-gray-100 text-gray-800">Closed</span></td>
441
- <td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td>
442
- </tr>`;
443
- });
444
- }
445
-
446
- function updateAdvancedStats(stats) {
447
- document.getElementById('advanced-stats').innerHTML = `
448
- <div>
449
- <div class="flex justify-between mb-1"><span class="text-gray-500 dark:text-gray-400">Profit Factor</span><span class="font-bold text-green-500">${stats.profitFactor}</span></div>
450
- <div class="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700"><div class="bg-green-600 h-2 rounded-full" style="width: ${Math.min(stats.profitFactor * 25, 100)}%"></div></div>
451
- </div>
452
- <div>
453
- <div class="flex justify-between mb-1"><span class="text-gray-500 dark:text-gray-400">Win Rate</span><span class="font-bold text-purple-500">${stats.winRate}%</span></div>
454
- <div class="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700"><div class="bg-purple-600 h-2 rounded-full" style="width: ${stats.winRate}%"></div></div>
455
- </div>`;
456
- }
457
-
458
- function updatePerformanceChart(trades) {
459
- const monthlyPL = trades.reduce((acc, trade) => {
460
- const month = new Date(trade.time).toLocaleString('default', { month: 'short' });
461
- acc[month] = (acc[month] || 0) + (trade.income || 0);
462
- return acc;
463
- }, {});
464
- performanceChart.data.labels = Object.keys(monthlyPL);
465
- performanceChart.data.datasets[0].data = Object.values(monthlyPL);
466
- performanceChart.update();
467
- }
468
-
469
- function updateAllocationChart(allocation) {
470
- allocationChart.data.labels = allocation.labels;
471
- allocationChart.data.datasets[0].data = allocation.data;
472
- allocationChart.update();
473
- const legend = document.getElementById('allocation-legend');
474
- legend.innerHTML = allocation.labels.map((label, i) => `
475
- <div class="mb-3">
476
- <div class="flex justify-between mb-1">
477
- <span class="text-gray-500 dark:text-gray-400 flex items-center">
478
- <span class="h-3 w-3 bg-[${allocationChart.data.datasets[0].backgroundColor[i]}] rounded-full mr-2"></span>
479
- ${label}
480
- </span>
481
- <span class="font-medium dark:text-white">${allocation.data[i].toFixed(1)}%</span>
482
- </div>
483
- <div class="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700">
484
- <div class="bg-[${allocationChart.data.datasets[0].backgroundColor[i]}] h-2 rounded-full" style="width: ${allocation.data[i]}%"></div>
485
- </div>
486
- </div>`).join('');
487
- }
488
-
489
- async function fetchData() {
490
- try {
491
- document.querySelectorAll('.trading-card h3').forEach(el => el.textContent = 'Loading...');
492
- document.getElementById('trading-table-body').innerHTML = '<tr><td colspan="8" class="py-4 text-center">Loading...</td></tr>';
493
-
494
- if (!API_KEY || !API_SECRET) {
495
- throw new Error('Please enter your BingX API Key and Secret in the credentials form.');
496
- }
497
-
498
- const [balance, positions, trades] = await Promise.all([
499
- fetchBalance(),
500
- fetchOpenPositions(),
501
- fetchTradeHistory()
502
- ]);
503
-
504
- document.getElementById('total-balance').textContent = `$${balance.toFixed(2)}`;
505
- document.getElementById('open-trades').textContent = positions.length;
506
- const longCount = positions.filter(p => p.positionSide === 'LONG').length;
507
- 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>`;
508
- const todayProfit = calculateTodayProfit(trades);
509
- document.getElementById('today-profit').textContent = `$${todayProfit.toFixed(2)}`;
510
- const riskPercent = balance ? (positions.reduce((sum, p) => sum + (p.positionValue || 0), 0) / balance * 100) : 0;
511
- document.getElementById('risk-exposure').textContent = riskPercent < 20 ? 'Low' : riskPercent < 50 ? 'Medium' : 'High';
512
- document.getElementById('exposure-percent').innerHTML = `<span class="font-medium">${riskPercent.toFixed(1)}%</span><span class="ml-2">of balance</span>`;
513
-
514
- updateTradingTable(positions, trades);
515
- const stats = calculateAdvancedStats(trades);
516
- updateAdvancedStats(stats);
517
- updatePerformanceChart(trades);
518
- const allocation = calculatePortfolioAllocation(positions);
519
- updateAllocationChart(allocation);
520
-
521
- document.getElementById('last-sync').textContent = `Last synced: ${new Date().toLocaleTimeString()}`;
522
- document.getElementById('allocation-update').textContent = `Last updated: ${new Date().toLocaleTimeString()}`;
523
- } catch (error) {
524
- console.error('Error fetching data:', error);
525
- alert(`Failed to sync with BingX API: ${error.message}. Please check your credentials, permissions, network, or BingX API documentation.`);
526
- }
527
- }
528
-
529
- document.getElementById('refresh-btn').addEventListener('click', fetchData);
530
- document.getElementById('sync-now').addEventListener('click', fetchData);
531
- window.addEventListener('load', fetchData);
532
- setInterval(fetchData, 120000);
533
- </script>
534
- </body>
535
- </html>
536
- """
537
-
538
- # Combine Gradio with Custom HTML
539
- demo = gr.TabbedInterface(
540
- [gradio_app, gr.HTML(bingx_html)],
541
- ["Hot Dog Classifier", "BingX Dashboard"],
542
- title="Multi-Function App"
543
- )
544
-
545
- if __name__ == "__main__":
546
- demo.launch()