F4: Tu Primera Estrategia Completa 🚀

Módulo Fundamental 4 - Duración: 2-3 horas

🎯 Objetivos del Módulo

Al completar este módulo tendrás:

  • ✅ Una estrategia de trading completa y funcional
  • ✅ Reglas claras de entrada, salida y gestión de riesgo
  • ✅ Un backtest que prueba tu estrategia con datos reales
  • ✅ Métricas para evaluar si tu estrategia es rentable

📊 La Estrategia: “Golden Cross con Filtros”

Vamos a construir una estrategia real y probada paso a paso.

¿Por qué esta estrategia?

  • Simple pero efectiva: Fácil de entender y programar
  • Probada históricamente: Usada por fondos institucionales
  • Adaptable: Funciona en diferentes mercados
  • Buena para aprender: Incluye todos los componentes esenciales

Componentes de la Estrategia

Componente Descripción
Señal Principal Golden Cross (SMA 50 cruza SMA 200)
Filtro 1 RSI no en sobrecompra (< 70)
Filtro 2 Volumen > promedio 20 días
Stop Loss 5% desde entrada
Take Profit 15% desde entrada
Gestión de Riesgo Máximo 2% del capital por trade

🔨 Paso 1: Construir la Estrategia

Código Base de la Estrategia

# Para Google Colab
!pip install yfinance pandas numpy matplotlib

import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime, timedelta

class GoldenCrossStrategy:
    """
    Estrategia Golden Cross con Filtros de Calidad
    """

    def __init__(self, symbol, start_date, end_date, capital_inicial=10000):
        self.symbol = symbol
        self.start_date = start_date
        self.end_date = end_date
        self.capital_inicial = capital_inicial
        self.capital = capital_inicial

        # Parámetros de la estrategia
        self.sma_corta = 50
        self.sma_larga = 200
        self.rsi_periodo = 14
        self.rsi_sobrecompra = 70
        self.volumen_filtro = 1.2  # 20% sobre promedio
        self.stop_loss_pct = 0.05   # 5%
        self.take_profit_pct = 0.15 # 15%
        self.riesgo_por_trade = 0.02 # 2% del capital

        # Datos para tracking
        self.trades = []
        self.posicion_actual = None

    def descargar_datos(self):
        """Descarga y prepara los datos"""
        print(f"📊 Descargando datos de {self.symbol}...")

        # Descargar con margen extra para calcular indicadores
        fecha_inicio_ext = pd.to_datetime(self.start_date) - timedelta(days=300)
        self.data = yf.download(self.symbol, start=fecha_inicio_ext, end=self.end_date)

        if self.data.empty:
            raise ValueError(f"No se encontraron datos para {self.symbol}")

        print(f"✅ Descargados {len(self.data)} días de datos")

    def calcular_indicadores(self):
        """Calcula todos los indicadores necesarios"""
        print("🧮 Calculando indicadores...")

        # Medias móviles
        self.data['SMA_50'] = self.data['Close'].rolling(window=self.sma_corta).mean()
        self.data['SMA_200'] = self.data['Close'].rolling(window=self.sma_larga).mean()

        # RSI
        delta = self.data['Close'].diff()
        ganancia = (delta.where(delta > 0, 0)).rolling(window=self.rsi_periodo).mean()
        perdida = (-delta.where(delta < 0, 0)).rolling(window=self.rsi_periodo).mean()
        rs = ganancia / perdida
        self.data['RSI'] = 100 - (100 / (1 + rs))

        # Volumen promedio
        self.data['Volume_MA'] = self.data['Volume'].rolling(window=20).mean()
        self.data['Volume_Ratio'] = self.data['Volume'] / self.data['Volume_MA']

        # Señales de cruce
        self.data['Golden_Cross'] = (
            (self.data['SMA_50'] > self.data['SMA_200']) &
            (self.data['SMA_50'].shift(1) <= self.data['SMA_200'].shift(1))
        )

        self.data['Death_Cross'] = (
            (self.data['SMA_50'] < self.data['SMA_200']) &
            (self.data['SMA_50'].shift(1) >= self.data['SMA_200'].shift(1))
        )

        # Limpiar NaN del inicio
        self.data = self.data[self.data.index >= self.start_date].dropna()

        print("✅ Indicadores calculados")

    def generar_señales(self):
        """Genera señales de compra/venta basadas en la estrategia"""
        print("🎯 Generando señales de trading...")

        self.data['Señal'] = 0  # 0: Sin posición, 1: Compra, -1: Venta

        for i in range(len(self.data)):
            fecha = self.data.index[i]
            row = self.data.iloc[i]

            # SEÑAL DE COMPRA
            if (row['Golden_Cross'] and
                row['RSI'] < self.rsi_sobrecompra and
                row['Volume_Ratio'] > self.volumen_filtro and
                self.posicion_actual is None):

                self.data.loc[fecha, 'Señal'] = 1
                self.abrir_posicion(fecha, row['Close'], 'LONG')

            # VERIFICAR STOPS SI HAY POSICIÓN
            elif self.posicion_actual is not None:
                precio_actual = row['Close']
                precio_entrada = self.posicion_actual['precio_entrada']

                # Stop Loss
                if precio_actual <= precio_entrada * (1 - self.stop_loss_pct):
                    self.data.loc[fecha, 'Señal'] = -1
                    self.cerrar_posicion(fecha, precio_actual, 'STOP_LOSS')

                # Take Profit
                elif precio_actual >= precio_entrada * (1 + self.take_profit_pct):
                    self.data.loc[fecha, 'Señal'] = -1
                    self.cerrar_posicion(fecha, precio_actual, 'TAKE_PROFIT')

                # Death Cross (salida por señal)
                elif row['Death_Cross']:
                    self.data.loc[fecha, 'Señal'] = -1
                    self.cerrar_posicion(fecha, precio_actual, 'DEATH_CROSS')

        # Cerrar posición al final si queda abierta
        if self.posicion_actual is not None:
            ultimo_precio = self.data['Close'].iloc[-1]
            self.cerrar_posicion(self.data.index[-1], ultimo_precio, 'FIN_PERIODO')

        print(f"✅ Generadas {len(self.trades)} operaciones")

    def abrir_posicion(self, fecha, precio, tipo):
        """Abre una nueva posición"""

        # Calcular tamaño de posición basado en riesgo
        capital_a_riesgo = self.capital * self.riesgo_por_trade
        stop_loss_precio = precio * (1 - self.stop_loss_pct)
        riesgo_por_accion = precio - stop_loss_precio
        num_acciones = int(capital_a_riesgo / riesgo_por_accion)

        # Limitar al capital disponible
        max_acciones = int(self.capital * 0.95 / precio)  # Usar máximo 95% del capital
        num_acciones = min(num_acciones, max_acciones)

        self.posicion_actual = {
            'fecha_entrada': fecha,
            'precio_entrada': precio,
            'num_acciones': num_acciones,
            'tipo': tipo,
            'stop_loss': stop_loss_precio,
            'take_profit': precio * (1 + self.take_profit_pct)
        }

    def cerrar_posicion(self, fecha, precio, razon):
        """Cierra la posición actual y registra el trade"""

        if self.posicion_actual is None:
            return

        # Calcular resultado
        precio_entrada = self.posicion_actual['precio_entrada']
        num_acciones = self.posicion_actual['num_acciones']
        ganancia_perdida = (precio - precio_entrada) * num_acciones
        retorno_pct = ((precio - precio_entrada) / precio_entrada) * 100

        # Actualizar capital
        self.capital += ganancia_perdida

        # Registrar trade
        trade = {
            'fecha_entrada': self.posicion_actual['fecha_entrada'],
            'fecha_salida': fecha,
            'precio_entrada': precio_entrada,
            'precio_salida': precio,
            'num_acciones': num_acciones,
            'ganancia_perdida': ganancia_perdida,
            'retorno_pct': retorno_pct,
            'razon_salida': razon,
            'capital_despues': self.capital
        }
        self.trades.append(trade)

        # Limpiar posición
        self.posicion_actual = None

    def calcular_metricas(self):
        """Calcula métricas de performance de la estrategia"""

        if not self.trades:
            print("❌ No hay trades para analizar")
            return None

        df_trades = pd.DataFrame(self.trades)

        # Métricas básicas
        total_trades = len(df_trades)
        trades_ganadores = len(df_trades[df_trades['ganancia_perdida'] > 0])
        trades_perdedores = len(df_trades[df_trades['ganancia_perdida'] < 0])
        win_rate = (trades_ganadores / total_trades) * 100

        # Ganancias y pérdidas
        total_ganancia = df_trades[df_trades['ganancia_perdida'] > 0]['ganancia_perdida'].sum()
        total_perdida = abs(df_trades[df_trades['ganancia_perdida'] < 0]['ganancia_perdida'].sum())
        profit_factor = total_ganancia / total_perdida if total_perdida > 0 else np.inf

        # Retornos
        retorno_total = ((self.capital - self.capital_inicial) / self.capital_inicial) * 100
        promedio_ganancia = df_trades[df_trades['ganancia_perdida'] > 0]['retorno_pct'].mean()
        promedio_perdida = df_trades[df_trades['ganancia_perdida'] < 0]['retorno_pct'].mean()

        # Drawdown máximo
        capital_maximo = self.capital_inicial
        max_drawdown = 0
        for trade in self.trades:
            capital_maximo = max(capital_maximo, trade['capital_despues'])
            drawdown = ((capital_maximo - trade['capital_despues']) / capital_maximo) * 100
            max_drawdown = max(max_drawdown, drawdown)

        # Buy & Hold comparison
        precio_inicial = self.data['Close'].iloc[0]
        precio_final = self.data['Close'].iloc[-1]
        buy_hold_return = ((precio_final - precio_inicial) / precio_inicial) * 100

        metricas = {
            'total_trades': total_trades,
            'trades_ganadores': trades_ganadores,
            'trades_perdedores': trades_perdedores,
            'win_rate': win_rate,
            'profit_factor': profit_factor,
            'retorno_total': retorno_total,
            'promedio_ganancia': promedio_ganancia,
            'promedio_perdida': promedio_perdida,
            'max_drawdown': max_drawdown,
            'capital_final': self.capital,
            'buy_hold_return': buy_hold_return
        }

        return metricas

    def visualizar_resultados(self):
        """Crea visualizaciones de los resultados"""

        fig, axes = plt.subplots(4, 1, figsize=(15, 16), sharex=True)

        # 1. Precio y Señales
        ax1 = axes[0]
        ax1.plot(self.data.index, self.data['Close'], label='Precio', linewidth=1, alpha=0.7)
        ax1.plot(self.data.index, self.data['SMA_50'], label='SMA 50', linewidth=1, alpha=0.7)
        ax1.plot(self.data.index, self.data['SMA_200'], label='SMA 200', linewidth=1, alpha=0.7)

        # Marcar entradas y salidas
        entradas = self.data[self.data['Señal'] == 1]
        salidas = self.data[self.data['Señal'] == -1]

        ax1.scatter(entradas.index, entradas['Close'], color='green', marker='^',
                   s=100, label=f'Compras ({len(entradas)})', zorder=5)
        ax1.scatter(salidas.index, salidas['Close'], color='red', marker='v',
                   s=100, label=f'Ventas ({len(salidas)})', zorder=5)

        ax1.set_title(f'{self.symbol} - Estrategia Golden Cross con Filtros', fontsize=14)
        ax1.set_ylabel('Precio ($)')
        ax1.legend(loc='upper left')
        ax1.grid(True, alpha=0.3)

        # 2. RSI
        ax2 = axes[1]
        ax2.plot(self.data.index, self.data['RSI'], color='purple', linewidth=1)
        ax2.axhline(y=70, color='red', linestyle='--', alpha=0.5)
        ax2.axhline(y=30, color='green', linestyle='--', alpha=0.5)
        ax2.set_ylabel('RSI')
        ax2.set_ylim(0, 100)
        ax2.grid(True, alpha=0.3)

        # 3. Volumen
        ax3 = axes[2]
        colors = ['green' if s == 1 else 'red' if s == -1 else 'gray' for s in self.data['Señal']]
        ax3.bar(self.data.index, self.data['Volume'], color=colors, alpha=0.5)
        ax3.plot(self.data.index, self.data['Volume_MA'], color='blue', linewidth=1)
        ax3.set_ylabel('Volumen')
        ax3.grid(True, alpha=0.3)

        # 4. Equity Curve
        ax4 = axes[3]
        if self.trades:
            equity_data = [self.capital_inicial]
            equity_dates = [self.data.index[0]]

            for trade in self.trades:
                equity_dates.append(trade['fecha_salida'])
                equity_data.append(trade['capital_despues'])

            ax4.plot(equity_dates, equity_data, color='blue', linewidth=2, label='Estrategia')

            # Comparar con Buy & Hold
            buy_hold_values = self.capital_inicial * (self.data['Close'] / self.data['Close'].iloc[0])
            ax4.plot(self.data.index, buy_hold_values, color='gray', linewidth=1,
                    alpha=0.7, label='Buy & Hold')

            ax4.set_ylabel('Capital ($)')
            ax4.set_xlabel('Fecha')
            ax4.legend()
            ax4.grid(True, alpha=0.3)

        plt.tight_layout()
        plt.show()

    def ejecutar_backtest(self):
        """Ejecuta el backtest completo"""
        print("\\n" + "="*60)
        print(f"🚀 BACKTESTING: {self.symbol}")
        print(f"📅 Período: {self.start_date} a {self.end_date}")
        print(f"💰 Capital Inicial: ${self.capital_inicial:,.2f}")
        print("="*60)

        # Ejecutar pasos
        self.descargar_datos()
        self.calcular_indicadores()
        self.generar_señales()

        # Calcular métricas
        metricas = self.calcular_metricas()

        if metricas:
            print("\\n📊 RESULTADOS DEL BACKTEST:")
            print("="*60)
            print(f"Total de Operaciones: {metricas['total_trades']}")
            print(f"Operaciones Ganadoras: {metricas['trades_ganadores']}")
            print(f"Operaciones Perdedoras: {metricas['trades_perdedores']}")
            print(f"\\n✅ Win Rate: {metricas['win_rate']:.2f}%")
            print(f"📈 Profit Factor: {metricas['profit_factor']:.2f}")
            print(f"\\n💰 Capital Final: ${metricas['capital_final']:,.2f}")
            print(f"📊 Retorno Total: {metricas['retorno_total']:.2f}%")
            print(f"📉 Max Drawdown: {metricas['max_drawdown']:.2f}%")
            print(f"\\n🔄 Buy & Hold Return: {metricas['buy_hold_return']:.2f}%")

            if metricas['retorno_total'] > metricas['buy_hold_return']:
                print("\\n✅ ¡La estrategia SUPERA a Buy & Hold!")
            else:
                print("\\n❌ La estrategia NO supera a Buy & Hold")

        # Visualizar
        self.visualizar_resultados()

        return metricas

# EJECUTAR LA ESTRATEGIA
if __name__ == "__main__":
    # Configurar parámetros
    SYMBOL = 'AAPL'  # Puedes cambiar el símbolo
    START_DATE = '2020-01-01'
    END_DATE = '2024-01-01'
    CAPITAL_INICIAL = 10000

    # Crear y ejecutar estrategia
    estrategia = GoldenCrossStrategy(SYMBOL, START_DATE, END_DATE, CAPITAL_INICIAL)
    resultados = estrategia.ejecutar_backtest()

📈 Paso 2: Optimizar la Estrategia

Probando Diferentes Parámetros

def optimizar_parametros(symbol='AAPL', start_date='2020-01-01', end_date='2024-01-01'):
    """Prueba diferentes combinaciones de parámetros"""

    print("🔧 OPTIMIZACIÓN DE PARÁMETROS")
    print("="*60)

    resultados_optimizacion = []

    # Parámetros a probar
    sma_cortas = [20, 30, 50]
    sma_largas = [100, 150, 200]
    stop_losses = [0.03, 0.05, 0.07]
    take_profits = [0.10, 0.15, 0.20]

    mejor_resultado = None
    mejor_retorno = -np.inf

    for sma_c in sma_cortas:
        for sma_l in sma_largas:
            if sma_c >= sma_l:
                continue

            for sl in stop_losses:
                for tp in take_profits:
                    # Crear estrategia con estos parámetros
                    estrategia = GoldenCrossStrategy(symbol, start_date, end_date)
                    estrategia.sma_corta = sma_c
                    estrategia.sma_larga = sma_l
                    estrategia.stop_loss_pct = sl
                    estrategia.take_profit_pct = tp

                    # Ejecutar backtest silenciosamente
                    try:
                        estrategia.descargar_datos()
                        estrategia.calcular_indicadores()
                        estrategia.generar_señales()
                        metricas = estrategia.calcular_metricas()

                        if metricas and metricas['total_trades'] > 0:
                            resultado = {
                                'sma_corta': sma_c,
                                'sma_larga': sma_l,
                                'stop_loss': sl,
                                'take_profit': tp,
                                'retorno': metricas['retorno_total'],
                                'win_rate': metricas['win_rate'],
                                'profit_factor': metricas['profit_factor'],
                                'max_drawdown': metricas['max_drawdown'],
                                'num_trades': metricas['total_trades']
                            }

                            resultados_optimizacion.append(resultado)

                            if metricas['retorno_total'] > mejor_retorno:
                                mejor_retorno = metricas['retorno_total']
                                mejor_resultado = resultado

                    except:
                        continue

    # Mostrar resultados
    if mejor_resultado:
        print("\\n🏆 MEJORES PARÁMETROS ENCONTRADOS:")
        print("="*60)
        print(f"SMA Corta: {mejor_resultado['sma_corta']}")
        print(f"SMA Larga: {mejor_resultado['sma_larga']}")
        print(f"Stop Loss: {mejor_resultado['stop_loss']*100:.1f}%")
        print(f"Take Profit: {mejor_resultado['take_profit']*100:.1f}%")
        print(f"\\nRetorno: {mejor_resultado['retorno']:.2f}%")
        print(f"Win Rate: {mejor_resultado['win_rate']:.2f}%")
        print(f"Profit Factor: {mejor_resultado['profit_factor']:.2f}")
        print(f"Max Drawdown: {mejor_resultado['max_drawdown']:.2f}%")

    return pd.DataFrame(resultados_optimizacion)

# Ejecutar optimización
df_optimizacion = optimizar_parametros('AAPL')

# Ver top 5 combinaciones
print("\\n📊 TOP 5 COMBINACIONES:")
print(df_optimizacion.nlargest(5, 'retorno')[['sma_corta', 'sma_larga', 'stop_loss', 'take_profit', 'retorno', 'win_rate']])

🎯 Paso 3: Validación y Mejoras

Analizando Debilidades

def analizar_trades_detallado(estrategia):
    """Análisis detallado de cada trade"""

    if not estrategia.trades:
        print("No hay trades para analizar")
        return

    df_trades = pd.DataFrame(estrategia.trades)

    print("\\n📋 ANÁLISIS DETALLADO DE TRADES:")
    print("="*60)

    # Análisis por razón de salida
    print("\\n📊 Distribución de Salidas:")
    for razon in df_trades['razon_salida'].unique():
        trades_razon = df_trades[df_trades['razon_salida'] == razon]
        avg_return = trades_razon['retorno_pct'].mean()
        count = len(trades_razon)
        print(f"{razon}: {count} trades, Retorno promedio: {avg_return:.2f}%")

    # Duración de trades
    df_trades['duracion'] = (pd.to_datetime(df_trades['fecha_salida']) -
                            pd.to_datetime(df_trades['fecha_entrada'])).dt.days

    print(f"\\n⏱️ Duración promedio: {df_trades['duracion'].mean():.1f} días")
    print(f"Duración máxima: {df_trades['duracion'].max()} días")
    print(f"Duración mínima: {df_trades['duracion'].min()} días")

    # Mejor y peor trade
    mejor_trade = df_trades.loc[df_trades['ganancia_perdida'].idxmax()]
    peor_trade = df_trades.loc[df_trades['ganancia_perdida'].idxmin()]

    print(f"\\n🏆 Mejor Trade:")
    print(f"  Entrada: {mejor_trade['fecha_entrada']}")
    print(f"  Ganancia: ${mejor_trade['ganancia_perdida']:.2f} ({mejor_trade['retorno_pct']:.2f}%)")

    print(f"\\n😞 Peor Trade:")
    print(f"  Entrada: {peor_trade['fecha_entrada']}")
    print(f"  Pérdida: ${peor_trade['ganancia_perdida']:.2f} ({peor_trade['retorno_pct']:.2f}%)")

    return df_trades

# Analizar trades de la última estrategia
df_trades = analizar_trades_detallado(estrategia)

🏆 Proyecto Final: Multi-Estrategia

Comparando Múltiples Estrategias

def comparar_estrategias():
    """Compara diferentes estrategias en el mismo período"""

    estrategias_config = [
        {'nombre': 'Golden Cross Original', 'sma_c': 50, 'sma_l': 200, 'sl': 0.05, 'tp': 0.15},
        {'nombre': 'Golden Cross Agresivo', 'sma_c': 20, 'sma_l': 50, 'sl': 0.03, 'tp': 0.10},
        {'nombre': 'Golden Cross Conservador', 'sma_c': 50, 'sma_l': 200, 'sl': 0.07, 'tp': 0.20},
        {'nombre': 'Golden Cross Rápido', 'sma_c': 10, 'sma_l': 30, 'sl': 0.02, 'tp': 0.05},
    ]

    resultados_comparacion = []

    for config in estrategias_config:
        print(f"\\nProbando: {config['nombre']}...")

        estrategia = GoldenCrossStrategy('SPY', '2020-01-01', '2024-01-01', 10000)
        estrategia.sma_corta = config['sma_c']
        estrategia.sma_larga = config['sma_l']
        estrategia.stop_loss_pct = config['sl']
        estrategia.take_profit_pct = config['tp']

        estrategia.descargar_datos()
        estrategia.calcular_indicadores()
        estrategia.generar_señales()
        metricas = estrategia.calcular_metricas()

        if metricas:
            resultados_comparacion.append({
                'Estrategia': config['nombre'],
                'Retorno (%)': metricas['retorno_total'],
                'Win Rate (%)': metricas['win_rate'],
                'Profit Factor': metricas['profit_factor'],
                'Max DD (%)': metricas['max_drawdown'],
                'Trades': metricas['total_trades']
            })

    # Crear tabla comparativa
    df_comparacion = pd.DataFrame(resultados_comparacion)
    df_comparacion = df_comparacion.round(2)

    print("\\n" + "="*80)
    print("📊 COMPARACIÓN DE ESTRATEGIAS")
    print("="*80)
    print(df_comparacion.to_string(index=False))

    # Identificar ganadora
    mejor_estrategia = df_comparacion.loc[df_comparacion['Retorno (%)'].idxmax()]
    print(f"\\n🏆 MEJOR ESTRATEGIA: {mejor_estrategia['Estrategia']}")
    print(f"   Retorno: {mejor_estrategia['Retorno (%)']}%")
    print(f"   Profit Factor: {mejor_estrategia['Profit Factor']}")

    return df_comparacion

# Ejecutar comparación
df_comparacion = comparar_estrategias()

✅ Checkpoint del Módulo

Estrategia Completa ✅

  • Mi estrategia tiene reglas claras de entrada
  • Implementé stop loss y take profit
  • Uso gestión de riesgo (2% por trade)
  • Combino múltiples indicadores

Backtest Funcional ✅

  • Puedo probar con datos históricos reales
  • Calculo métricas de performance
  • Comparo con Buy & Hold
  • Visualizo resultados claramente

Optimización ✅

  • Probé diferentes parámetros
  • Identifiqué mejores configuraciones
  • Analicé trades individuales
  • Comparé múltiples variaciones

Aprendizajes ✅

  • Entiendo por qué algunas estrategias fallan
  • Sé la importancia del stop loss
  • Veo el impacto de los parámetros
  • Puedo mejorar la estrategia

🎓 ¡FELICITACIONES!

Has completado los FUNDAMENTOS del trading cuantitativo.

Ya tienes:

  • ✅ Conocimiento de qué es ser quant
  • ✅ Habilidades de Python para trading
  • ✅ Dominio de indicadores técnicos
  • ✅ Tu primera estrategia completa y probada

🚀 Próximos Pasos

Continúa con ESTRATEGIAS (Nivel 2)

E1: Momentum Trading

  • Gap & Go para small caps
  • Breakout strategies
  • Momentum scanning

E2: Mean Reversion

  • VWAP reclaim
  • Oversold bounces
  • Pairs trading

E3: Backtesting Avanzado

  • Walk-forward analysis
  • Monte Carlo simulation
  • Stress testing

💡 Reflexión Final

“Una estrategia mediocre bien ejecutada es mejor que una estrategia perfecta mal ejecutada.”

Tu primera estrategia no será perfecta, pero ya tienes las herramientas para mejorarla continuamente. Cada día de trading genera nuevos datos para aprender.

¡Ya eres oficialmente un Quant Trader en formación! 🎉


🎯 ¿Listo para estrategias más avanzadas?E1: Momentum Trading