EricSam commited on
Commit
ffbd55f
Β·
verified Β·
1 Parent(s): be8d8d3

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +290 -0
app.py ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from transformers import pipeline
3
+ import hmac
4
+ import hashlib
5
+ import requests
6
+ import time
7
+ import os
8
+ from datetime import datetime
9
+
10
+ # Initialize hotdog classification pipeline
11
+ hotdog_pipeline = pipeline(task="image-classification", model="julien-c/hotdog-not-hotdog")
12
+
13
+ # Configuration
14
+ API_BASE_URL = "https://open-api.bingx.com"
15
+ API_KEY = os.getenv('BINGX_API_KEY', "")
16
+ API_SECRET = os.getenv('BINGX_API_SECRET', "")
17
+
18
+ # Generate Signature
19
+ def generate_signature(api_secret, params_str):
20
+ return hmac.new(api_secret.encode("utf-8"), params_str.encode("utf-8"), hashlib.sha256).hexdigest()
21
+
22
+ # Parse Parameters
23
+ def parse_params(params):
24
+ sorted_keys = sorted(params.keys())
25
+ param_pairs = [f"{key}={params[key]}" for key in sorted_keys]
26
+ params_str = "&".join(param_pairs)
27
+ return params_str + "&timestamp=" + str(int(time.time() * 1000)) if params_str else "timestamp=" + str(int(time.time() * 1000))
28
+
29
+ # Fetch from BingX API
30
+ def fetch_from_api(endpoint, params=None):
31
+ if not API_KEY or not API_SECRET:
32
+ raise ValueError("BingX API Key and Secret are not set. Please configure them in Hugging Face Space Secrets.")
33
+ params = params or {}
34
+ params['recvWindow'] = params.get('recvWindow', '5000')
35
+ params_str = parse_params(params)
36
+ signature = generate_signature(API_SECRET, params_str)
37
+ url = f"{API_BASE_URL}{endpoint}?{params_str}&signature={signature}"
38
+ try:
39
+ response = requests.get(url, headers={'X-BX-APIKEY': API_KEY, 'Content-Type': 'application/json'})
40
+ response.raise_for_status()
41
+ return response.json()
42
+ except requests.exceptions.RequestException as e:
43
+ raise Exception(f"API Error: {str(e)}")
44
+
45
+ # Fetch Specific Data
46
+ def fetch_balance():
47
+ data = fetch_from_api('/openApi/swap/v3/user/balance')
48
+ return data['data'][0]['walletBalance'] if data['data'] and data['data'][0]['asset'] == 'USDT' else 0
49
+
50
+ def fetch_open_positions():
51
+ data = fetch_from_api('/openApi/swap/v2/user/positions', {'symbol': 'BTC-USDT'})
52
+ return data['data'] or []
53
+
54
+ def fetch_trade_history():
55
+ data = fetch_from_api('/openApi/swap/v2/user/income', {'limit': 100})
56
+ return data['data'] or []
57
+
58
+ # Helper Functions
59
+ def calculate_today_profit(trades):
60
+ today = datetime.now().date()
61
+ return sum(trade['income'] for trade in trades if datetime.fromtimestamp(trade['time'] / 1000).date() == today)
62
+
63
+ def calculate_advanced_stats(trades):
64
+ total_profit, total_loss, wins = 0, 0, 0
65
+ for trade in trades:
66
+ pl = trade['income']
67
+ if pl > 0:
68
+ total_profit += pl
69
+ wins += 1
70
+ else:
71
+ total_loss += abs(pl)
72
+ profit_factor = total_loss and (total_profit / total_loss) or 0
73
+ win_rate = trades and (wins / len(trades) * 100) or 0
74
+ return {'profit_factor': profit_factor, 'win_rate': win_rate}
75
+
76
+ def calculate_portfolio_allocation(positions):
77
+ total_value = sum(pos['positionValue'] for pos in positions if 'positionValue' in pos)
78
+ by_symbol = {}
79
+ for pos in positions:
80
+ by_symbol[pos['symbol']] = by_symbol.get(pos['symbol'], 0) + (pos.get('positionValue', 0))
81
+ labels = list(by_symbol.keys())
82
+ data = [val / total_value * 100 if total_value else 0 for val in by_symbol.values()]
83
+ return {'labels': labels, 'data': data}
84
+
85
+ # Update UI Functions (as Gradio components)
86
+ def update_trading_table(positions, trades):
87
+ table_rows = []
88
+ for pos in positions:
89
+ table_rows.append(f"""
90
+ <tr class="border-b border-gray-200 dark:border-gray-700">
91
+ <td class="py-4">{pos['symbol']}</td>
92
+ <td class="py-4"><span class="{('bg-green-100 text-green-800' if pos['positionSide'] == 'LONG' else 'bg-red-100 text-red-800')} px-2 py-1 rounded">{pos['positionSide']}</span></td>
93
+ <td class="py-4">{pos['quantity']}</td>
94
+ <td class="py-4">${pos['entryPrice']:.2f}</td>
95
+ <td class="py-4 font-medium">${pos['markPrice']:.2f}</td>
96
+ <td class="py-4 font-bold {('text-green-500' if pos['unrealizedProfit'] > 0 else 'text-red-500')}">${pos['unrealizedProfit']:.2f}</td>
97
+ <td class="py-4"><span class="px-2 py-1 rounded bg-blue-100 text-blue-800">Open</span></td>
98
+ <td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td>
99
+ </tr>
100
+ """)
101
+ for trade in trades[:5]:
102
+ table_rows.append(f"""
103
+ <tr class="border-b border-gray-200 dark:border-gray-700">
104
+ <td class="py-4">{trade['symbol']}</td>
105
+ <td class="py-4"><span class="{('bg-green-100 text-green-800' if trade['positionSide'] == 'LONG' else 'bg-red-100 text-red-800')} px-2 py-1 rounded">{trade['positionSide']}</span></td>
106
+ <td class="py-4">{trade.get('quantity', 0)}</td>
107
+ <td class="py-4">${trade.get('entryPrice', 0):.2f}</td>
108
+ <td class="py-4 font-medium">${trade.get('exitPrice', 0):.2f}</td>
109
+ <td class="py-4 font-bold {('text-green-500' if trade['income'] > 0 else 'text-red-500')}">${trade['income']:.2f}</td>
110
+ <td class="py-4"><span class="px-2 py-1 rounded bg-gray-100 text-gray-800">Closed</span></td>
111
+ <td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td>
112
+ </tr>
113
+ """)
114
+ return gr.HTML("\n".join(table_rows))
115
+
116
+ def update_advanced_stats(stats):
117
+ return gr.HTML(f"""
118
+ <div class="space-y-5">
119
+ <div>
120
+ <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['profit_factor']}</span></div>
121
+ <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: {min(stats['profit_factor'] * 25, 100)}%"></div></div>
122
+ </div>
123
+ <div>
124
+ <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['win_rate']}%</span></div>
125
+ <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['win_rate']}%"></div></div>
126
+ </div>
127
+ </div>
128
+ """)
129
+
130
+ def update_performance_chart(trades):
131
+ monthly_pl = {}
132
+ for trade in trades:
133
+ month = datetime.fromtimestamp(trade['time'] / 1000).strftime('%b')
134
+ monthly_pl[month] = monthly_pl.get(month, 0) + trade['income']
135
+ chart_data = {
136
+ 'labels': list(monthly_pl.keys()),
137
+ 'datasets': [{'label': 'Profit/Loss', 'data': list(monthly_pl.values()), 'borderColor': '#1E90FF', 'backgroundColor': 'rgba(30, 144, 255, 0.1)', 'tension': 0.4, 'fill': True}]
138
+ }
139
+ return gr.Chart(value=chart_data, type="line", options={
140
+ 'responsive': True,
141
+ 'plugins': {'legend': {'display': False}},
142
+ 'scales': {'y': {'grid': {'color': 'rgba(0, 0, 0, 0.05)', 'borderDash': [5]}, 'ticks': {'callback': lambda value: f'${value}'}}, 'x': {'grid': {'display': False}}}
143
+ })
144
+
145
+ def update_allocation_chart(allocation):
146
+ chart_data = {
147
+ 'labels': allocation['labels'],
148
+ 'datasets': [{'data': allocation['data'], 'backgroundColor': ['#1E90FF', '#10b981', '#8b5cf6', '#f59e0b'], 'borderWidth': 0}]
149
+ }
150
+ legend_html = "\n".join(f"""
151
+ <div class="mb-3">
152
+ <div class="flex justify-between mb-1">
153
+ <span class="text-gray-500 dark:text-gray-400 flex items-center">
154
+ <span class="h-3 w-3 bg-[{chart_data['datasets'][0]['backgroundColor'][i]}] rounded-full mr-2"></span>
155
+ {label}
156
+ </span>
157
+ <span class="font-medium dark:text-white">{data:.1f}%</span>
158
+ </div>
159
+ <div class="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700">
160
+ <div class="bg-[{chart_data['datasets'][0]['backgroundColor'][i]}] h-2 rounded-full" style="width: {data}%"></div>
161
+ </div>
162
+ </div>
163
+ """ for i, (label, data) in enumerate(zip(allocation['labels'], allocation['data'])))
164
+ return gr.Chart(value=chart_data, type="doughnut", options={
165
+ 'responsive': True, 'cutout': '65%', 'plugins': {'legend': {'display': False}, 'tooltip': {'callbacks': {'label': lambda context: f"{context['label']}: {context['parsed']}%"}}}
166
+ }), gr.HTML(legend_html)
167
+
168
+ # Main Data Fetch and Update Function
169
+ def update_dashboard():
170
+ try:
171
+ balance = fetch_balance()
172
+ positions = fetch_open_positions()
173
+ trades = fetch_trade_history()
174
+
175
+ total_balance = f"${balance:.2f}"
176
+ open_trades = len(positions)
177
+ long_count = len([p for p in positions if p['positionSide'] == 'LONG'])
178
+ today_profit = f"${calculate_today_profit(trades):.2f}"
179
+ risk_percent = balance and (sum(p.get('positionValue', 0) for p in positions) / balance * 100) or 0
180
+ risk_exposure = 'Low' if risk_percent < 20 else 'Medium' if risk_percent < 50 else 'High'
181
+ exposure_percent = f"{risk_percent:.1f}%"
182
+
183
+ stats = calculate_advanced_stats(trades)
184
+ allocation = calculate_portfolio_allocation(positions)
185
+
186
+ return (
187
+ total_balance, open_trades, f"{long_count} Long β€’ {len(positions) - long_count} Short", today_profit,
188
+ risk_exposure, exposure_percent, update_trading_table(positions, trades), update_advanced_stats(stats),
189
+ update_performance_chart(trades), update_allocation_chart(allocation), datetime.now().strftime('%I:%M %p'),
190
+ datetime.now().strftime('%I:%M %p')
191
+ )
192
+ except Exception as e:
193
+ return (
194
+ "$0.00", 0, "0 Long β€’ 0 Short", "$0.00", "Low", "0%", gr.HTML("<tr><td colspan='8' class='py-4 text-center'>Error: Failed to sync with BingX API</td></tr>"),
195
+ gr.HTML("<div>Error loading stats</div>"), gr.Chart(), (gr.Chart(), gr.HTML("")), "Just now", "Just now"
196
+ )
197
+
198
+ # Gradio Interface
199
+ with gr.Blocks(title="Nakhoda4X Pro") as demo:
200
+ gr.Markdown("""
201
+ # Nakhoda4X Pro
202
+ **Trading Dashboard** connected to BingX via API | **Hot Dog Classifier** for image analysis
203
+ """)
204
+
205
+ with gr.Tab("Trading Dashboard"):
206
+ with gr.Row():
207
+ with gr.Column():
208
+ gr.Markdown("### Trading Dashboard")
209
+ gr.Markdown("Connected to BingX via API")
210
+ with gr.Column():
211
+ refresh_btn = gr.Button("Refresh", variant="secondary")
212
+ new_trade_btn = gr.Button("New Trade", variant="primary")
213
+
214
+ with gr.Row():
215
+ with gr.Column(scale=1):
216
+ stats_cards = gr.Blocks()
217
+ with stats_cards:
218
+ with gr.Row():
219
+ gr.HTML("<div class='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6'>")
220
+ total_balance = gr.HTML("<h3 id='total-balance' class='text-2xl font-bold mt-2 dark:text-white'>$0.00</h3>")
221
+ open_trades = gr.HTML("<h3 id='open-trades' class='text-2xl font-bold mt-2 dark:text-white'>0</h3>")
222
+ today_profit = gr.HTML("<h3 id='today-profit' class='text-2xl font-bold mt-2 dark:text-white'>$0.00</h3>")
223
+ risk_exposure = gr.HTML("<h3 id='risk-exposure' class='text-2xl font-bold mt-2 dark:text-white'>Low</h3>")
224
+ with gr.Row():
225
+ gr.HTML("</div>")
226
+ balance_change = gr.HTML("<div id='balance-change' class='mt-4 flex items-center text-green-500'><i class='fas fa-caret-up mr-1'></i><span class='font-medium'>0.0%</span><span class='text-gray-500 ml-2 dark:text-gray-400'>last 24h</span></div>")
227
+ trade_types = gr.HTML("<div id='trade-types' class='mt-4 flex items-center text-blue-500'><span class='font-medium'>0 Long</span><span class='text-gray-500 mx-2 dark:text-gray-400'>β€’</span><span class='font-medium'>0 Short</span></div>")
228
+ profit_change = gr.HTML("<div id='profit-change' class='mt-4 flex items-center text-green-500'><i class='fas fa-caret-up mr-1'></i><span class='font-medium'>0.0%</span><span class='text-gray-500 ml-2 dark:text-gray-400'>vs yesterday</span></div>")
229
+ exposure_percent = gr.HTML("<div id='exposure-percent' class='mt-4 flex items-center text-gray-500 dark:text-gray-400'><span class='font-medium'>0%</span><span class='ml-2'>of balance</span></div>")
230
+
231
+ with gr.Column(scale=3):
232
+ with gr.Row():
233
+ with gr.Column():
234
+ performance_chart = gr.Chart()
235
+ with gr.Column():
236
+ advanced_stats = gr.HTML()
237
+
238
+ with gr.Row():
239
+ trading_activity = gr.HTML()
240
+
241
+ with gr.Row():
242
+ with gr.Column():
243
+ api_connection = gr.Blocks()
244
+ with api_connection:
245
+ gr.HTML("<div class='bg-gradient-to-r from-primary to-secondary rounded-2xl p-6'>")
246
+ gr.HTML("<div class='flex items-center mb-4'><div class='mr-4'><div class='h-12 w-12 rounded-xl bg-white/30 flex items-center justify-center'><i class='fas fa-plug text-white text-xl'></i></div></div><div><h3 class='text-lg font-bold text-white'>BingX API Connected</h3><p id='last-sync' class='text-blue-100'>Last synced: Just now</p></div></div>")
247
+ sync_now = gr.Button("Sync Now", elem_classes="w-full py-3 bg-white rounded-xl text-primary font-bold flex items-center justify-center")
248
+ with gr.Column():
249
+ portfolio_allocation = gr.Blocks()
250
+ with portfolio_allocation:
251
+ gr.HTML("<div class='bg-white dark:bg-darkCard rounded-2xl shadow-md p-6'>")
252
+ gr.HTML("<div class='flex justify-between items-center mb-6'><h3 class='text-lg font-bold text-gray-800 dark:text-white'>Portfolio Allocation</h3><div><span id='allocation-update' class='text-gray-500 dark:text-gray-400 text-sm'>Last updated: Just now</span></div></div>")
253
+ with gr.Row():
254
+ allocation_chart = gr.Chart()
255
+ allocation_legend = gr.HTML()
256
+
257
+ # Initial update
258
+ stats_cards.update(value=gr.HTML("<div class='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6'>"))
259
+ demo.load(fn=update_dashboard, inputs=None, outputs=[
260
+ total_balance, open_trades, trade_types, today_profit,
261
+ risk_exposure, exposure_percent, trading_activity, advanced_stats,
262
+ performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None)
263
+ ])
264
+ refresh_btn.click(fn=update_dashboard, inputs=None, outputs=[
265
+ total_balance, open_trades, trade_types, today_profit,
266
+ risk_exposure, exposure_percent, trading_activity, advanced_stats,
267
+ performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None)
268
+ ])
269
+ sync_now.click(fn=update_dashboard, inputs=None, outputs=[
270
+ total_balance, open_trades, trade_types, today_profit,
271
+ risk_exposure, exposure_percent, trading_activity, advanced_stats,
272
+ performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None)
273
+ ])
274
+
275
+ with gr.Tab("Hot Dog Classifier"):
276
+ gr.Markdown("### Hot Dog? Or Not?")
277
+ with gr.Row():
278
+ input_img = gr.Image(label="Select hot dog candidate", sources=['upload', 'webcam'], type="pil")
279
+ output_img = gr.Image(label="Processed Image")
280
+ output_label = gr.Label(label="Result", num_top_classes=2)
281
+ submit_btn = gr.Button("Classify")
282
+ submit_btn.click(fn=lambda img: (img, {p["label"]: p["score"] for p in hotdog_pipeline(img)}), inputs=input_img, outputs=[output_img, output_label])
283
+
284
+ demo.load(None, _js="() => [document.body.classList.toggle('dark', window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)]")
285
+ with gr.Row():
286
+ theme_toggle = gr.Button("Toggle Theme", elem_classes="text-gray-600 dark:text-gray-300")
287
+ theme_toggle.click(None, _js="() => document.body.classList.toggle('dark')")
288
+
289
+ if __name__ == "__main__":
290
+ demo.launch()