diegobeyl/backtesting
2
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 