Spaces:
Sleeping
Sleeping
| """ | |
| 🤖 Ultra Trading Agent - Gradio Dashboard | |
| ========================================== | |
| Full-featured trading bot with multi-timeframe analysis, | |
| dynamic leverage, SL/TP, and comprehensive backtesting. | |
| """ | |
| import gradio as gr | |
| import pandas as pd | |
| import numpy as np | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| import json | |
| import sys | |
| import os | |
| import traceback | |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) | |
| from trading_bot.indicators import compute_all_indicators, ema, rsi, macd, bollinger_bands, atr | |
| from trading_bot.multi_timeframe import MultiTimeframeAnalyzer, resample_ohlcv, TIMEFRAME_HIERARCHY | |
| from trading_bot.risk_manager import RiskManager | |
| from trading_bot.backtester import Backtester | |
| from trading_bot.data_fetcher import generate_sample_data | |
| # ======================== | |
| # DATA LOADING | |
| # ======================== | |
| def load_data(source: str, symbol: str, days: int, volatility: float): | |
| """Load market data from selected source.""" | |
| if source == "📊 Données Synthétiques (Test)": | |
| df = generate_sample_data(days=days, timeframe='1h', | |
| start_price=45000, volatility=volatility) | |
| info = f"✅ Données synthétiques générées: {len(df)} barres sur {days} jours" | |
| elif source == "🔗 Binance (Live)": | |
| try: | |
| from trading_bot.data_fetcher import fetch_ohlcv_ccxt | |
| df = fetch_ohlcv_ccxt(symbol=symbol, timeframe='1h', | |
| exchange_name='binance', limit=days * 24) | |
| info = f"✅ Données live de Binance: {len(df)} barres pour {symbol}" | |
| except Exception as e: | |
| df = generate_sample_data(days=days, timeframe='1h', start_price=45000, volatility=volatility) | |
| info = f"⚠️ Binance non disponible ({str(e)[:50]}), données synthétiques utilisées" | |
| else: | |
| df = generate_sample_data(days=days, timeframe='1h', start_price=45000, volatility=volatility) | |
| info = f"✅ Données générées: {len(df)} barres" | |
| return df, info | |
| # ======================== | |
| # CHARTING | |
| # ======================== | |
| def create_candlestick_chart(df, trades_df=None, signals_df=None, title="Prix & Signaux"): | |
| """Create an interactive candlestick chart with indicators and trade markers.""" | |
| # Use last 500 bars for visibility | |
| plot_df = df.tail(500).copy() | |
| indicators = compute_all_indicators(plot_df) | |
| fig = make_subplots( | |
| rows=4, cols=1, shared_xaxes=True, | |
| vertical_spacing=0.03, | |
| row_heights=[0.5, 0.15, 0.15, 0.2], | |
| subplot_titles=[title, 'RSI', 'MACD', 'Volume'] | |
| ) | |
| # Candlesticks | |
| fig.add_trace(go.Candlestick( | |
| x=plot_df.index, open=plot_df['open'], high=plot_df['high'], | |
| low=plot_df['low'], close=plot_df['close'], name='Prix', | |
| increasing_line_color='#26a69a', decreasing_line_color='#ef5350' | |
| ), row=1, col=1) | |
| # EMAs | |
| for period, color, name in [(9, '#ff9800', 'EMA 9'), (21, '#2196f3', 'EMA 21'), | |
| (50, '#9c27b0', 'EMA 50'), (200, '#f44336', 'EMA 200')]: | |
| col = f'ema_{period}' | |
| if col in indicators.columns: | |
| fig.add_trace(go.Scatter( | |
| x=indicators.index, y=indicators[col], name=name, | |
| line=dict(color=color, width=1), opacity=0.7 | |
| ), row=1, col=1) | |
| # Bollinger Bands | |
| if 'bb_upper' in indicators.columns: | |
| fig.add_trace(go.Scatter( | |
| x=indicators.index, y=indicators['bb_upper'], name='BB Upper', | |
| line=dict(color='rgba(128,128,128,0.3)', width=1) | |
| ), row=1, col=1) | |
| fig.add_trace(go.Scatter( | |
| x=indicators.index, y=indicators['bb_lower'], name='BB Lower', | |
| line=dict(color='rgba(128,128,128,0.3)', width=1), | |
| fill='tonexty', fillcolor='rgba(128,128,128,0.05)' | |
| ), row=1, col=1) | |
| # Trade markers | |
| if trades_df is not None and len(trades_df) > 0: | |
| # Filter to visible range | |
| visible_trades = trades_df[ | |
| (trades_df.get('entry_time', pd.Series(dtype='datetime64[ns]')).notna()) & | |
| (trades_df.get('exit_time', pd.Series(dtype='datetime64[ns]')).notna()) | |
| ].copy() | |
| if 'entry_time' in visible_trades.columns: | |
| for _, trade in visible_trades.iterrows(): | |
| try: | |
| entry_t = pd.Timestamp(trade['entry_time']) | |
| exit_t = pd.Timestamp(trade['exit_time']) | |
| if entry_t < plot_df.index[0]: | |
| continue | |
| color = '#26a69a' if trade.get('pnl', 0) > 0 else '#ef5350' | |
| marker = '▲' if trade.get('side') == 'LONG' else '▼' | |
| # Entry | |
| fig.add_trace(go.Scatter( | |
| x=[entry_t], y=[trade['entry_price']], | |
| mode='markers+text', text=[marker], | |
| textposition='top center', | |
| marker=dict(color=color, size=10, symbol='triangle-up' if trade.get('side') == 'LONG' else 'triangle-down'), | |
| showlegend=False, | |
| hovertext=f"{trade.get('side','?')} | Entry: ${trade['entry_price']:,.2f} | Lev: {trade.get('leverage',1)}x" | |
| ), row=1, col=1) | |
| # Exit | |
| fig.add_trace(go.Scatter( | |
| x=[exit_t], y=[trade['exit_price']], | |
| mode='markers', | |
| marker=dict(color=color, size=8, symbol='x'), | |
| showlegend=False, | |
| hovertext=f"Exit: ${trade['exit_price']:,.2f} | PnL: ${trade.get('pnl',0):,.2f} | {trade.get('reason','?')}" | |
| ), row=1, col=1) | |
| except: | |
| pass | |
| # Signal arrows | |
| if signals_df is not None and len(signals_df) > 0: | |
| visible_signals = signals_df[signals_df['timestamp'] >= plot_df.index[0]].copy() | |
| entries = visible_signals[visible_signals['action'].str.contains('ENTER', na=False)] | |
| longs = entries[entries['action'] == 'ENTER_LONG'] | |
| shorts = entries[entries['action'] == 'ENTER_SHORT'] | |
| if len(longs) > 0: | |
| fig.add_trace(go.Scatter( | |
| x=longs['timestamp'], y=longs['price'], | |
| mode='markers', name='🟢 Long', | |
| marker=dict(color='#26a69a', size=12, symbol='triangle-up', | |
| line=dict(color='white', width=1)) | |
| ), row=1, col=1) | |
| if len(shorts) > 0: | |
| fig.add_trace(go.Scatter( | |
| x=shorts['timestamp'], y=shorts['price'], | |
| mode='markers', name='🔴 Short', | |
| marker=dict(color='#ef5350', size=12, symbol='triangle-down', | |
| line=dict(color='white', width=1)) | |
| ), row=1, col=1) | |
| # RSI | |
| if 'rsi' in indicators.columns: | |
| fig.add_trace(go.Scatter( | |
| x=indicators.index, y=indicators['rsi'], name='RSI', | |
| line=dict(color='#ff9800', width=1.5) | |
| ), row=2, col=1) | |
| fig.add_hline(y=70, line_dash="dash", line_color="red", opacity=0.5, row=2, col=1) | |
| fig.add_hline(y=30, line_dash="dash", line_color="green", opacity=0.5, row=2, col=1) | |
| fig.add_hrect(y0=30, y1=70, fillcolor="rgba(128,128,128,0.05)", line_width=0, row=2, col=1) | |
| # MACD | |
| if 'macd' in indicators.columns: | |
| fig.add_trace(go.Scatter( | |
| x=indicators.index, y=indicators['macd'], name='MACD', | |
| line=dict(color='#2196f3', width=1.5) | |
| ), row=3, col=1) | |
| fig.add_trace(go.Scatter( | |
| x=indicators.index, y=indicators['macd_signal'], name='Signal', | |
| line=dict(color='#ff5722', width=1.5) | |
| ), row=3, col=1) | |
| colors = ['#26a69a' if v > 0 else '#ef5350' for v in indicators['macd_hist'].fillna(0)] | |
| fig.add_trace(go.Bar( | |
| x=indicators.index, y=indicators['macd_hist'], name='Histogram', | |
| marker_color=colors, opacity=0.5 | |
| ), row=3, col=1) | |
| # Volume | |
| if 'volume' in plot_df.columns: | |
| colors = ['#26a69a' if c >= o else '#ef5350' | |
| for c, o in zip(plot_df['close'], plot_df['open'])] | |
| fig.add_trace(go.Bar( | |
| x=plot_df.index, y=plot_df['volume'], name='Volume', | |
| marker_color=colors, opacity=0.5 | |
| ), row=4, col=1) | |
| fig.update_layout( | |
| template='plotly_dark', | |
| height=900, | |
| showlegend=True, | |
| legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="center", x=0.5), | |
| xaxis_rangeslider_visible=False, | |
| margin=dict(l=60, r=20, t=80, b=20), | |
| paper_bgcolor='#1a1a2e', | |
| plot_bgcolor='#16213e', | |
| font=dict(color='#e0e0e0'), | |
| ) | |
| return fig | |
| def create_equity_chart(equity_data): | |
| """Create equity curve chart.""" | |
| if not equity_data: | |
| fig = go.Figure() | |
| fig.update_layout(template='plotly_dark', title="Pas de données") | |
| return fig | |
| eq_df = pd.DataFrame(equity_data) | |
| fig = make_subplots(rows=2, cols=1, shared_xaxes=True, | |
| vertical_spacing=0.1, row_heights=[0.7, 0.3], | |
| subplot_titles=['💰 Courbe de Capital', '📉 Drawdown']) | |
| # Equity | |
| fig.add_trace(go.Scatter( | |
| x=eq_df['timestamp'], y=eq_df['equity'], name='Capital', | |
| fill='tozeroy', fillcolor='rgba(38, 166, 154, 0.2)', | |
| line=dict(color='#26a69a', width=2) | |
| ), row=1, col=1) | |
| # Drawdown | |
| equity_arr = eq_df['equity'].values | |
| peak = np.maximum.accumulate(equity_arr) | |
| dd = (peak - equity_arr) / peak * 100 | |
| fig.add_trace(go.Scatter( | |
| x=eq_df['timestamp'], y=-dd, name='Drawdown %', | |
| fill='tozeroy', fillcolor='rgba(239, 83, 80, 0.3)', | |
| line=dict(color='#ef5350', width=1.5) | |
| ), row=2, col=1) | |
| fig.update_layout( | |
| template='plotly_dark', height=500, | |
| paper_bgcolor='#1a1a2e', plot_bgcolor='#16213e', | |
| font=dict(color='#e0e0e0'), | |
| showlegend=False, | |
| margin=dict(l=60, r=20, t=40, b=20), | |
| ) | |
| return fig | |
| def create_trades_chart(trades): | |
| """Create trade analysis charts.""" | |
| if not trades: | |
| fig = go.Figure() | |
| fig.update_layout(template='plotly_dark', title="Aucun trade") | |
| return fig | |
| trades_df = pd.DataFrame(trades) | |
| fig = make_subplots(rows=2, cols=2, | |
| subplot_titles=['PnL par Trade', 'Distribution PnL (%)', | |
| 'PnL Cumulé', 'Leverage Utilisé']) | |
| # PnL per trade | |
| colors = ['#26a69a' if p > 0 else '#ef5350' for p in trades_df['pnl']] | |
| fig.add_trace(go.Bar( | |
| x=list(range(len(trades_df))), y=trades_df['pnl'], | |
| marker_color=colors, name='PnL' | |
| ), row=1, col=1) | |
| # PnL distribution | |
| fig.add_trace(go.Histogram( | |
| x=trades_df['pnl_pct'], nbinsx=30, name='PnL %', | |
| marker_color='#2196f3', opacity=0.7 | |
| ), row=1, col=2) | |
| # Cumulative PnL | |
| cum_pnl = trades_df['pnl'].cumsum() | |
| fig.add_trace(go.Scatter( | |
| x=list(range(len(cum_pnl))), y=cum_pnl, name='PnL Cumulé', | |
| fill='tozeroy', fillcolor='rgba(38, 166, 154, 0.2)', | |
| line=dict(color='#26a69a', width=2) | |
| ), row=2, col=1) | |
| # Leverage used | |
| fig.add_trace(go.Scatter( | |
| x=list(range(len(trades_df))), y=trades_df['leverage'], | |
| mode='markers+lines', name='Leverage', | |
| marker=dict(color='#ff9800', size=4), | |
| line=dict(color='#ff9800', width=1) | |
| ), row=2, col=2) | |
| fig.update_layout( | |
| template='plotly_dark', height=600, | |
| paper_bgcolor='#1a1a2e', plot_bgcolor='#16213e', | |
| font=dict(color='#e0e0e0'), | |
| showlegend=False, | |
| margin=dict(l=60, r=20, t=40, b=20), | |
| ) | |
| return fig | |
| # ======================== | |
| # MAIN BACKTEST FUNCTION | |
| # ======================== | |
| def run_backtest(source, symbol, days, volatility, capital, risk_pct, max_leverage, | |
| sl_mult, tp_mult, trailing_pct, use_trailing, min_confidence, | |
| min_confluence, min_rr, tf1, tf2, tf3): | |
| """Run the full backtest and return all visualizations.""" | |
| try: | |
| # Load data | |
| df, data_info = load_data(source, symbol, int(days), float(volatility)) | |
| # Build timeframes list | |
| timeframes = sorted( | |
| list(set([tf1, tf2, tf3])), | |
| key=lambda x: TIMEFRAME_HIERARCHY.get(x, 0) | |
| ) | |
| # Config | |
| config = { | |
| 'risk': { | |
| 'initial_capital': float(capital), | |
| 'risk_per_trade': float(risk_pct) / 100, | |
| 'max_leverage': int(max_leverage), | |
| 'sl_atr_multiplier': float(sl_mult), | |
| 'tp_atr_multiplier': float(tp_mult), | |
| 'trailing_stop_pct': float(trailing_pct) / 100, | |
| 'use_trailing': use_trailing, | |
| 'min_rr_ratio': float(min_rr), | |
| 'sizing_mode': 'fixed', | |
| 'max_drawdown': 0.99, | |
| }, | |
| 'min_confidence': float(min_confidence) / 100, | |
| 'min_confluence': float(min_confluence) / 100, | |
| } | |
| # Run backtest | |
| bt = Backtester(config) | |
| results = bt.run(df, base_tf=timeframes[0], timeframes=timeframes) | |
| # Get DataFrames | |
| equity_data = results.get('equity_curve', []) | |
| trades = results.get('trades', []) | |
| signals_df = pd.DataFrame(results.get('signals', [])) | |
| trades_df = pd.DataFrame(trades) | |
| # Create charts | |
| price_chart = create_candlestick_chart(df, trades_df, signals_df, | |
| f"📈 {symbol} - Multi-Timeframe Analysis") | |
| equity_chart = create_equity_chart(equity_data) | |
| trades_chart = create_trades_chart(trades) | |
| # Stats summary | |
| stats = results | |
| total_trades = stats.get('total_trades', 0) | |
| # Format numbers with capping for display | |
| final_capital = stats.get('capital', capital) | |
| total_pnl = stats.get('total_pnl', 0) | |
| return_pct = stats.get('return_pct', 0) | |
| # Cap display for extreme compounding | |
| if abs(return_pct) > 1e6: | |
| return_str = f"{'+'if return_pct>0 else ''}{return_pct:.2e}%" | |
| capital_str = f"${final_capital:.2e}" | |
| pnl_str = f"${total_pnl:.2e}" | |
| else: | |
| return_str = f"{'+'if return_pct>0 else ''}{return_pct:.1f}%" | |
| capital_str = f"${final_capital:,.2f}" | |
| pnl_str = f"${total_pnl:,.2f}" | |
| summary = f""" | |
| ## 📊 Résultats du Backtest | |
| | Métrique | Valeur | | |
| |----------|--------| | |
| | 📅 **Période** | {df.index[0].strftime('%Y-%m-%d')} → {df.index[-1].strftime('%Y-%m-%d')} | | |
| | 📊 **Barres analysées** | {len(df):,} | | |
| | 🕐 **Timeframes** | {', '.join(timeframes)} | | |
| | 💰 **Capital Initial** | ${float(capital):,.2f} | | |
| | 💰 **Capital Final** | {capital_str} | | |
| | 📈 **PnL Total** | {pnl_str} | | |
| | 📊 **Return** | {return_str} | | |
| | 🔢 **Trades Total** | {total_trades} | | |
| | ✅ **Win Rate** | {stats.get('win_rate', 0):.1%} | | |
| | 📊 **Profit Factor** | {stats.get('profit_factor', 0):.2f} | | |
| | 📉 **Max Drawdown** | {stats.get('max_drawdown', 0):.1%} | | |
| | 📈 **Sharpe Ratio** | {stats.get('sharpe_ratio', 0):.2f} | | |
| | 🏆 **Best Trade** | ${stats.get('best_trade', 0):,.2f} | | |
| | 💀 **Worst Trade** | ${stats.get('worst_trade', 0):,.2f} | | |
| | 💵 **Avg Win** | ${stats.get('avg_win', 0):,.2f} | | |
| | 💸 **Avg Loss** | ${stats.get('avg_loss', 0):,.2f} | | |
| | ⚡ **Avg Leverage** | {stats.get('avg_leverage', 0):.1f}x | | |
| | 💳 **Total Fees** | ${stats.get('total_fees', 0):,.2f} | | |
| | 📈 **Longs** | {stats.get('trades_long', 0)} | | |
| | 📉 **Shorts** | {stats.get('trades_short', 0)} | | |
| ### ℹ️ {data_info} | |
| """ | |
| # Trades table | |
| if len(trades_df) > 0: | |
| display_cols = ['side', 'entry_price', 'exit_price', 'pnl', 'pnl_pct', 'leverage', 'reason'] | |
| available_cols = [c for c in display_cols if c in trades_df.columns] | |
| table_df = trades_df[available_cols].copy() | |
| if 'pnl' in table_df.columns: | |
| table_df['pnl'] = table_df['pnl'].apply(lambda x: f"${x:,.2f}" if abs(x) < 1e8 else f"${x:.2e}") | |
| if 'pnl_pct' in table_df.columns: | |
| table_df['pnl_pct'] = table_df['pnl_pct'].apply(lambda x: f"{x:.1f}%") | |
| if 'entry_price' in table_df.columns: | |
| table_df['entry_price'] = table_df['entry_price'].apply(lambda x: f"${x:,.2f}") | |
| if 'exit_price' in table_df.columns: | |
| table_df['exit_price'] = table_df['exit_price'].apply(lambda x: f"${x:,.2f}") | |
| else: | |
| table_df = pd.DataFrame({"Info": ["Aucun trade exécuté"]}) | |
| return price_chart, equity_chart, trades_chart, summary, table_df | |
| except Exception as e: | |
| error_fig = go.Figure() | |
| error_fig.update_layout(template='plotly_dark', title=f"Erreur: {str(e)}") | |
| error_msg = f"## ❌ Erreur\n\n```\n{traceback.format_exc()}\n```" | |
| return error_fig, error_fig, error_fig, error_msg, pd.DataFrame({"Erreur": [str(e)]}) | |
| # ======================== | |
| # LIVE ANALYSIS | |
| # ======================== | |
| def run_live_analysis(symbol, days, tf1, tf2, tf3): | |
| """Run live analysis on current data (simulated).""" | |
| try: | |
| df = generate_sample_data(days=int(days), timeframe='1h', start_price=45000, volatility=0.025) | |
| timeframes = sorted(list(set([tf1, tf2, tf3])), | |
| key=lambda x: TIMEFRAME_HIERARCHY.get(x, 0)) | |
| # Get latest analysis | |
| tf_data = {timeframes[0]: df} | |
| for tf in timeframes: | |
| if tf != timeframes[0] and TIMEFRAME_HIERARCHY.get(tf, 0) > TIMEFRAME_HIERARCHY.get(timeframes[0], 0): | |
| tf_data[tf] = resample_ohlcv(df, tf) | |
| analyzer = MultiTimeframeAnalyzer(timeframes) | |
| result = analyzer.analyze(tf_data) | |
| # Compute ATR for SL/TP | |
| indicators = compute_all_indicators(df) | |
| current_price = df.iloc[-1]['close'] | |
| atr_val = indicators['atr'].iloc[-1] | |
| rm = RiskManager({'initial_capital': 10000, 'max_leverage': 20}) | |
| direction = result['direction'] | |
| confidence = result['confidence'] | |
| if direction in ['LONG', 'SHORT']: | |
| sl, tp = rm.calculate_sl_tp(current_price, atr_val, direction) | |
| lev = rm.calculate_leverage(atr_val/current_price, confidence, | |
| result.get('signals', {}).get(timeframes[0], {}).get('volatility', 0.5)) | |
| else: | |
| sl, tp = current_price, current_price | |
| lev = 1 | |
| direction_emoji = {"LONG": "🟢 LONG", "SHORT": "🔴 SHORT", "NEUTRAL": "⚪ NEUTRE"} | |
| analysis = f""" | |
| ## 🔍 Analyse Multi-Timeframe en Direct | |
| ### Signal: {direction_emoji.get(direction, '⚪ NEUTRE')} | |
| | Paramètre | Valeur | | |
| |-----------|--------| | |
| | **Prix Actuel** | ${current_price:,.2f} | | |
| | **Direction** | {direction} | | |
| | **Confiance** | {confidence:.0%} | | |
| | **Confluence** | {result.get('confluence', 0):.0%} | | |
| | **Score Combiné** | {result.get('combined_score', 0):.3f} | | |
| | **Leverage Suggéré** | {lev}x | | |
| | **Stop Loss** | ${sl:,.2f} ({abs(current_price-sl)/current_price*100:.2f}%) | | |
| | **Take Profit** | ${tp:,.2f} ({abs(tp-current_price)/current_price*100:.2f}%) | | |
| | **ATR** | ${atr_val:,.2f} | | |
| | **R:R Ratio** | {abs(tp-current_price)/abs(current_price-sl):.2f} | | |
| ### Analyse par Timeframe | |
| """ | |
| for tf, sig in result.get('signals', {}).items(): | |
| bias = sig.get('bias', 'NEUTRAL') | |
| bias_emoji = {"BULLISH": "🟢", "BEARISH": "🔴", "NEUTRAL": "⚪"} | |
| analysis += f"**{tf}** {bias_emoji.get(bias, '⚪')} {bias} | " | |
| analysis += f"Trend: {sig.get('trend', 0):.2f} | " | |
| analysis += f"Mom: {sig.get('momentum', 0):.2f} | " | |
| analysis += f"Str: {sig.get('strength', 0):.2f}\n\n" | |
| # Chart | |
| chart = create_candlestick_chart(df, title=f"📊 {symbol} - Analyse Live") | |
| # Add SL/TP lines | |
| if direction != 'NEUTRAL': | |
| chart.add_hline(y=sl, line_dash="dash", line_color="red", opacity=0.7, | |
| annotation_text=f"SL ${sl:,.0f}", row=1, col=1) | |
| chart.add_hline(y=tp, line_dash="dash", line_color="green", opacity=0.7, | |
| annotation_text=f"TP ${tp:,.0f}", row=1, col=1) | |
| chart.add_hline(y=current_price, line_dash="dot", line_color="yellow", opacity=0.5, | |
| annotation_text=f"Prix ${current_price:,.0f}", row=1, col=1) | |
| return chart, analysis | |
| except Exception as e: | |
| fig = go.Figure() | |
| fig.update_layout(template='plotly_dark') | |
| return fig, f"## ❌ Erreur\n\n{str(e)}" | |
| # ======================== | |
| # GRADIO UI | |
| # ======================== | |
| custom_css = """ | |
| .gradio-container { | |
| max-width: 1400px !important; | |
| background-color: #0a0a1a !important; | |
| } | |
| .dark { | |
| background-color: #0a0a1a !important; | |
| } | |
| h1 { | |
| text-align: center; | |
| background: linear-gradient(90deg, #26a69a, #2196f3, #9c27b0); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| font-size: 2.5em !important; | |
| margin-bottom: 0 !important; | |
| } | |
| .subtitle { | |
| text-align: center; | |
| color: #888; | |
| font-size: 1.1em; | |
| margin-bottom: 20px; | |
| } | |
| """ | |
| with gr.Blocks(title="🤖 Ultra Trading Agent") as demo: | |
| gr.Markdown("# 🤖 Ultra Trading Agent") | |
| gr.Markdown("<p class='subtitle'>Bot de Trading Multi-Timeframe avec Gestion Dynamique du Risque</p>") | |
| with gr.Tabs(): | |
| # ===================== | |
| # TAB 1: BACKTEST | |
| # ===================== | |
| with gr.Tab("📊 Backtest"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### ⚙️ Configuration") | |
| with gr.Accordion("📈 Données", open=True): | |
| data_source = gr.Dropdown( | |
| choices=["📊 Données Synthétiques (Test)", "🔗 Binance (Live)"], | |
| value="📊 Données Synthétiques (Test)", | |
| label="Source de Données" | |
| ) | |
| symbol_input = gr.Textbox(value="BTC/USDT", label="Symbole") | |
| days_input = gr.Slider(30, 730, 180, step=30, label="Jours de données") | |
| volatility_input = gr.Slider(0.005, 0.05, 0.025, step=0.005, label="Volatilité (synthétique)") | |
| with gr.Accordion("💰 Capital & Risque", open=True): | |
| capital_input = gr.Number(value=10000, label="Capital Initial ($)") | |
| risk_input = gr.Slider(0.5, 5, 2, step=0.5, label="Risque par Trade (%)") | |
| max_lev_input = gr.Slider(1, 50, 20, step=1, label="Leverage Maximum") | |
| min_rr_input = gr.Slider(1, 5, 1.5, step=0.5, label="R:R Minimum") | |
| with gr.Accordion("🎯 Stop-Loss & Take-Profit", open=True): | |
| sl_mult = gr.Slider(0.5, 3, 1.5, step=0.1, label="SL (× ATR)") | |
| tp_mult = gr.Slider(1, 6, 3.0, step=0.5, label="TP (× ATR)") | |
| trailing_pct = gr.Slider(0.5, 5, 1.5, step=0.5, label="Trailing Stop (%)") | |
| use_trailing = gr.Checkbox(value=True, label="Activer Trailing Stop") | |
| with gr.Accordion("🕐 Timeframes", open=True): | |
| tf1 = gr.Dropdown(choices=['5m','15m','30m','1h','2h','4h','6h','8h','12h','1d'], | |
| value='1h', label="Timeframe 1 (Entrée)") | |
| tf2 = gr.Dropdown(choices=['15m','30m','1h','2h','4h','6h','8h','12h','1d','3d','1w'], | |
| value='4h', label="Timeframe 2") | |
| tf3 = gr.Dropdown(choices=['1h','4h','6h','8h','12h','1d','3d','1w','1M'], | |
| value='1d', label="Timeframe 3") | |
| with gr.Accordion("🔧 Seuils de Signal", open=False): | |
| min_conf = gr.Slider(10, 80, 30, step=5, label="Confiance Min (%)") | |
| min_confl = gr.Slider(30, 100, 50, step=10, label="Confluence Min (%)") | |
| run_btn = gr.Button("🚀 Lancer le Backtest", variant="primary", size="lg") | |
| with gr.Column(scale=3): | |
| summary_output = gr.Markdown("*Configurez les paramètres et lancez le backtest...*") | |
| price_chart = gr.Plot(label="Graphique des Prix & Signaux") | |
| with gr.Row(): | |
| equity_chart = gr.Plot(label="Courbe de Capital") | |
| trades_chart = gr.Plot(label="Analyse des Trades") | |
| trades_table = gr.Dataframe(label="📋 Détail des Trades", wrap=True) | |
| run_btn.click( | |
| fn=run_backtest, | |
| inputs=[data_source, symbol_input, days_input, volatility_input, | |
| capital_input, risk_input, max_lev_input, | |
| sl_mult, tp_mult, trailing_pct, use_trailing, | |
| min_conf, min_confl, min_rr_input, tf1, tf2, tf3], | |
| outputs=[price_chart, equity_chart, trades_chart, summary_output, trades_table] | |
| ) | |
| # ===================== | |
| # TAB 2: LIVE ANALYSIS | |
| # ===================== | |
| with gr.Tab("🔍 Analyse Live"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 🔍 Configuration") | |
| live_symbol = gr.Textbox(value="BTC/USDT", label="Symbole") | |
| live_days = gr.Slider(7, 90, 30, step=7, label="Jours de données") | |
| live_tf1 = gr.Dropdown(choices=['5m','15m','30m','1h','4h'], | |
| value='1h', label="TF Entrée") | |
| live_tf2 = gr.Dropdown(choices=['1h','4h','8h','12h','1d'], | |
| value='4h', label="TF Moyen") | |
| live_tf3 = gr.Dropdown(choices=['4h','1d','3d','1w'], | |
| value='1d', label="TF Long") | |
| analyze_btn = gr.Button("🔍 Analyser", variant="primary", size="lg") | |
| with gr.Column(scale=3): | |
| live_analysis = gr.Markdown("*Cliquez sur Analyser pour obtenir le signal en direct...*") | |
| live_chart = gr.Plot(label="Graphique Live") | |
| analyze_btn.click( | |
| fn=run_live_analysis, | |
| inputs=[live_symbol, live_days, live_tf1, live_tf2, live_tf3], | |
| outputs=[live_chart, live_analysis] | |
| ) | |
| # ===================== | |
| # TAB 3: DOCUMENTATION | |
| # ===================== | |
| with gr.Tab("📚 Guide"): | |
| gr.Markdown(""" | |
| # 📚 Guide du Trading Agent | |
| ## 🏗️ Architecture | |
| Ce bot utilise une **analyse multi-timeframe** pour identifier les meilleures opportunités de trading : | |
| ### 1. Indicateurs Techniques | |
| - **EMAs** (9, 21, 50, 200) — Identification de tendance | |
| - **RSI** (14) — Momentum et conditions de sur-achat/sur-vente | |
| - **MACD** (12, 26, 9) — Momentum et croisements | |
| - **Bollinger Bands** (20, 2σ) — Volatilité et niveaux extrêmes | |
| - **ATR** (14) — Calcul dynamique de SL/TP | |
| - **ADX** (14) — Force de la tendance | |
| - **Stochastique** (14, 3) — Momentum oscillant | |
| - **SuperTrend** (10, 3) — Direction de tendance | |
| - **Ichimoku Cloud** — Support/résistance dynamique | |
| ### 2. Analyse Multi-Timeframe | |
| Le bot analyse **3 timeframes simultanément** : | |
| - **TF Bas** (ex: 1h) — Timing d'entrée précis | |
| - **TF Moyen** (ex: 4h) — Confirmation de tendance | |
| - **TF Haut** (ex: 1D) — Direction générale du marché | |
| La **confluence** entre les timeframes détermine la force du signal. | |
| ### 3. Signaux de Trading | |
| - **🟢 LONG** — Acheter (toutes les TFs sont haussières) | |
| - **🔴 SHORT** — Vendre (toutes les TFs sont baissières) | |
| - **⚪ HOLD** — Pas de position (signal mixte ou faible) | |
| ### 4. Gestion du Risque | |
| - **Leverage Dynamique** — Ajusté en fonction de la volatilité (ATR), la confiance du signal, et le drawdown | |
| - **Position Sizing** — Basé sur le % de risque par trade | |
| - **Stop-Loss** — ATR × multiplicateur (par défaut 1.5) | |
| - **Take-Profit** — ATR × multiplicateur (par défaut 3.0) | |
| - **Trailing Stop** — S'active après un profit minimum, suit le prix | |
| - **Protection Max Drawdown** — Arrête le trading si le drawdown dépasse 15% | |
| - **Cooldown** — Pause après une série de pertes consécutives | |
| ### 5. Position Management | |
| - Maximum 3 positions simultanées | |
| - Retournement automatique si le signal s'inverse fortement | |
| - Fermeture sur SL, TP, trailing stop, ou signal contraire | |
| ## ⚡ Paramètres Clés | |
| | Paramètre | Description | Défaut | | |
| |-----------|-------------|--------| | |
| | Risk/Trade | % du capital risqué par trade | 2% | | |
| | Max Leverage | Leverage maximum autorisé | 20x | | |
| | SL ATR Mult | Multiplicateur ATR pour le stop-loss | 1.5x | | |
| | TP ATR Mult | Multiplicateur ATR pour le take-profit | 3.0x | | |
| | Min R:R | Ratio risque/récompense minimum | 1.5:1 | | |
| | Min Confiance | Score minimum pour entrer | 30% | | |
| | Min Confluence | % de TFs en accord | 50% | | |
| ## ⚠️ Avertissement | |
| Ce bot est un outil **éducatif et de recherche**. Le trading comporte des risques significatifs. | |
| Les performances passées ne garantissent pas les résultats futurs. | |
| **N'investissez jamais plus que ce que vous pouvez vous permettre de perdre.** | |
| """) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860, | |
| theme=gr.themes.Soft(primary_hue="teal", secondary_hue="blue"), | |
| css=custom_css) | |