---
title: "Tu primer sistema en Python: cruce de medias móviles sobre SPY"
description: "Cruce de medias móviles sobre SPY en Python: datos con yfinance, señales sin look-ahead, drawdown y optimización. Práctica del nivel 2 del curso."
url: "https://pythonparatrading.com/primer-sistema-cruce-de-medias/"
lang: "es"
date: "2025-01-01"
updated: "2026-08-28"
image: "https://pythonparatrading.com/content/images/2025/04/Curso-Trading-Python-desde-0-en-Castellano-Python-para-Traders.jpg"
tags: ["Algoritmos", "Quant"]
authors: ["Jesús Cuesta"]
---

# Tu primer sistema en Python: cruce de medias móviles sobre SPY

> Un sistema de cruce de medias sobre SPY de principio a fin: datos con yfinance, señales sin look-ahead, drawdown y optimización.

- Idioma: es (español)
- Publicado: 2025-01-01
- Actualizado: 2026-08-28
- Temas: Algoritmos, Quant
- Requisitos: pandas · numpy · yfinance · matplotlib · quantstats
- Dataset: Yahoo Finance vía yfinance: SPY diario.
- Resumen: Cruce de medias móviles sobre SPY en Python: datos con yfinance, señales sin look-ahead, drawdown y optimización. Práctica del nivel 2 del curso.

Puntos clave:

- Descarga de SPY con yfinance y cálculo de dos medias móviles.
- Señales con np.where y shift(1) para no usar información futura.
- Retornos, curva acumulada y drawdown con quantstats.
- Optimización por rejilla con itertools.product: alpha, beta, Sharpe, drawdown y VaR.

---

> **Práctica del curso.** Este research es la práctica del [nivel 2 del Curso Python para Trading](https://pythonparatrading.com/curso-python-trading/nivel-2/).

¿Quieres aprender trading cuantitativo y automatizar tus estrategias? Este **curso python trading** es la guía definitiva para comenzar a programar, analizar y optimizar sistemas de trading con Python, la herramienta más utilizada por traders y quants en todo el mundo.

Este articulo ha sido modificado en abril de 2025 para adaptar el codigo a las ultimas actualizaciones de las librerias en uso.

## ¿Por qué aprender trading con Python?

El trading en los mercados financieros evoluciona constantemente. Python te permite:

-   Automatizar estrategias y análisis.
-   Acceder a datos históricos y en tiempo real.
-   Realizar backtesting y optimización de sistemas.
-   Visualizar resultados y métricas de riesgo.

Dominar Python es clave para cualquier trader moderno.

---

---

## Bibliotecas esenciales para trading en Python

Para este **curso python trading** usaremos:

-   **Pandas**: análisis y manipulación de datos tabulares.
-   **Numpy**: cálculos numéricos y operaciones con matrices.
-   **Matplotlib**: visualización de datos y gráficos.
-   **yfinance**: descarga de datos financieros desde Yahoo Finance.
-   **itertools**: generación eficiente de combinaciones de parámetros.
-   **quantstats**: métricas de rendimiento y riesgo.
-   **warnings**: gestión de advertencias en Python.

```python
import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt
from itertools import product
import quantstats as qs
import warnings

%matplotlib inline
warnings.filterwarnings("ignore")
```

---

## Descarga de datos y variables iniciales

Definimos los parámetros de las medias móviles y descargamos los datos históricos del ETF SPY (S&P500): Si buscas más fuentes de datos gratuitas (acciones, futuros, opciones y forex), en la [guía de OpenBB](https://pythonparatrading.com/openbb-datos-de-mercado-gratuitos/) tienes un paso a paso completo.

```python
media_1 = 20
media_2 = 3 * media_1
df = yf.download('SPY', interval='1d')
df.columns = df.columns.droplevel(1)
df = df['2023':]
```

Mediante yf,download, descargamos los datos del SPY en diario, mediante df.columns = df.columns.droplevel(1) eliminamos los multindices innecesarios, y reducimos el historico para que empiece en 2023 por el unico motivo de poder visualizar mejor las senales que generen el algoritmo.

---

## Cálculo y visualización de medias móviles

Calculamos las medias móviles sobre el precio de cierre ajustado:

```python
df['Avg_1'] = df['Adj Close'].rolling(media_1).mean()
df['Avg_2'] = df['Adj Close'].rolling(media_2).mean()
```

Visualizamos el precio y las medias móviles:

```python
df[['Close', 'Avg_1', 'Avg_2']].plot()
```

El resultado seria algo similar a esto.

![Cierre del SPY desde 2023 con sus dos medias móviles, Avg_1 (rápida) y Avg_2 (lenta)](https://pythonparatrading.com/content/images/2025/04/image-30.png)

---

## Generación y visualización de señales de trading

Creamos señales de entrada y salida según el cruce de medias móviles. Vamos a utilizar de la libreria de numpy, la funcion where. Donde evaluara una condicion, y si es positiva le asignara el valor 1 o True, y en caso contrario, le asignara el valor 0 o False. Generando las senales unicamente cuando la media 1 o avg\_1 es menor que la media 2 o avg\_2.

El comando .shift(1) evalua la vela inmediatamente anterior para actuar en esta vela. De otra forma estariamos filtrando informacion del futuro, generando overfit en la estrategia y retornos irreales.

```python
df['In_Trade'] = np.where(df['Avg_1'].shift(1) < df['Avg_2'].shift(1), 1, 0)
```

Visualizamos las señales de entrada y salida en el gráfico:

```python
# Señales de entrada
idx_open = df.loc[df["In_Trade"] == 1].index
plt.scatter(idx_open, df.loc[idx_open]["Close"], marker="*")
plt.plot(df["Close"].index, df["Close"], alpha=0.35)
plt.plot(df["Close"].index, df["Avg_1"], alpha=0.35)
plt.plot(df["Close"].index, df["Avg_2"], alpha=0.35)
plt.show()

# Señales de salida
idx_open = df.loc[df["In_Trade"] == 0].index
plt.scatter(idx_open, df.loc[idx_open]["Close"], marker="*")
plt.plot(df["Close"].index, df["Close"], alpha=0.35)
plt.plot(df["Close"].index, df["Avg_1"], alpha=0.35)
plt.plot(df["Close"].index, df["Avg_2"], alpha=0.35)
plt.show()
```

El grafico de las entradas tendria una aparencia tal que:

![Cierre del SPY y sus medias móviles con estrellas en los puntos de entrada del cruce de medias](https://pythonparatrading.com/content/images/2025/04/image-32.png)

Y el grafico de las salidas tendria una aparecia tal que:

![Cierre del SPY y sus medias móviles con estrellas en los puntos de salida del cruce de medias](https://pythonparatrading.com/content/images/2025/04/image-33.png)

---

## Análisis de rentabilidad y drawdown

Calculamos el retorno diario y de la estrategia:

```python
df["pct"] = df["Close"].pct_change()
df["return"] = df["pct"] * df["In_Trade"].shift(1)
df[['pct', 'return']].plot()
df[['pct', 'return']].cumsum().plot()
```

Visualizamos el drawdown de la estrategia:

```python
qs.stats.to_drawdown_series(df[['return', 'pct']]).plot()
```

![Drawdown de la estrategia (return) frente al SPY comprado y mantenido (pct), de 2023 a 2025](https://pythonparatrading.com/content/images/2025/04/image-38.png)

La variacon porcentua quedaria de la siguiente forma

![Retornos diarios del SPY (pct) y de la estrategia (return), que vale cero fuera de mercado](https://pythonparatrading.com/content/images/2025/04/image-34.png)

Y la suma de los retornos quedaria de la siguiente manera

![Suma de retornos: SPY (pct) cerca de 0,4 frente a la estrategia de cruce de medias (return) cerca de 0,1](https://pythonparatrading.com/content/images/2025/04/image-35.png)

---

## Optimización del sistema de trading

Probamos diferentes combinaciones de medias móviles para encontrar la más rentable

```python
rapido = [5, 15]
lento = [30, 60]
resultados = []
eqs = []
combinaciones = list(product(rapido, lento))
for media_rapida, media_lenta in product(rapido, lento):
    temp = pd.DataFrame()
    temp['Avg_1'] = df['Close'].rolling(media_rapida).mean()
    temp['Avg_2'] = df['Close'].rolling(media_lenta).mean()
    temp['pct'] = df['Close'].pct_change()
    temp.loc[(temp["Avg_1"].shift(1) > temp["Avg_2"].shift(1)), "In_Trade"] = 1
    temp.loc[(temp["Avg_1"].shift(1) < temp["Avg_2"].shift(1)), "In_Trade"] = 0
    ret_c = ((temp['In_Trade']) * (temp['pct'])).cumsum().dropna()
    ret = ((temp['In_Trade']) * (temp['pct']))
    greeks = qs.stats.greeks(ret, temp['pct'].dropna())
    sharpe = qs.stats.sharpe(ret)
    mdd = qs.stats.max_drawdown(ret)
    dom = len(temp[temp['In_Trade'] == 1]) / len(temp.index)
    var = qs.stats.value_at_risk(ret, 1) * 100
    resultados.append({'media_1': media_rapida, 'media_2': media_lenta, 'ret': ret_c[-1],
                      'beta': greeks[0], 'alpha': greeks[1], 'sharpe': sharpe, 'max_dd': mdd, 'on_market': dom, 'var': var})
    eqs.append(ret)
pd.DataFrame(resultados)
```

Este bloque de código realiza una optimización sistemática de la estrategia de cruce de medias móviles probando todas las combinaciones posibles entre varios periodos de medias rápidas y lentas. Para cada combinación, se calcula la media móvil rápida y lenta sobre el precio de cierre ajustado, se generan las señales de entrada y salida, y se calcula tanto el retorno diario como el acumulado de la estrategia. Además, se obtienen métricas clave de rendimiento y riesgo, como alpha, beta, Sharpe, drawdown máximo, porcentaje de tiempo en mercado y VaR, que se almacenan en una lista de resultados junto con los retornos diarios de cada combinación

Al finalizar, se construye un DataFrame con todos los resultados, lo que permite comparar fácilmente el desempeño de cada configuración de medias móviles. Este proceso automatizado facilita identificar qué parámetros ofrecen mejores resultados históricos y es fundamental para el desarrollo de sistemas de trading robustos y cuantitativos.

El resultado que nos ofrece es

![Tabla por combinación de medias (5/30, 5/60, 15/30, 15/60): retorno, beta, alpha, Sharpe, max_dd, on_market y var](https://pythonparatrading.com/content/images/2025/04/image-36.png)

Visualización de todas las optimizaciones:

```python
pd.DataFrame(eqs).transpose().cumsum().plot()

```

![Retornos acumulados de las cuatro combinaciones de medias móviles (0 a 3), de 2023 a 2025](https://pythonparatrading.com/content/images/2025/04/image-37.png)

**Métricas clave de evaluación:**

-   **Alpha:** rentabilidad ajustada al riesgo frente al mercado.
-   **Beta:** sensibilidad de la estrategia respecto al mercado.
-   **Sharpe:** relación entre exceso de retorno y volatilidad.
-   **Max Drawdown:** máxima caída desde un pico.
-   **Time on Market:** proporción de tiempo en el mercado.
-   **Value at Risk (VaR):** pérdida máxima esperada bajo un nivel de confianza.

---

## Preguntas frecuentes

**¿Para quién es este curso python trading?**

-   Principiantes en trading y programación.
-   Traders que quieren automatizar estrategias.
-   Analistas cuantitativos y financieros.

**¿Necesito experiencia previa en Python?**

-   No es imprescindible, pero ayuda. El curso está diseñado para que puedas seguirlo aunque seas nuevo en Python.

**¿Qué ventajas tiene usar Python para trading?**

-   Es gratuito, versátil y tiene una enorme comunidad.
-   Permite automatizar análisis, backtesting y ejecución de órdenes.
-   Es el estándar en trading cuantitativo y análisis financiero.

---

## Resumen y recursos adicionales

Este **curso python trading** te ha mostrado cómo descargar datos, calcular medias móviles, generar señales, analizar resultados y optimizar tu sistema de trading. Dominar estas herramientas te permitirá crear y mejorar tus propias estrategias de trading algorítmico.

**Sigue aprendiendo:**

-   [Modelos Cuantitativos y Ejecución Algorítmica](https://pythonparatrading.com/modelos-cuantitativos-ejecucion-algoritmica/)
-   [Guía Básica ETFs en Castellano](https://unespeculador.com/guiabasicaetfs/)

---

> ¿Listo para llevar tu trading al siguiente nivel?  
> Empieza ahora tu propio **curso python trading** y automatiza tus estrategias.

---

Si quieres acceder al codigo completo y actualizado, ademas de otros recursos para miembros, te recomendamos registrarte.

Cualquier duda, contacta conmigo en jcx\[a\]quantarmy.com

Un saludo, y hasta la proxima!
