EricSam commited on
Commit
7472168
Β·
verified Β·
1 Parent(s): 8c11a49

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +112 -89
app.py CHANGED
@@ -119,11 +119,11 @@ def update_advanced_stats(stats):
119
  return gr.HTML(f"""
120
  <div class="space-y-5">
121
  <div>
122
- <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>
123
  <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>
124
  </div>
125
  <div>
126
- <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>
127
  <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>
128
  </div>
129
  </div>
@@ -141,7 +141,7 @@ def update_performance_chart(trades):
141
  return gr.Chart(value=chart_data, type="line", options={
142
  'responsive': True,
143
  'plugins': {'legend': {'display': False}},
144
- 'scales': {'y': {'grid': {'color': 'rgba(0, 0, 0, 0.05)', 'borderDash': [5]}, 'ticks': {'callback': lambda value: f'${value}'}}, 'x': {'grid': {'display': false}}}
145
  })
146
 
147
  def update_allocation_chart(allocation):
@@ -193,7 +193,7 @@ def update_dashboard(api_key, api_secret):
193
  )
194
  except Exception as e:
195
  return (
196
- "$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 - Check credentials</td></tr>"),
197
  gr.HTML("<div>Error loading stats</div>"), gr.Chart(), (gr.Chart(), gr.HTML("")), "Just now", "Just now"
198
  )
199
 
@@ -203,91 +203,116 @@ def save_credentials(new_api_key, new_api_secret, api_key, api_secret):
203
  return gr.Warning("Both API Key and Secret are required."), api_key, api_secret
204
  return gr.Info("Credentials saved successfully! Syncing data..."), gr.State(value=new_api_key), gr.State(value=new_api_secret)
205
 
 
 
 
 
206
  # Gradio Interface
207
  with gr.Blocks(title="Nakhoda4X Pro") as demo:
208
- gr.Markdown("""
209
- # Nakhoda4X Pro
210
- **Trading Dashboard** connected to BingX via API | **Hot Dog Classifier** for image analysis
211
- """)
212
-
213
- with gr.Tab("Trading Dashboard"):
214
- with gr.Row():
215
- with gr.Column():
216
- gr.Markdown("### Trading Dashboard")
217
- gr.Markdown("Connected to BingX via API")
218
- with gr.Column():
219
- refresh_btn = gr.Button("Refresh", variant="secondary")
220
-
221
- with gr.Row():
222
- with gr.Column(scale=1):
223
- stats_cards = gr.Blocks()
224
- with stats_cards:
225
- with gr.Row():
226
- gr.HTML("<div class='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6'>")
227
- total_balance = gr.HTML("<h3 id='total-balance' class='text-2xl font-bold mt-2 dark:text-white'>$0.00</h3>")
228
- open_trades = gr.HTML("<h3 id='open-trades' class='text-2xl font-bold mt-2 dark:text-white'>0</h3>")
229
- today_profit = gr.HTML("<h3 id='today-profit' class='text-2xl font-bold mt-2 dark:text-white'>$0.00</h3>")
230
- risk_exposure = gr.HTML("<h3 id='risk-exposure' class='text-2xl font-bold mt-2 dark:text-white'>Low</h3>")
231
- with gr.Row():
232
- gr.HTML("</div>")
233
- 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>")
234
- 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>")
235
- 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>")
236
- 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>")
237
-
238
- with gr.Column(scale=3):
239
- with gr.Row():
240
- with gr.Column():
241
- performance_chart = gr.Chart()
242
- with gr.Column():
243
- advanced_stats = gr.HTML()
244
-
245
- with gr.Row():
246
- trading_activity = gr.HTML()
247
-
248
- with gr.Row():
249
- with gr.Column():
250
- api_connection = gr.Blocks()
251
- with api_connection:
252
- gr.HTML("<div class='bg-gradient-to-r from-primary to-secondary rounded-2xl p-6'>")
253
- 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>")
254
- with gr.Row():
255
- api_key_input = gr.Textbox(label="API Key", value=api_key.value, type="password")
256
- api_secret_input = gr.Textbox(label="API Secret", value=api_secret.value, type="password")
257
- save_btn = gr.Button("Save Credentials", variant="primary")
258
- 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")
259
- gr.Markdown("**Note:** Credentials are stored in memory for this session only.")
260
- with gr.Column():
261
- portfolio_allocation = gr.Blocks()
262
- with portfolio_allocation:
263
- gr.HTML("<div class='bg-white dark:bg-darkCard rounded-2xl shadow-md p-6'>")
264
- 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>")
265
- with gr.Row():
266
- allocation_chart = gr.Chart()
267
- allocation_legend = gr.HTML()
268
-
269
- # Event Handlers
270
- save_btn.click(
271
- fn=save_credentials,
272
- inputs=[api_key_input, api_secret_input, api_key, api_secret],
273
- outputs=[gr.Markdown(), api_key, api_secret]
274
- )
275
- demo.load(fn=update_dashboard, inputs=[api_key, api_secret], outputs=[
276
- total_balance, open_trades, trade_types, today_profit,
277
- risk_exposure, exposure_percent, trading_activity, advanced_stats,
278
- performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None)
279
- ])
280
- refresh_btn.click(fn=update_dashboard, inputs=[api_key, api_secret], outputs=[
281
- total_balance, open_trades, trade_types, today_profit,
282
- risk_exposure, exposure_percent, trading_activity, advanced_stats,
283
- performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None)
284
- ])
285
- sync_now.click(fn=update_dashboard, inputs=[api_key, api_secret], outputs=[
286
- total_balance, open_trades, trade_types, today_profit,
287
- risk_exposure, exposure_percent, trading_activity, advanced_stats,
288
- performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None)
289
- ])
290
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  with gr.Tab("Hot Dog Classifier"):
292
  gr.Markdown("### Hot Dog? Or Not?")
293
  with gr.Row():
@@ -297,9 +322,7 @@ with gr.Blocks(title="Nakhoda4X Pro") as demo:
297
  submit_btn = gr.Button("Classify")
298
  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])
299
 
300
- demo.load(None, _js="() => [document.body.classList.toggle('dark', window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)]")
301
- with gr.Row():
302
- theme_toggle = gr.Button("Toggle Theme", elem_classes="text-gray-600 dark:text-gray-300")
303
  theme_toggle.click(None, _js="() => document.body.classList.toggle('dark')")
304
 
305
  if __name__ == "__main__":
 
119
  return gr.HTML(f"""
120
  <div class="space-y-5">
121
  <div>
122
+ <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']:.2f}</span></div>
123
  <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>
124
  </div>
125
  <div>
126
+ <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']:.1f}%</span></div>
127
  <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>
128
  </div>
129
  </div>
 
141
  return gr.Chart(value=chart_data, type="line", options={
142
  'responsive': True,
143
  'plugins': {'legend': {'display': False}},
144
+ 'scales': {'y': {'grid': {'color': 'rgba(0, 0, 0, 0.05)', 'borderDash': [5]}, 'ticks': {'callback': lambda value: f'${value}'}}, 'x': {'grid': {'display': False}}}
145
  })
146
 
147
  def update_allocation_chart(allocation):
 
193
  )
194
  except Exception as e:
195
  return (
196
+ "$Loading...", 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 - Check credentials</td></tr>"),
197
  gr.HTML("<div>Error loading stats</div>"), gr.Chart(), (gr.Chart(), gr.HTML("")), "Just now", "Just now"
198
  )
199
 
 
203
  return gr.Warning("Both API Key and Secret are required."), api_key, api_secret
204
  return gr.Info("Credentials saved successfully! Syncing data..."), gr.State(value=new_api_key), gr.State(value=new_api_secret)
205
 
206
+ # Toggle API Form Visibility
207
+ def toggle_api_form(show_form):
208
+ return gr.update(visible=show_form)
209
+
210
  # Gradio Interface
211
  with gr.Blocks(title="Nakhoda4X Pro") as demo:
212
+ # Custom CSS and JS for Tailwind and dark mode
213
+ demo.css = """
214
+ .trading-card:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); }
215
+ .animate-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
216
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
217
+ .coin-animation { animation: float 3s ease-in-out infinite; }
218
+ @keyframes float { 0% { transform: translateY(0px); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0px); } }
219
+ .api-form { max-height: 0; overflow: hidden; transition: max-height 0.3s ease-in-out; }
220
+ .api-form.open { max-height: 200px; }
221
+ body { background-color: #f3f4f6; }
222
+ body.dark { background-color: #111827; }
223
+ .dark .bg-white { background-color: #1f2937; }
224
+ .dark .text-gray-800 { color: #ffffff; }
225
+ .dark .text-gray-500 { color: #9ca3af; }
226
+ """
227
+ demo.js = """
228
+ () => [document.body.classList.toggle('dark', window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)]
229
+ """
230
+
231
+ # Header
232
+ with gr.Row():
233
+ with gr.Column(scale=1):
234
+ gr.HTML("""
235
+ <div class="bg-white dark:bg-darkCard shadow-sm flex items-center p-4">
236
+ <div class="bg-primary w-10 h-10 rounded-xl flex items-center justify-center mr-3 coin-animation">
237
+ <i class="fas fa-chart-line text-white text-xl"></i>
238
+ </div>
239
+ <h1 class="text-2xl font-bold text-gray-800 dark:text-white">Nakhoda4X <span class="text-primary">Pro</span></h1>
240
+ </div>
241
+ """)
242
+ with gr.Column(scale=1, elem_classes="flex justify-end items-center space-x-6"):
243
+ theme_toggle = gr.Button(value="Toggle Theme", elem_classes="text-gray-600 dark:text-gray-300")
244
+ gr.HTML('<div><i class="fas fa-bell text-gray-600 dark:text-gray-300 text-xl"></i><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></div>')
245
+ gr.HTML('<div class="flex items-center"><div class="mr-3 text-right"><p class="font-semibold text-gray-800 dark:text-white">SahabatPipHijau</p><p class="text-sm text-gray-500">Premium Account</p></div><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">J</div></div>')
246
+
247
+ # Main Content
248
+ with gr.Row():
249
+ with gr.Column():
250
+ gr.Markdown("### Trading Dashboard")
251
+ gr.Markdown("Connected to BingX via API")
252
+ with gr.Row():
253
+ refresh_btn = gr.Button("Refresh", elem_classes="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")
254
+ new_trade_btn = gr.Button("New Trade", elem_classes="px-4 py-2 bg-primary text-white rounded-lg flex items-center")
255
+
256
+ # Stats Cards
257
+ with gr.Row(elem_classes="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"):
258
+ total_balance = gr.HTML("<div class='trading-card bg-white dark:bg-darkCard rounded-2xl shadow-md p-6'><div class='flex justify-between'><div><p class='text-gray-500 dark:text-gray-400'>Total Balance</p><h3 id='total-balance' class='text-2xl font-bold mt-2 dark:text-white'>$0.00</h3></div><div class='w-12 h-12 rounded-xl bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center'><i class='fas fa-wallet text-blue-500 text-xl'></i></div></div><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></div>")
259
+ open_trades = gr.HTML("<div class='trading-card bg-white dark:bg-darkCard rounded-2xl shadow-md p-6'><div class='flex justify-between'><div><p class='text-gray-500 dark:text-gray-400'>Open Trades</p><h3 id='open-trades' class='text-2xl font-bold mt-2 dark:text-white'>0</h3></div><div class='w-12 h-12 rounded-xl bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center'><i class='fas fa-exchange-alt text-purple-500 text-xl'></i></div></div><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></div>")
260
+ today_profit = gr.HTML("<div class='trading-card bg-white dark:bg-darkCard rounded-2xl shadow-md p-6'><div class='flex justify-between'><div><p class='text-gray-500 dark:text-gray-400'>Today's Profit</p><h3 id='today-profit' class='text-2xl font-bold mt-2 dark:text-white'>$0.00</h3></div><div class='w-12 h-12 rounded-xl bg-green-100 dark:bg-green-900/30 flex items-center justify-center'><i class='fas fa-chart-bar text-green-500 text-xl'></i></div></div><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></div>")
261
+ risk_exposure = gr.HTML("<div class='trading-card bg-white dark:bg-darkCard rounded-2xl shadow-md p-6'><div class='flex justify-between'><div><p class='text-gray-500 dark:text-gray-400'>Risk Exposure</p><h3 id='risk-exposure' class='text-2xl font-bold mt-2 dark:text-white'>Low</h3></div><div class='w-12 h-12 rounded-xl bg-yellow-100 dark:bg-yellow-900/30 flex items-center justify-center'><i class='fas fa-exclamation-triangle text-yellow-500 text-xl'></i></div></div><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></div>")
262
+
263
+ # Charts and Statistics
264
+ with gr.Row(elem_classes="grid grid-cols-1 lg:grid-cols-3 gap-6"):
265
+ with gr.Column(scale=2):
266
+ performance_chart = gr.Chart()
267
+ gr.HTML("<div class='flex justify-between items-center mb-6'><h3 class='text-lg font-bold text-gray-800 dark:text-white'>Monthly Performance</h3><div class='flex space-x-3'><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><button class='px-3 py-1 rounded-lg text-sm bg-primary text-white'>3M</button><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><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></div></div>")
268
+ with gr.Column(scale=1):
269
+ advanced_stats = gr.HTML()
270
+
271
+ # Trading Activity
272
+ trading_activity = gr.HTML()
273
+ gr.HTML("<div class='flex justify-between items-center mb-6'><h3 class='text-lg font-bold text-gray-800 dark:text-white'>Trading Activity</h3><div class='flex space-x-3'><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><button class='px-3 py-1 rounded-lg text-sm bg-primary text-white'>Open</button><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></div></div>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
 
275
+ # API Connection and Portfolio Allocation
276
+ with gr.Row(elem_classes="grid grid-cols-1 lg:grid-cols-3 gap-6"):
277
+ with gr.Column():
278
+ api_connection = gr.Blocks()
279
+ with api_connection:
280
+ gr.HTML("<div class='bg-gradient-to-r from-primary to-secondary rounded-2xl p-6'><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>")
281
+ show_form = gr.State(False)
282
+ toggle_btn = gr.Button("Manage API Credentials", elem_classes="w-full py-2 bg-white rounded-xl text-primary font-bold flex items-center justify-center mb-2")
283
+ with gr.Column(elem_classes="api-form", visible=False) as api_form:
284
+ api_key_input = gr.Textbox(label="API Key", value=api_key.value, type="password")
285
+ api_secret_input = gr.Textbox(label="API Secret", value=api_secret.value, type="password")
286
+ save_btn = gr.Button("Save Credentials", elem_classes="w-full py-2 bg-primary text-white rounded-lg")
287
+ sync_now = gr.Button("Sync Now", elem_classes="mt-2 w-full py-3 bg-white rounded-xl text-primary font-bold flex items-center justify-center")
288
+
289
+ with gr.Column(scale=2):
290
+ portfolio_allocation = gr.Blocks()
291
+ with portfolio_allocation:
292
+ gr.HTML("<div class='bg-white dark:bg-darkCard rounded-2xl shadow-md p-6'><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>")
293
+ allocation_chart, allocation_legend = gr.Chart(), gr.HTML()
294
+
295
+ # Event Handlers
296
+ toggle_btn.click(fn=toggle_api_form, inputs=[show_form], outputs=[api_form], _js="() => !document.querySelector('.api-form').classList.contains('open')")
297
+ save_btn.click(fn=save_credentials, inputs=[api_key_input, api_secret_input, api_key, api_secret], outputs=[gr.Markdown(), api_key, api_secret])
298
+ demo.load(fn=update_dashboard, inputs=[api_key, api_secret], outputs=[
299
+ total_balance, open_trades, trade_types, today_profit,
300
+ risk_exposure, exposure_percent, trading_activity, advanced_stats,
301
+ performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None)
302
+ ])
303
+ refresh_btn.click(fn=update_dashboard, inputs=[api_key, api_secret], outputs=[
304
+ total_balance, open_trades, trade_types, today_profit,
305
+ risk_exposure, exposure_percent, trading_activity, advanced_stats,
306
+ performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None)
307
+ ])
308
+ sync_now.click(fn=update_dashboard, inputs=[api_key, api_secret], outputs=[
309
+ total_balance, open_trades, trade_types, today_profit,
310
+ risk_exposure, exposure_percent, trading_activity, advanced_stats,
311
+ performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None)
312
+ ])
313
+ new_trade_btn.click(None, _js="() => alert('New Trade functionality not implemented yet.')")
314
+
315
+ # Hot Dog Classifier Tab
316
  with gr.Tab("Hot Dog Classifier"):
317
  gr.Markdown("### Hot Dog? Or Not?")
318
  with gr.Row():
 
322
  submit_btn = gr.Button("Classify")
323
  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])
324
 
325
+ # Theme Toggle
 
 
326
  theme_toggle.click(None, _js="() => document.body.classList.toggle('dark')")
327
 
328
  if __name__ == "__main__":