Team Ai
Apppublic

diegobeyl/backtesting

sourceHugging Faceupdated 9mo agoView on Hugging Face
2likes
app.py643 linesDownload Raw Back to backtesting_app
1"""2AriasCampos Backtester3Aplicación de backtesting para algoritmos de trading estructural4 5Algoritmos disponibles:6- FAST (AriasCampos 5.2): Doble estructura simultánea7- SLOW (AriasCampos Slow): Estructura única con sensibilidad8 9Autor: Javier10Versión: 2.0 - Arquitectura Modular11"""12 13import streamlit as st14import sys15import os16import logging17from pathlib import Path18 19# Añadir el directorio raíz al path para permitir imports absolutos20root_dir = Path(__file__).resolve().parent.parent21if str(root_dir) not in sys.path:22    sys.path.insert(0, str(root_dir))23 24# =============================================================================25# CONFIGURACIÓN DE LOGGING - Evitar duplicados26# =============================================================================27# Deshabilitar loggers de terceros28for logger_name in ['streamlit', 'urllib3', 'matplotlib', 'PIL']:29    logging.getLogger(logger_name).propagate = False30    logging.getLogger(logger_name).handlers = []31 32from backtesting_app.ui import render_strategies_tab33from backtesting_app.ui.screener import render_screener_tab34 35# =============================================================================36# CONFIGURACIÓN DE LA PÁGINA37# =============================================================================38 39st.set_page_config(40    page_title="AriasCampos Backtester",41    page_icon="📈",42    layout="wide",43    initial_sidebar_state="expanded"44)45 46# =============================================================================47# ESTILOS CSS48# =============================================================================49 50st.markdown("""51<style>52    /* Tema oscuro general */53    .stApp {54        background-color: #0e1117;55    }56    57    /* Headers */58    h1, h2, h3 {59        color: #ffffff !important;60    }61    62    h5 {63        color: #ffffff !important;64        font-size: 1.1rem !important;65        margin-bottom: 0.5rem !important;66    }67    68    /* Sidebar */69    .css-1d391kg {70        background-color: #131722;71    }72    73    /* Métricas */74    [data-testid="stMetricValue"] {75        font-size: 1.8rem;76        font-weight: bold;77        color: #2962ff !important;78    }79    80    [data-testid="stMetricLabel"] {81        color: #787b86;82        font-size: 0.95rem;83    }84    85    [data-testid="stMetricDelta"] {86        font-size: 0.9rem;87    }88    89    /* Botones - Mejora principal */90    .stButton {91        min-height: 60px;92        margin-bottom: 0.5rem;93    }94    95    .stButton > button {96        background: linear-gradient(135deg, #2962ff 0%, #1e4bd8 100%);97        color: white;98        border: 2px solid #2962ff;99        padding: 0.75rem 1rem;100        border-radius: 6px;101        font-weight: bold;102        font-size: 0.95rem;103        height: 100%;104        width: 100%;105        transition: all 0.3s ease;106        white-space: pre-wrap;107        word-wrap: break-word;108    }109    110    .stButton > button:hover {111        background: linear-gradient(135deg, #1e4bd8 0%, #2962ff 100%);112        box-shadow: 0 4px 12px rgba(41, 98, 255, 0.4);113        transform: translateY(-2px);114        border-color: #2962ff;115    }116    117    .stButton > button:active {118        background: linear-gradient(135deg, #0d47a1 0%, #1e4bd8 100%);119        box-shadow: 0 2px 6px rgba(41, 98, 255, 0.2);120        transform: translateY(0);121    }122    123    /* Tabs */124    .stTabs [data-baseweb="tab-list"] {125        gap: 8px;126        background-color: transparent;127        border-bottom: 2px solid #1e222d;128        padding: 0;129    }130    131    .stTabs [data-baseweb="tab"] {132        background-color: #1e222d;133        border-radius: 6px 6px 0 0;134        padding: 12px 24px;135        color: #787b86;136        border: none;137        font-weight: 600;138        transition: all 0.3s ease;139    }140    141    .stTabs [data-baseweb="tab"]:hover {142        background-color: #262d38;143        color: #b0b5be;144    }145    146    .stTabs [aria-selected="true"] {147        background-color: #2962ff;148        color: white;149        box-shadow: 0 2px 8px rgba(41, 98, 255, 0.3);150    }151    152    /* DataFrames */153    .dataframe {154        background-color: #131722 !important;155    }156    157    /* Expanders */158    .streamlit-expanderHeader {159        background-color: #1e222d;160        border-radius: 4px;161    }162    163    /* Info boxes */164    .stAlert {165        background-color: #1e222d;166        border: 1px solid #2962ff;167        border-radius: 6px;168        padding: 1rem;169    }170    171    /* Dividers */172    hr {173        background-color: #1e222d !important;174        border: none;175        height: 2px;176        margin: 1rem 0;177    }178    179    /* Inputs */180    .stSelectbox, .stMultiSelect, .stNumberInput, .stSlider {181        color: white;182    }183    184    /* Caption text */185    small, .stCaption {186        color: #787b86 !important;187    }188    189    /* Text content */190    p {191        color: #b0b5be;192    }193    194    /* Success messages */195    .stSuccess {196        background-color: rgba(41, 98, 255, 0.1) !important;197        border: 1px solid #2962ff !important;198        border-radius: 6px;199        padding: 1rem;200    }201    202    /* Warning messages */203    .stWarning {204        background-color: rgba(255, 193, 7, 0.1) !important;205        border: 1px solid #ffc107 !important;206        border-radius: 6px;207        padding: 1rem;208    }209    210    /* Error messages */211    .stError {212        background-color: rgba(255, 52, 52, 0.1) !important;213        border: 1px solid #ff3434 !important;214        border-radius: 6px;215        padding: 1rem;216    }217    218    /* Info messages */219    .stInfo {220        background-color: rgba(33, 150, 243, 0.1) !important;221        border: 1px solid #2196f3 !important;222        border-radius: 6px;223        padding: 1rem;224    }225    226    /* Footer */227    .footer {228        position: fixed;229        bottom: 0;230        left: 0;231        right: 0;232        background-color: #131722;233        padding: 10px;234        text-align: center;235        font-size: 0.8rem;236        color: #787b86;237    }238</style>239""", unsafe_allow_html=True)240 241# =============================================================================242# HEADER243# =============================================================================244 245st.markdown("""246<div style="text-align: center; padding: 1rem 0;">247    <h1 style="margin: 0;">📈 AriasCampos Backtester</h1>248    <p style="color: #787b86; margin: 0.5rem 0 0 0;">249        Sistema de Backtesting para Algoritmos de Trading Estructural250    </p>251</div>252""", unsafe_allow_html=True)253 254# =============================================================================255# SIDEBAR GLOBAL256# =============================================================================257from datetime import datetime, timedelta258from backtesting_app.core.data_loader import DATA_SOURCES259 260with st.sidebar:261    st.header("🔧 Configuración Global")262    263    # Algoritmo264    algorithm_choice = st.selectbox(265        "🤖 Algoritmo",266        options=['FAST', 'SLOW'],267        index=0,268        key="global_algo",269        help="FAST: AriasCampos 5.2 (2 estructuras), SLOW: AriasCampos Slow (1 estructura)"270    )271    st.session_state['config_algorithm'] = algorithm_choice272    273    st.markdown("---")274    275    # Timeframe276    st.subheader("⏱️ Timeframe")277    timeframes = {278        '1h': '1 Hora',279        '4h': '4 Horas',280        '1d': '1 Día',281        '1wk': '1 Semana',282        '1mo': '1 Mes'283    }284    timeframe = st.selectbox(285        "Intervalo",286        options=list(timeframes.keys()),287        format_func=lambda x: timeframes[x],288        index=4,  # 1mo por defecto289        key="global_timeframe"290    )291    st.session_state['config_timeframe'] = timeframe292    293    # Período - DROPDOWN en lugar de botones294    st.subheader("📅 Período")295    period_options = {296        '1m': 'Último Mes',297        '3m': '3 Meses',298        '6m': '6 Meses',299        '1y': '1 Año',300        '2y': '2 Años',301        '5y': '5 Años',302        '10y': '10 Años',303        'max': 'Máximo disponible'304    }305    306    selected_period = st.selectbox(307        "Período de datos",308        options=list(period_options.keys()),309        format_func=lambda x: period_options[x],310        index=4,  # 2 años por defecto311        key="global_period"312    )313    314    # Calcular fechas según período315    end_date = datetime.now()316    period_days = {317        '1m': 30, '3m': 90, '6m': 180, '1y': 365,318        '2y': 730, '5y': 1825, '10y': 3650, 'max': 10000319    }320    start_date = end_date - timedelta(days=period_days[selected_period])321    322    st.session_state['config_start_date'] = start_date323    st.session_state['config_end_date'] = end_date324    st.caption(f"📊 {start_date.strftime('%Y-%m-%d')} → {end_date.strftime('%Y-%m-%d')}")325    326    st.markdown("---")327 328    # Fuente de datos - Ahora fija a yfinance329    st.session_state['config_source'] = 'yahoo'330 331    # Twelve Data API Key (con clave por defecto)332    st.subheader("🔑 Twelve Data")333    st.caption("✅ API Key configurada - Historiales hasta 10 años en timeframes de 1h/4h")334 335    # API key por defecto336    DEFAULT_API_KEY = "5e4ae0d60bcc452292708eef93b4de48"337 338    # Permitir override si el usuario quiere usar otra339    with st.expander("🔧 Cambiar API Key (opcional)", expanded=False):340        twelve_key = st.text_input(341            "API Key personalizada",342            type="password",343            help="Si quieres usar tu propia API key de Twelve Data",344            placeholder="Dejar vacío para usar la API key por defecto"345        )346 347    # Usar la API key ingresada o la por defecto348    st.session_state['config_api_key'] = twelve_key if twelve_key else DEFAULT_API_KEY349 350    st.markdown("---")351    352    # Gestión de Capital353    st.subheader("💰 Capital")354    355    initial_capital = st.number_input(356        "Capital Inicial ($)",357        min_value=100,358        max_value=10000000,359        value=10000,360        step=1000,361        key="global_capital"362    )363    st.session_state['config_capital'] = initial_capital364    365    risk_percent = st.slider(366        "Riesgo por Trade (%)",367        min_value=0.1,368        max_value=10.0,369        value=1.0,370        step=0.1,371        key="global_risk"372    )373    st.session_state['config_risk'] = risk_percent374    375    commission = st.number_input(376        "Comisión (%)",377        min_value=0.0,378        max_value=1.0,379        value=0.1,380        step=0.01,381        key="global_commission"382    )383    st.session_state['config_commission'] = commission384    385    # Dirección de trades386    direction_filter = st.selectbox(387        "Dirección de Trades",388        options=['LONG y SHORT', 'Solo LONG', 'Solo SHORT'],389        key="global_direction"390    )391    st.session_state['config_direction'] = direction_filter392    393    st.markdown("---")394    395    # Parámetros del algoritmo (expandible)396    with st.expander(f"⚙️ Parámetros {algorithm_choice}", expanded=True):397        if algorithm_choice == 'FAST':398            lookbehind = st.number_input("Lookbehind", 0, 20, 0, key="global_lb",399                help="Barras hacia atrás para buscar máximos/mínimos")400            stop_resets = st.checkbox("Stop resets support", True, key="global_srs",401                help="Si el stop resetea los niveles de soporte/resistencia")402            break_margin = st.number_input("Break Margin (%)", 0.0, 5.0, 0.0, 0.1, key="global_bm",403                help="Margen adicional para confirmar ruptura")404            405            # Márgenes por timeframe406            st.markdown("##### Margen S/R por Timeframe")407            margin_defaults = {'1h': 0.15, '4h': 0.3, '1d': 0.7, '1wk': 1.4, '1mo': 3.0}408            margin = st.number_input(409                f"Margen {timeframe} (%)",410                min_value=0.0, max_value=10.0,411                value=margin_defaults.get(timeframe, 0.3),412                step=0.05, key="global_margin"413            )414            415            st.session_state['config_params'] = {416                'lookbehind': lookbehind,417                'stop_resets_support': stop_resets,418                'break_margin': break_margin,419                f'margin_{timeframe.replace("1", "")}': margin420            }421        else:  # SLOW422            lookbehind = st.number_input("Lookbehind", 0, 20, 0, key="global_lb",423                help="Barras hacia atrás para buscar máximos/mínimos")424            sensibilidad = st.number_input("Sensibilidad", 1, 10, 2, key="global_sens",425                help="Rupturas consecutivas para cambiar tendencia")426            offset = st.number_input("Offset", 0, 10, 1, key="global_offset",427                help="Desplazamiento para buscar máximos/mínimos")428            429            # Márgenes Long/Short por timeframe430            st.markdown("##### Márgenes Long")431            margin_defaults = {'1h': 0.15, '4h': 0.3, '1d': 0.7, '1wk': 1.4, '1mo': 3.0}432            default_margin = margin_defaults.get(timeframe, 0.3)433            434            margin_long_support = st.number_input(435                f"Soporte Long {timeframe}", 0.0, 10.0, 0.0, 0.05, key="global_mls"436            )437            margin_long_resistance = st.number_input(438                f"Resistencia Long {timeframe}", 0.0, 10.0, default_margin, 0.05, key="global_mlr"439            )440            441            st.markdown("##### Márgenes Short")442            margin_short_support = st.number_input(443                f"Soporte Short {timeframe}", 0.0, 10.0, default_margin, 0.05, key="global_mss"444            )445            margin_short_resistance = st.number_input(446                f"Resistencia Short {timeframe}", 0.0, 10.0, 0.0, 0.05, key="global_msr"447            )448            449            st.session_state['config_params'] = {450                'lookbehind': lookbehind,451                'sensibilidad': sensibilidad,452                'offset': offset,453                'margin_long_support': margin_long_support,454                'margin_long_resistance': margin_long_resistance,455                'margin_short_support': margin_short_support,456                'margin_short_resistance': margin_short_resistance457            }458 459# =============================================================================460# TABS PRINCIPALES461# =============================================================================462 463tab1, tab2, tab3 = st.tabs([464    "🔍 Screener",465    "📈 Estrategias",466    "ℹ️ Documentación"467])468 469# Tab 1: Screener470with tab1:471    render_screener_tab()472 473# Tab 2: Estrategias474with tab2:475    render_strategies_tab()476 477# Tab 3: Documentación478with tab3:479    st.header("📖 Documentación")480    481    st.markdown("""482    **AriasCampos Backtester** es una herramienta profesional de backtesting para algoritmos de trading 483    estructural. Soporta múltiples fuentes de datos (Yahoo Finance, YahooQuery) y ofrece un análisis 484    detallado de métricas de rendimiento y visualización estilo TradingView.485    486    ---487    488    ## 🚀 Algoritmo FAST (AriasCampos 5.2)489    490    ### Concepto491    El algoritmo FAST mantiene **dos estructuras simultáneas**: una alcista y una bajista. 492    Esto permite capturar cambios de tendencia más rápidamente.493    494    ### Parámetros495    496    | Parámetro | Descripción | Rango | Default |497    |-----------|-------------|-------|---------|498    | `lookbehind` | Barras hacia atrás para buscar máximos/mínimos | 1-20 | 10 |499    | `stop_resets_support` | Si el stop resetea niveles S/R | true/false | true |500    | `break_margin` | Margen adicional para confirmar ruptura | 0-5% | 0% |501    | `margin_XH` | Margen de S/R por timeframe | 0-10% | Variable |502    503    ### Lógica504    1. Inicializa dos estructuras en la primera barra505    2. La estructura alcista se actualiza cuando el precio rompe resistencia506    3. La estructura bajista se actualiza cuando el precio rompe soporte507    4. Se genera señal LONG cuando la estructura alcista cambia a bullish508    5. Se genera señal SHORT cuando la estructura bajista cambia a bearish509    510    ---511    512    ## 🐢 Algoritmo SLOW (AriasCampos Slow Strategy)513    514    ### Concepto515    El algoritmo SLOW mantiene **una única estructura** que cambia según la tendencia actual. 516    Requiere múltiples rupturas consecutivas antes de cambiar de tendencia, lo que filtra el ruido.517    518    ### Parámetros519    520    | Parámetro | Descripción | Rango | Default |521    |-----------|-------------|-------|---------|522    | `sensibilidad` | Rupturas consecutivas necesarias | 1-10 | 2 |523    | `offset` | Desplazamiento para buscar niveles | 0-10 | 1 |524    | `margin_long_support` | Margen de soporte para longs | 0-10% | 0% |525    | `margin_long_resistance` | Margen de resistencia para longs | 0-10% | 0.3% |526    | `margin_short_support` | Margen de soporte para shorts | 0-10% | 0.3% |527    | `margin_short_resistance` | Margen de resistencia para shorts | 0-10% | 0% |528    529    ### Lógica530    1. Detecta patrones de envolvente (high > last_high AND low < last_low)531    2. Cuenta rupturas consecutivas de S/R532    3. Cambia tendencia solo cuando se alcanza la sensibilidad configurada533    4. Actualiza niveles según la tendencia activa534    535    ---536    537    ## 💰 Gestión de Capital538    539    ### Tamaño de Posición540    El tamaño de cada posición se calcula basándose en:541    - Capital actual542    - Porcentaje de riesgo por trade543    - Distancia al stop loss544    545    ```546    risk_amount = capital × (risk_percent / 100)547    position_size = risk_amount / |entry_price - stop_loss|548    ```549    550    ### Trailing Stop551    - **LONG**: El stop loss se mueve hacia arriba cuando el soporte sube552    - **SHORT**: El stop loss se mueve hacia abajo cuando la resistencia baja553    554    ---555    556    ## 📊 Métricas Calculadas557    558    | Métrica | Descripción |559    |---------|-------------|560    | **Retorno Total** | Ganancia/pérdida absoluta y porcentual |561    | **Win Rate** | Porcentaje de trades ganadores |562    | **Profit Factor** | Ganancias brutas / Pérdidas brutas |563    | **Max Drawdown** | Máxima caída desde un pico de equity |564    | **Sharpe Ratio** | Retorno ajustado por volatilidad (anualizado) |565    | **Avg Bars Held** | Duración promedio de trades |566    567    ---568    569    ## 📈 Activos Soportados570    571    La aplicación permite analizar una amplia gama de instrumentos financieros. A continuación se detallan los activos recomendados y disponibles:572    573    #### 💻 Acciones Tech574    `AAPL`, `MSFT`, `GOOGL`, `AMZN`, `META`, `NVDA`, `TSLA`, `AMD`, `INTC`, `CRM`, `ADBE`, `NFLX`, `ORCL`, `CSCO`575    576    #### 🔗 Acciones Crypto-Related577    `MSTR`, `COIN`, `MARA`, `RIOT`, `CLSK`, `HUT`, `BITF`578    579    #### 📦 ETFs580    `SPY`, `QQQ`, `IWM`, `DIA`, `XLF`, `XLK`, `GLD`, `SLV`, `USO`, `TLT`, `VXX`, `ARKK`, `BITO`, `GBTC`581    582    > **Tip**: Puedes escribir cualquier símbolo de Yahoo Finance directamente en el selector de la pestaña de "Activo Único" para realizar el backtesting.583    584    ---585    586    ## ⚙️ Arquitectura del Sistema587    588    ```589    backtesting_app/590    ├── app.py                 # Aplicación principal Streamlit591    ├── algorithms/            # Módulo de algoritmos (inmutables)592    │   ├── base.py           # Clase base abstracta593    │   ├── fast_ariascampos.py   # Algoritmo FAST594    │   └── slow_ariascampos.py   # Algoritmo SLOW595    ├── core/                  # Módulo core596    │   ├── data_loader.py    # Descarga de datos (yfinance)597    │   └── backtester.py     # Motor de backtesting598    └── ui/                    # Módulo de interfaz599        ├── visualization.py   # Gráficos (Plotly)600        ├── components.py      # Componentes reutilizables601        ├── single_asset.py    # Tab activo único602        └── multi_asset.py     # Tab multi-activo603    ```604    ---605    606    ## 📂 Estructura del Proyecto607    608    - Los algoritmos originales están protegidos en el módulo `algorithms/`609    - Solo los parámetros configurables pueden ser modificados desde la UI610    - Los datos se obtienen de **Yahoo Finance** (gratis, sin API key)611    - No se almacena información sensible612    613    ---614    615    ## 📝 Changelog616    617    ### v2.1 - Optimizaciones de Datos618    - Consolidación del motor de datos usando **yfinance**.619    - Sistema de validación de datos mejorado con detección de errores OHLCV.620    - Soporte absoluto de importaciones para mayor robustez.621 622    ### v2.0 - Arquitectura Modular623    - Separación en módulos: algorithms, core, ui624    - Soporte para múltiples algoritmos (FAST y SLOW)625    - Tab de multi-activos y Screener avanzado626    - Mejoras en visualización estilo TradingView627    628    ### v1.0 - Versión Inicial629    - Implementación de AriasCampos 5.2630    - Backtesting de activo único631    - Visualización básica632    """)633 634# =============================================================================635# FOOTER636# =============================================================================637 638st.markdown("""639<div class="footer">640    <p>AriasCampos Backtester v2.0 | Desarrollado con ❤️ usando Streamlit, Plotly y yfinance</p>641</div>642""", unsafe_allow_html=True)643