Team Ai
Apppublic

javitechjkd/backtestingv2

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
visualization.py412 linesDownload Raw Back to ui
1"""2Visualización de gráficos - Estilo TradingView3"""4 5import plotly.graph_objects as go6from plotly.subplots import make_subplots7import pandas as pd8import numpy as np9from typing import List, Optional, Dict, Any10import sys11from pathlib import Path12 13# Add parent directory for imports14sys.path.insert(0, str(Path(__file__).parent.parent.parent))15 16from backtesting_app.algorithms.base import AlgorithmResult, TrendState17 18 19# Colores estilo TradingView20COLORS = {21    'background': '#131722',22    'grid': '#1e222d',23    'text': '#787b86',24    'bullish': '#26a69a',25    'bearish': '#ef5350',26    'bullish_bg': 'rgba(38, 166, 154, 0.15)',27    'bearish_bg': 'rgba(239, 83, 80, 0.15)',28    'support': '#26a69a',      # Verde como en TradingView29    'resistance': '#ef5350',   # Rojo como en TradingView30    'signal_long': '#00e676',31    'signal_short': '#ff1744',32    'equity': '#42a5f5',33}34 35 36class ChartBuilder:37    """Constructor de gráficos estilo TradingView"""38    39    def __init__(self, title: str = ""):40        self.title = title41        self.fig = None42    43    def create_candlestick_chart(44        self,45        df: pd.DataFrame,46        algo_result: Optional[AlgorithmResult] = None,47        show_signals: bool = True,48        show_levels: bool = True,49        show_background: bool = True,50        height: int = 70051    ) -> go.Figure:52        """53        Crea gráfico de velas con indicadores del algoritmo54        """55        # Crear figura56        self.fig = go.Figure()57        58        # Fondos de tendencia (primero para que quede detrás)59        if show_background and algo_result:60            self._add_trend_backgrounds(df, algo_result)61        62        # Velas63        self._add_candlesticks(df)64        65        # Niveles de soporte/resistencia66        if show_levels and algo_result:67            self._add_support_resistance(df, algo_result)68        69        # Señales70        if show_signals and algo_result:71            self._add_signals(df, algo_result)72        73        # Layout74        self._apply_dark_theme(height)75        76        return self.fig77    78    def _add_candlesticks(self, df: pd.DataFrame):79        """Añade las velas al gráfico"""80        self.fig.add_trace(go.Candlestick(81            x=df.index,82            open=df['open'],83            high=df['high'],84            low=df['low'],85            close=df['close'],86            name='OHLC',87            increasing=dict(line=dict(color=COLORS['bullish'], width=1), 88                          fillcolor=COLORS['bullish']),89            decreasing=dict(line=dict(color=COLORS['bearish'], width=1), 90                          fillcolor=COLORS['bearish']),91            showlegend=False92        ))93    94    def _add_trend_backgrounds(self, df: pd.DataFrame, algo_result: AlgorithmResult):95        """Añade fondos de color según la tendencia - rectángulo por cada vela para evitar huecos"""96        97        if not algo_result.states:98            return99            100        y_min = df['low'].min() * 0.99101        y_max = df['high'].max() * 1.01102        103        # Crear un rectángulo por cada vela para evitar huecos104        for i, state in enumerate(algo_result.states):105            if i >= len(df):106                break107            108            color = COLORS['bullish_bg'] if state.trend == TrendState.BULLISH else COLORS['bearish_bg']109            110            # Para el eje X, usamos índices numéricos si es necesario111            if i == 0:112                x0 = df.index[0]113            else:114                # Punto medio entre la vela anterior y la actual115                x0 = df.index[i]116            117            if i == len(df) - 1:118                x1 = df.index[-1]119            else:120                x1 = df.index[min(i + 1, len(df) - 1)]121            122            self.fig.add_vrect(123                x0=x0,124                x1=x1,125                fillcolor=color,126                layer="below",127                line_width=0128            )129    130    def _add_support_resistance(self, df: pd.DataFrame, algo_result: AlgorithmResult):131        """Añade líneas de soporte y resistencia - formato original con step"""132        133        # Construir arrays de soporte y resistencia134        supports = []135        resistances = []136        137        for i, state in enumerate(algo_result.states):138            if i < len(df):139                supports.append(state.support)140                resistances.append(state.resistance)141        142        # Rellenar None con el valor anterior (forward fill manual)143        last_sup = None144        last_res = None145        for i in range(len(supports)):146            if supports[i] is not None:147                last_sup = supports[i]148            else:149                supports[i] = last_sup150            151            if resistances[i] is not None:152                last_res = resistances[i]153            else:154                resistances[i] = last_res155        156        # Soporte - línea verde con diagonales (como TradingView)157        self.fig.add_trace(go.Scatter(158            x=df.index[:len(supports)],159            y=supports,160            mode='lines',161            name='Soporte',162            line=dict(color=COLORS['support'], width=2),163            hovertemplate='Soporte: %{y:.2f}<extra></extra>'164        ))165        166        # Resistencia - línea roja con diagonales (como TradingView)167        self.fig.add_trace(go.Scatter(168            x=df.index[:len(resistances)],169            y=resistances,170            mode='lines',171            name='Resistencia',172            line=dict(color=COLORS['resistance'], width=2),173            hovertemplate='Resistencia: %{y:.2f}<extra></extra>'174        ))175    176    def _add_signals(self, df: pd.DataFrame, algo_result: AlgorithmResult):177        """Añade marcadores de señales de compra/venta"""178        179        long_x, long_y = [], []180        short_x, short_y = [], []181        182        for i, state in enumerate(algo_result.states):183            if i < len(df) and state.signal:184                if state.signal.signal_type == 'LONG':185                    long_x.append(df.index[i])186                    long_y.append(df.iloc[i]['low'] * 0.998)187                elif state.signal.signal_type == 'SHORT':188                    short_x.append(df.index[i])189                    short_y.append(df.iloc[i]['high'] * 1.002)190        191        # Señales LONG192        if long_x:193            self.fig.add_trace(go.Scatter(194                x=long_x,195                y=long_y,196                mode='markers',197                name='LONG',198                marker=dict(199                    symbol='triangle-up',200                    size=15,201                    color=COLORS['signal_long'],202                    line=dict(width=1, color='white')203                ),204                hovertemplate='LONG<extra></extra>'205            ))206        207        # Señales SHORT208        if short_x:209            self.fig.add_trace(go.Scatter(210                x=short_x,211                y=short_y,212                mode='markers',213                name='SHORT',214                marker=dict(215                    symbol='triangle-down',216                    size=15,217                    color=COLORS['signal_short'],218                    line=dict(width=1, color='white')219                ),220                hovertemplate='SHORT<extra></extra>'221            ))222    223    def _apply_dark_theme(self, height: int):224        """Aplica tema oscuro estilo TradingView"""225        self.fig.update_layout(226            title=dict(227                text=self.title,228                font=dict(color='white', size=16),229                x=0.5230            ),231            template='plotly_dark',232            paper_bgcolor=COLORS['background'],233            plot_bgcolor=COLORS['background'],234            height=height,235            margin=dict(l=50, r=50, t=50, b=50),236            xaxis=dict(237                gridcolor=COLORS['grid'],238                showgrid=True,239                rangeslider=dict(visible=False),240                type='date'241            ),242            yaxis=dict(243                gridcolor=COLORS['grid'],244                showgrid=True,245                side='right'246            ),247            legend=dict(248                yanchor="top",249                y=0.99,250                xanchor="left",251                x=0.01,252                bgcolor='rgba(0,0,0,0.5)',253                font=dict(color='white')254            ),255            hovermode='x unified'256        )257 258 259def create_candlestick_chart(260    df: pd.DataFrame,261    algo_result: Optional[AlgorithmResult] = None,262    title: str = "",263    show_signals: bool = True,264    show_levels: bool = True,265    show_background: bool = True,266    height: int = 700267) -> go.Figure:268    """269    Función helper para crear gráfico de velas270    """271    builder = ChartBuilder(title)272    return builder.create_candlestick_chart(273        df, algo_result, show_signals, show_levels, show_background, height274    )275 276 277def create_equity_chart(278    equity_curve: pd.Series,279    title: str = "Curva de Equity",280    height: int = 400281) -> go.Figure:282    """283    Crea gráfico de curva de equity284    """285    fig = go.Figure()286    287    # Línea de equity288    fig.add_trace(go.Scatter(289        x=equity_curve.index if hasattr(equity_curve, 'index') else list(range(len(equity_curve))),290        y=equity_curve.values,291        mode='lines',292        name='Equity',293        line=dict(color=COLORS['equity'], width=2),294        fill='tozeroy',295        fillcolor='rgba(66, 165, 245, 0.2)'296    ))297    298    # Línea de capital inicial299    initial = equity_curve.iloc[0] if len(equity_curve) > 0 else 0300    fig.add_hline(301        y=initial,302        line_dash="dash",303        line_color="gray",304        annotation_text=f"Capital inicial: ${initial:,.0f}",305        annotation_position="top left"306    )307    308    # Layout309    fig.update_layout(310        title=dict(text=title, font=dict(color='white', size=14), x=0.5),311        template='plotly_dark',312        paper_bgcolor=COLORS['background'],313        plot_bgcolor=COLORS['background'],314        height=height,315        margin=dict(l=50, r=50, t=50, b=30),316        xaxis=dict(gridcolor=COLORS['grid'], showgrid=True),317        yaxis=dict(318            gridcolor=COLORS['grid'],319            showgrid=True,320            side='right',321            tickformat='$,.0f'322        ),323        showlegend=False,324        hovermode='x unified'325    )326    327    return fig328 329 330def create_drawdown_chart(331    equity_curve: pd.Series,332    title: str = "Drawdown",333    height: int = 200334) -> go.Figure:335    """336    Crea gráfico de drawdown337    """338    # Calcular drawdown339    rolling_max = equity_curve.expanding().max()340    drawdown = ((equity_curve - rolling_max) / rolling_max) * 100341    342    fig = go.Figure()343    344    fig.add_trace(go.Scatter(345        x=drawdown.index if hasattr(drawdown, 'index') else list(range(len(drawdown))),346        y=drawdown.values,347        mode='lines',348        name='Drawdown',349        line=dict(color=COLORS['bearish'], width=1),350        fill='tozeroy',351        fillcolor='rgba(239, 83, 80, 0.3)'352    ))353    354    fig.update_layout(355        title=dict(text=title, font=dict(color='white', size=12), x=0.5),356        template='plotly_dark',357        paper_bgcolor=COLORS['background'],358        plot_bgcolor=COLORS['background'],359        height=height,360        margin=dict(l=50, r=50, t=40, b=20),361        xaxis=dict(gridcolor=COLORS['grid'], showgrid=True),362        yaxis=dict(363            gridcolor=COLORS['grid'],364            showgrid=True,365            side='right',366            tickformat='.1f',367            ticksuffix='%'368        ),369        showlegend=False370    )371    372    return fig373 374 375def create_multi_asset_comparison_chart(376    results: Dict[str, Any],377    metric: str = 'total_return_percent',378    title: str = "Comparación Multi-Activo",379    height: int = 400380) -> go.Figure:381    """382    Crea gráfico de barras comparando múltiples activos383    """384    symbols = list(results.keys())385    values = [getattr(results[s], metric) for s in symbols]386    387    colors = [COLORS['bullish'] if v >= 0 else COLORS['bearish'] for v in values]388    389    fig = go.Figure()390    391    fig.add_trace(go.Bar(392        x=symbols,393        y=values,394        marker_color=colors,395        text=[f"{v:.1f}%" if 'percent' in metric else f"${v:,.0f}" for v in values],396        textposition='outside'397    ))398    399    fig.update_layout(400        title=dict(text=title, font=dict(color='white', size=14), x=0.5),401        template='plotly_dark',402        paper_bgcolor=COLORS['background'],403        plot_bgcolor=COLORS['background'],404        height=height,405        margin=dict(l=50, r=50, t=50, b=50),406        xaxis=dict(gridcolor=COLORS['grid']),407        yaxis=dict(gridcolor=COLORS['grid'], tickformat='.1f' if 'percent' in metric else ',.0f'),408        showlegend=False409    )410    411    return fig412