Spaces:
Runtime error
Runtime error
import gradio as gr | |
from transformers import pipeline | |
import hmac | |
import hashlib | |
import requests | |
import time | |
import os | |
from datetime import datetime | |
import plotly.express as px | |
# Initialize hotdog classification pipeline with error handling | |
hotdog_pipeline = None | |
try: | |
hotdog_pipeline = pipeline(task="image-classification", model="julien-c/hotdog-not-hotdog") | |
except RuntimeError as e: | |
print(f"Warning: Hotdog classifier failed to initialize: {e}. Image classification will be unavailable.") | |
# Global state for API credentials (updated by user input) | |
api_key = gr.State(os.getenv('BINGX_API_KEY', "")) | |
api_secret = gr.State(os.getenv('BINGX_API_SECRET', "")) | |
# Configuration | |
API_BASE_URL = "https://open-api.bingx.com" | |
# Generate Signature | |
def generate_signature(api_secret, params_str): | |
return hmac.new(api_secret.encode("utf-8"), params_str.encode("utf-8"), hashlib.sha256).hexdigest() | |
# Parse Parameters | |
def parse_params(params): | |
sorted_keys = sorted(params.keys()) | |
param_pairs = [f"{key}={params[key]}" for key in sorted_keys] | |
params_str = "&".join(param_pairs) | |
return params_str + "×tamp=" + str(int(time.time() * 1000)) | |
# Fetch from BingX API | |
def fetch_from_api(endpoint, params=None, api_key=api_key, api_secret=api_secret): | |
if not api_key.value or not api_secret.value: | |
raise ValueError("BingX API Key and Secret are not set. Please enter them in the Manage API Credentials section.") | |
params = params or {} | |
params['recvWindow'] = params.get('recvWindow', '5000') | |
params_str = parse_params(params) | |
signature = generate_signature(api_secret.value, params_str) | |
url = f"{API_BASE_URL}{endpoint}?{params_str}&signature={signature}" | |
try: | |
response = requests.get(url, headers={'X-BX-APIKEY': api_key.value, 'Content-Type': 'application/json'}) | |
response.raise_for_status() | |
return response.json() | |
except requests.exceptions.RequestException as e: | |
raise Exception(f"API Error: {str(e)}") | |
# Fetch Specific Data | |
def fetch_balance(api_key=api_key, api_secret=api_secret): | |
data = fetch_from_api('/openApi/swap/v3/user/balance', api_key=api_key, api_secret=api_secret) | |
return data['data'][0]['walletBalance'] if data['data'] and data['data'][0]['asset'] == 'USDT' else 0 | |
def fetch_open_positions(api_key=api_key, api_secret=api_secret): | |
data = fetch_from_api('/openApi/swap/v2/user/positions', {'symbol': 'BTC-USDT'}, api_key=api_key, api_secret=api_secret) | |
return data['data'] or [] | |
def fetch_trade_history(api_key=api_key, api_secret=api_secret): | |
data = fetch_from_api('/openApi/swap/v2/user/income', {'limit': 100}, api_key=api_key, api_secret=api_secret) | |
return data['data'] or [] | |
# Helper Functions | |
def calculate_today_profit(trades): | |
today = datetime.now().date() | |
return sum(trade['income'] for trade in trades if datetime.fromtimestamp(trade['time'] / 1000).date() == today) | |
def calculate_advanced_stats(trades): | |
total_profit, total_loss, wins = 0, 0, 0 | |
for trade in trades: | |
pl = trade['income'] | |
if pl > 0: | |
total_profit += pl | |
wins += 1 | |
else: | |
total_loss += abs(pl) | |
profit_factor = total_loss and (total_profit / total_loss) or 0 | |
win_rate = trades and (wins / len(trades) * 100) or 0 | |
return {'profit_factor': profit_factor, 'win_rate': win_rate} | |
def calculate_portfolio_allocation(positions): | |
total_value = sum(pos['positionValue'] for pos in positions if 'positionValue' in pos) | |
by_symbol = {} | |
for pos in positions: | |
by_symbol[pos['symbol']] = by_symbol.get(pos['symbol'], 0) + (pos.get('positionValue', 0)) | |
labels = list(by_symbol.keys()) | |
data = [val / total_value * 100 if total_value else 0 for val in by_symbol.values()] | |
return {'labels': labels, 'data': data} | |
# Update UI Functions | |
def update_trading_table(positions, trades): | |
table_rows = [] | |
for pos in positions: | |
table_rows.append(f""" | |
<tr class="border-b border-gray-200 dark:border-gray-700"> | |
<td class="py-4">{pos['symbol']}</td> | |
<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> | |
<td class="py-4">{pos['quantity']}</td> | |
<td class="py-4">${pos['entryPrice']:.2f}</td> | |
<td class="py-4 font-medium">${pos['markPrice']:.2f}</td> | |
<td class="py-4 font-bold {('text-green-500' if pos['unrealizedProfit'] > 0 else 'text-red-500')}">${pos['unrealizedProfit']:.2f}</td> | |
<td class="py-4"><span class="px-2 py-1 rounded bg-blue-100 text-blue-800">Open</span></td> | |
<td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td> | |
</tr> | |
""") | |
for trade in trades[:5]: | |
table_rows.append(f""" | |
<tr class="border-b border-gray-200 dark:border-gray-700"> | |
<td class="py-4">{trade['symbol']}</td> | |
<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> | |
<td class="py-4">{trade.get('quantity', 0)}</td> | |
<td class="py-4">${trade.get('entryPrice', 0):.2f}</td> | |
<td class="py-4 font-medium">${trade.get('exitPrice', 0):.2f}</td> | |
<td class="py-4 font-bold {('text-green-500' if trade['income'] > 0 else 'text-red-500')}">${trade['income']:.2f}</td> | |
<td class="py-4"><span class="px-2 py-1 rounded bg-gray-100 text-gray-800">Closed</span></td> | |
<td class="py-4 text-right"><button class="text-gray-400 hover:text-primary"><i class="fas fa-ellipsis-v"></i></button></td> | |
</tr> | |
""") | |
return gr.HTML("\n".join(table_rows)) | |
def update_advanced_stats(stats): | |
return gr.HTML(f""" | |
<div class="space-y-5"> | |
<div> | |
<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> | |
<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> | |
</div> | |
<div> | |
<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> | |
<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> | |
</div> | |
</div> | |
""") | |
def update_performance_chart(trades): | |
monthly_pl = {} | |
for trade in trades: | |
month = datetime.fromtimestamp(trade['time'] / 1000).strftime('%b') | |
monthly_pl[month] = monthly_pl.get(month, 0) + trade['income'] | |
chart_data = { | |
'x': list(monthly_pl.keys()), | |
'y': list(monthly_pl.values()), | |
'color': ['#1E90FF'] * len(monthly_pl), | |
'title': 'Monthly Performance', | |
'x_title': 'Month', | |
'y_title': 'Profit/Loss ($)' | |
} | |
return gr.LinePlot(value=chart_data) | |
def update_allocation_chart(allocation): | |
chart_data = { | |
'labels': allocation['labels'], | |
'values': allocation['data'], | |
'color': ['#1E90FF', '#10b981', '#8b5cf6', '#f59e0b'], | |
'title': 'Portfolio Allocation' | |
} | |
fig = px.pie(names=chart_data['labels'], values=chart_data['values'], color_discrete_sequence=chart_data['color'], title=chart_data['title']) | |
legend_html = "\n".join(f""" | |
<div class="mb-3"> | |
<div class="flex justify-between mb-1"> | |
<span class="text-gray-500 dark:text-gray-400 flex items-center"> | |
<span class="h-3 w-3 bg-[{chart_data['color'][i % 4]}] rounded-full mr-2"></span> | |
{label} | |
</span> | |
<span class="font-medium dark:text-white">{data:.1f}%</span> | |
</div> | |
<div class="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700"> | |
<div class="bg-[{chart_data['color'][i % 4]}] h-2 rounded-full" style="width: {data}%"></div> | |
</div> | |
</div> | |
""" for i, (label, data) in enumerate(zip(allocation['labels'], allocation['data']))) | |
return gr.Plot(value=fig), gr.HTML(legend_html) | |
# Main Data Fetch and Update Function | |
def update_dashboard(api_key, api_secret): | |
try: | |
balance = fetch_balance() | |
positions = fetch_open_positions() | |
trades = fetch_trade_history() | |
total_balance = f"${balance:.2f}" | |
open_trades = len(positions) | |
long_count = len([p for p in positions if p['positionSide'] == 'LONG']) | |
today_profit = f"${calculate_today_profit(trades):.2f}" | |
risk_percent = balance and (sum(p.get('positionValue', 0) for p in positions) / balance * 100) or 0 | |
risk_exposure = 'Low' if risk_percent < 20 else 'Medium' if risk_percent < 50 else 'High' | |
exposure_percent = f"{risk_percent:.1f}%" | |
stats = calculate_advanced_stats(trades) | |
allocation = calculate_portfolio_allocation(positions) | |
return ( | |
total_balance, open_trades, f"{long_count} Long β’ {len(positions) - long_count} Short", today_profit, | |
risk_exposure, exposure_percent, update_trading_table(positions, trades), update_advanced_stats(stats), | |
update_performance_chart(trades), update_allocation_chart(allocation), datetime.now().strftime('%I:%M %p'), | |
datetime.now().strftime('%I:%M %p') | |
) | |
except Exception as e: | |
return ( | |
"$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>"), | |
gr.HTML("<div>Error loading stats</div>"), gr.LinePlot(value={'x': [], 'y': [], 'color': [], 'title': 'Monthly Performance'}), | |
(gr.Plot(value=px.pie(names=[], values=[], title='Portfolio Allocation')), gr.HTML("")), "Just now", "Just now" | |
) | |
# Save Credentials Function | |
def save_credentials(new_api_key, new_api_secret, api_key, api_secret): | |
if not new_api_key or not new_api_secret: | |
return gr.Warning("Both API Key and Secret are required."), api_key, api_secret | |
return gr.Info("Credentials saved successfully! Syncing data..."), gr.State(value=new_api_key), gr.State(value=new_api_secret) | |
# Toggle API Form Visibility | |
def toggle_api_form(show_form): | |
return gr.update(visible=show_form) | |
# Gradio Interface | |
with gr.Blocks(title="Nakhoda4X Pro") as demo: | |
# Custom CSS and JS for Tailwind and dark mode | |
demo.css = """ | |
.trading-card:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); } | |
.animate-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } | |
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } | |
.coin-animation { animation: float 3s ease-in-out infinite; } | |
@keyframes float { 0% { transform: translateY(0px); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0px); } } | |
.api-form { max-height: 0; overflow: hidden; transition: max-height 0.3s ease-in-out; } | |
.api-form.open { max-height: 200px; } | |
body { background-color: #f3f4f6; } | |
body.dark { background-color: #111827; } | |
.dark .bg-white { background-color: #1f2937; } | |
.dark .text-gray-800 { color: #ffffff; } | |
.dark .text-gray-500 { color: #9ca3af; } | |
""" | |
demo.js = """ | |
() => [document.body.classList.toggle('dark', window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)] | |
""" | |
# Header | |
with gr.Row(): | |
with gr.Column(scale=1): | |
gr.HTML(""" | |
<div class="bg-white dark:bg-darkCard shadow-sm flex items-center p-4"> | |
<div class="bg-primary w-10 h-10 rounded-xl flex items-center justify-center mr-3 coin-animation"> | |
<i class="fas fa-chart-line text-white text-xl"></i> | |
</div> | |
<h1 class="text-2xl font-bold text-gray-800 dark:text-white">Nakhoda4X <span class="text-primary">Pro</span></h1> | |
</div> | |
""") | |
with gr.Column(scale=1, elem_classes="flex justify-end items-center space-x-6"): | |
theme_toggle = gr.Button(value="Toggle Theme", elem_classes="text-gray-600 dark:text-gray-300") | |
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>') | |
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>') | |
# Main Content | |
with gr.Row(): | |
with gr.Column(): | |
gr.Markdown("### Trading Dashboard") | |
gr.Markdown("Connected to BingX via API") | |
with gr.Row(): | |
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") | |
# Stats Cards | |
with gr.Row(elem_classes="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"): | |
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>") | |
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>") | |
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>") | |
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>") | |
# Charts and Statistics | |
with gr.Row(elem_classes="grid grid-cols-1 lg:grid-cols-3 gap-6"): | |
with gr.Column(scale=2): | |
performance_chart = gr.LinePlot() | |
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>") | |
with gr.Column(scale=1): | |
advanced_stats = gr.HTML() | |
# Trading Activity | |
trading_activity = gr.HTML() | |
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>") | |
# API Connection and Portfolio Allocation | |
with gr.Row(elem_classes="grid grid-cols-1 lg:grid-cols-3 gap-6"): | |
with gr.Column(): | |
api_connection = gr.Blocks() | |
with api_connection: | |
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>") | |
show_form = gr.State(False) | |
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") | |
with gr.Column(elem_classes="api-form", visible=False) as api_form: | |
api_key_input = gr.Textbox(label="API Key", value=api_key.value, type="password") | |
api_secret_input = gr.Textbox(label="API Secret", value=api_secret.value, type="password") | |
save_btn = gr.Button("Save Credentials", elem_classes="w-full py-2 bg-primary text-white rounded-lg") | |
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") | |
with gr.Column(scale=2): | |
portfolio_allocation = gr.Blocks() | |
with portfolio_allocation: | |
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>") | |
allocation_chart, allocation_legend = gr.Plot(), gr.HTML() | |
# Event Handlers | |
toggle_btn.click(fn=toggle_api_form, inputs=[show_form], outputs=[api_form], _js="() => !document.querySelector('.api-form').classList.contains('open')") | |
save_btn.click(fn=save_credentials, inputs=[api_key_input, api_secret_input, api_key, api_secret], outputs=[gr.Markdown(), api_key, api_secret]) | |
demo.load(fn=update_dashboard, inputs=[api_key, api_secret], outputs=[ | |
total_balance, open_trades, trade_types, today_profit, | |
risk_exposure, exposure_percent, trading_activity, advanced_stats, | |
performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None) | |
]) | |
refresh_btn.click(fn=update_dashboard, inputs=[api_key, api_secret], outputs=[ | |
total_balance, open_trades, trade_types, today_profit, | |
risk_exposure, exposure_percent, trading_activity, advanced_stats, | |
performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None) | |
]) | |
sync_now.click(fn=update_dashboard, inputs=[api_key, api_secret], outputs=[ | |
total_balance, open_trades, trade_types, today_profit, | |
risk_exposure, exposure_percent, trading_activity, advanced_stats, | |
performance_chart, (allocation_chart, allocation_legend), gr.State(value=None), gr.State(value=None) | |
]) | |
# Hot Dog Classifier Tab | |
with gr.Tab("Hot Dog Classifier"): | |
gr.Markdown("### Hot Dog? Or Not?") | |
with gr.Row(): | |
input_img = gr.Image(label="Select hot dog candidate", sources=['upload', 'webcam'], type="pil") | |
output_img = gr.Image(label="Processed Image") | |
output_label = gr.Label(label="Result", num_top_classes=2) | |
submit_btn = gr.Button("Classify") | |
submit_btn.click(fn=lambda img: (img, {p["label"]: p["score"] for p in hotdog_pipeline(img)}) if hotdog_pipeline else (None, {"Error": "Classifier unavailable"}), inputs=input_img, outputs=[output_img, output_label]) | |
# Theme Toggle | |
theme_toggle.click(None, _js="() => document.body.classList.toggle('dark')") | |
if __name__ == "__main__": | |
demo.launch() |