402 lines
10 KiB
Markdown
402 lines
10 KiB
Markdown
# 🧪 Backtesting Engine - Guía de Uso
|
||
|
||
## 📋 Descripción
|
||
|
||
El motor de backtesting permite simular estrategias de trading sobre datos históricos para evaluar su performance antes de arriesgar capital real.
|
||
|
||
## 🚀 Uso Rápido
|
||
|
||
### Demo Simple
|
||
|
||
```bash
|
||
python backtest.py
|
||
```
|
||
|
||
Esto ejecuta un backtest con:
|
||
- Estrategia: Moving Average Crossover (10/30)
|
||
- Símbolo: BTC/USDT
|
||
- Periodo: 60 días
|
||
- Capital inicial: $10,000
|
||
|
||
### Comparar Estrategias
|
||
|
||
```bash
|
||
python backtest.py compare
|
||
```
|
||
|
||
Compara 4 estrategias diferentes sobre los mismos datos.
|
||
|
||
## 📊 Estrategias Disponibles
|
||
|
||
### 1. Moving Average Crossover
|
||
|
||
Cruces de medias móviles:
|
||
|
||
```python
|
||
from src.strategies import MovingAverageCrossover
|
||
|
||
strategy = MovingAverageCrossover(
|
||
fast_period=10, # Periodo media rápida
|
||
slow_period=30, # Periodo media lenta
|
||
ma_type='sma' # 'sma' o 'ema'
|
||
)
|
||
```
|
||
|
||
**Señales:**
|
||
- BUY: Media rápida cruza por encima de media lenta
|
||
- SELL: Media rápida cruza por debajo de media lenta
|
||
|
||
### 2. RSI Strategy
|
||
|
||
Basada en niveles de RSI:
|
||
|
||
```python
|
||
from src.strategies import RSIStrategy
|
||
|
||
strategy = RSIStrategy(
|
||
rsi_period=14, # Periodo del RSI
|
||
oversold_threshold=30, # Umbral de sobreventa
|
||
overbought_threshold=70 # Umbral de sobrecompra
|
||
)
|
||
```
|
||
|
||
**Señales:**
|
||
- BUY: RSI < 30 (sobrevendido)
|
||
- SELL: RSI > 70 (sobrecomprado)
|
||
|
||
### 3. Buy and Hold
|
||
|
||
Estrategia base para comparación:
|
||
|
||
```python
|
||
from src.strategies import BuyAndHold
|
||
|
||
strategy = BuyAndHold()
|
||
```
|
||
|
||
Compra al inicio y mantiene hasta el final.
|
||
|
||
## 🛠️ Uso Programático
|
||
|
||
### Backtest Básico
|
||
|
||
```python
|
||
from src.data.storage import StorageManager
|
||
from src.backtest import BacktestEngine
|
||
from src.strategies import MovingAverageCrossover
|
||
|
||
# Cargar datos
|
||
storage = StorageManager(...)
|
||
data = storage.load_ohlcv('BTC/USDT', '1h')
|
||
|
||
# Crear estrategia
|
||
strategy = MovingAverageCrossover(fast_period=10, slow_period=30)
|
||
|
||
# Crear motor de backtesting
|
||
engine = BacktestEngine(
|
||
strategy=strategy,
|
||
initial_capital=10000,
|
||
commission=0.001, # 0.1% por trade
|
||
slippage=0.0005, # 0.05% de slippage
|
||
position_size=0.95 # Usar 95% del capital
|
||
)
|
||
|
||
# Ejecutar
|
||
results = engine.run(data)
|
||
|
||
# Ver resultados
|
||
from src.backtest.metrics import print_backtest_report
|
||
print_backtest_report(results)
|
||
```
|
||
|
||
### Crear Tu Propia Estrategia
|
||
|
||
```python
|
||
from src.backtest.strategy import Strategy, Signal
|
||
import pandas as pd
|
||
|
||
class MiEstrategia(Strategy):
|
||
|
||
def __init__(self):
|
||
super().__init__(name="Mi Estrategia", params={})
|
||
|
||
def init_indicators(self, data: pd.DataFrame) -> pd.DataFrame:
|
||
"""
|
||
Calcula tus indicadores aquí
|
||
"""
|
||
# Ejemplo: añadir SMA
|
||
data['sma_20'] = data['close'].rolling(20).mean()
|
||
return data
|
||
|
||
def generate_signal(self, idx: int) -> Signal:
|
||
"""
|
||
Lógica de tu estrategia
|
||
"""
|
||
current_price = self.data.iloc[idx]['close']
|
||
sma = self.data.iloc[idx]['sma_20']
|
||
|
||
# Ejemplo: comprar si precio > SMA
|
||
if current_price > sma and self.current_position == 0:
|
||
return Signal.BUY
|
||
|
||
# Vender si precio < SMA y tenemos posición
|
||
elif current_price < sma and self.current_position > 0:
|
||
return Signal.SELL
|
||
|
||
return Signal.HOLD
|
||
|
||
# Usar tu estrategia
|
||
strategy = MiEstrategia()
|
||
engine = BacktestEngine(strategy, initial_capital=10000)
|
||
results = engine.run(data)
|
||
```
|
||
|
||
## 📈 Métricas Disponibles
|
||
|
||
### Básicas
|
||
- **Total Return**: Retorno total del periodo
|
||
- **Total Trades**: Número de trades ejecutados
|
||
- **Win Rate**: Porcentaje de trades ganadores
|
||
- **Profit Factor**: Ganancia bruta / Pérdida bruta
|
||
|
||
### Avanzadas
|
||
- **Sharpe Ratio**: Retorno ajustado por riesgo
|
||
- **Sortino Ratio**: Como Sharpe pero solo penaliza volatilidad a la baja
|
||
- **Max Drawdown**: Máxima caída desde un pico
|
||
- **Calmar Ratio**: Retorno anualizado / Max Drawdown
|
||
- **Expectancy**: Ganancia esperada por trade
|
||
- **Recovery Factor**: Net Profit / Max Drawdown
|
||
|
||
## 🎯 Estructura de Resultados
|
||
|
||
```python
|
||
results = {
|
||
# Capital
|
||
'initial_capital': 10000,
|
||
'final_equity': 12500,
|
||
'total_return': 0.25,
|
||
'total_return_pct': 25.0,
|
||
'total_pnl': 2500,
|
||
|
||
# Trades
|
||
'total_trades': 15,
|
||
'winning_trades': 9,
|
||
'losing_trades': 6,
|
||
'win_rate': 0.6,
|
||
'win_rate_pct': 60.0,
|
||
|
||
# Performance
|
||
'gross_profit': 3000,
|
||
'gross_loss': 500,
|
||
'profit_factor': 6.0,
|
||
'avg_trade': 166.67,
|
||
'avg_win': 333.33,
|
||
'avg_loss': -83.33,
|
||
|
||
# Riesgo
|
||
'max_drawdown': -0.15,
|
||
'max_drawdown_pct': -15.0,
|
||
'sharpe_ratio': 1.8,
|
||
|
||
# Para análisis
|
||
'equity_curve': [...],
|
||
'timestamps': [...],
|
||
'trades': [Trade(...), Trade(...), ...]
|
||
}
|
||
```
|
||
|
||
## 💡 Mejores Prácticas
|
||
|
||
### 1. Periodo de Backtest
|
||
- **Mínimo**: 1 año de datos
|
||
- **Recomendado**: 3-5 años
|
||
- **Ideal**: Múltiples ciclos de mercado
|
||
|
||
### 2. Comisiones y Slippage
|
||
- Siempre incluir comisiones realistas
|
||
- Incluir slippage (0.05% - 0.1%)
|
||
- No optimizar en exceso (overfitting)
|
||
|
||
### 3. Position Sizing
|
||
- No usar 100% del capital por trade
|
||
- Recomendado: 50-95% del capital disponible
|
||
- Considerar gestión de riesgo
|
||
|
||
### 4. Validación
|
||
- **In-sample**: Periodo de entrenamiento/optimización
|
||
- **Out-of-sample**: Periodo de validación (datos no vistos)
|
||
- **Walk-forward**: Validación continua
|
||
|
||
### 5. Métricas Importantes
|
||
- No solo mirar retorno total
|
||
- Sharpe Ratio > 1.0 es bueno, > 2.0 es excelente
|
||
- Max Drawdown < 20% es aceptable
|
||
- Win Rate: >50% es bueno, pero no es lo único importante
|
||
- Profit Factor > 1.5 es bueno, > 2.0 es excelente
|
||
|
||
## ⚠️ Advertencias
|
||
|
||
### Limitaciones del Backtesting
|
||
|
||
1. **Look-ahead bias**: No usar información futura
|
||
2. **Survivorship bias**: Incluir activos que ya no existen
|
||
3. **Overfitting**: Optimizar demasiado para datos históricos
|
||
4. **Market conditions**: Pasado no garantiza futuro
|
||
5. **Ejecución perfecta**: Backtesting asume ejecución instantánea
|
||
|
||
### Realismo
|
||
|
||
El backtest asume:
|
||
- ✅ Comisiones y slippage incluidos
|
||
- ✅ No hay look-ahead bias
|
||
- ❌ Liquidez infinita (órdenes siempre se ejecutan)
|
||
- ❌ No considera impacto de mercado
|
||
- ❌ No simula rechazo de órdenes
|
||
|
||
## 📊 Ejemplos de Resultados
|
||
|
||
### Estrategia Exitosa
|
||
```
|
||
Retorno Total: 45.2%
|
||
Sharpe Ratio: 2.1
|
||
Max Drawdown: -12.3%
|
||
Win Rate: 58%
|
||
Profit Factor: 2.4
|
||
```
|
||
|
||
### Estrategia Problemática
|
||
```
|
||
Retorno Total: 15.2%
|
||
Sharpe Ratio: 0.4
|
||
Max Drawdown: -35.8%
|
||
Win Rate: 45%
|
||
Profit Factor: 1.1
|
||
```
|
||
|
||
## 🔁 Walk-Forward Validation (Out-of-Sample)
|
||
|
||
### 📌 ¿Qué es Walk-Forward Validation?
|
||
|
||
El *walk-forward validation* es una técnica avanzada de validación que simula cómo se comportaría una estrategia en condiciones reales:
|
||
|
||
- Los parámetros se **optimizan solo en datos pasados (TRAIN)**
|
||
- La estrategia se **ejecuta en datos futuros no vistos (TEST)**
|
||
- El proceso se repite de forma deslizante a lo largo del tiempo
|
||
|
||
Esto evita:
|
||
- Look-ahead bias
|
||
- Overfitting clásico
|
||
- Optimismo artificial en backtests
|
||
|
||
Es el estándar en *quant research* profesional.
|
||
|
||
---
|
||
|
||
### 🧠 Metodología aplicada en este proyecto
|
||
|
||
Para cada ventana temporal:
|
||
|
||
1. **TRAIN**
|
||
- Periodo fijo de entrenamiento
|
||
- Optimización por grid search
|
||
- Selección de parámetros según métrica objetivo (Sharpe Ratio)
|
||
|
||
2. **TEST (Out-of-Sample)**
|
||
- Backtest con los mejores parámetros del TRAIN
|
||
- Sin reoptimización
|
||
- Métricas registradas de forma independiente
|
||
|
||
3. **Desplazamiento**
|
||
- La ventana avanza en el tiempo
|
||
- Se repite el proceso hasta agotar los datos
|
||
|
||
---
|
||
|
||
### ⏱️ Configuración utilizada
|
||
|
||
- **Activo:** BTC/USDT
|
||
- **Timeframe:** 1h
|
||
- **Ventana TRAIN:** 365 días
|
||
- **Ventana TEST:** 90 días
|
||
- **Step:** 90 días
|
||
- **Capital inicial:** $10,000
|
||
- **Comisión:** 0.1%
|
||
- **Slippage:** 0.05%
|
||
|
||
**Estrategia base:**
|
||
- Moving Average Crossover
|
||
- Filtro de tendencia con ADX
|
||
|
||
---
|
||
|
||
### 📊 Resultados por ventana (TEST – Out-of-Sample)
|
||
|
||
| Window | Return % | Sharpe | Max DD % | Trades | Parámetros |
|
||
|------|----------|--------|----------|--------|------------|
|
||
| 1 | +38.00 | 0.75 | -11.93 | 3 | MA(15/50) + ADX 25 |
|
||
| 2 | +3.62 | 0.10 | -23.67 | 2 | MA(15/50) + ADX 30 |
|
||
| 3 | +8.54 | 0.22 | -10.28 | 2 | MA(15/50) + ADX 30 |
|
||
| 4 | 0.00 | 0.00 | 0.00 | 0 | Sin trades |
|
||
| 5 | +9.71 | 0.26 | -10.57 | 2 | MA(15/50) + ADX 30 |
|
||
| 6 | 0.00 | 0.00 | 0.00 | 0 | Sin trades |
|
||
| 7 | -2.25 | -0.06 | -12.42 | 2 | MA(15/50) + ADX 30 |
|
||
| 8 | -2.27 | -0.13 | -5.46 | 2 | MA(15/30) + ADX 30 |
|
||
|
||
---
|
||
|
||
### 📈 Métricas agregadas (Out-of-Sample)
|
||
|
||
- **Ventanas evaluadas:** 8
|
||
- **Retorno medio:** +6.92%
|
||
- **Sharpe medio:** 0.14
|
||
- **Max Drawdown medio:** -9.29%
|
||
|
||
---
|
||
|
||
### 🧩 Interpretación de resultados
|
||
|
||
- La estrategia **no es sobreoptimizada**
|
||
- Existen ventanas sin operaciones → el sistema sabe **no operar**
|
||
- Las pérdidas están **controladas**
|
||
- No hay colapsos en mercados adversos
|
||
- El rendimiento depende claramente del régimen de mercado
|
||
|
||
Este comportamiento es consistente con una estrategia:
|
||
- Tendencial
|
||
- Conservadora
|
||
- Apta para mejoras vía *position sizing* y *portfolio diversification*
|
||
|
||
---
|
||
|
||
### ✅ Decisiones tomadas tras Walk-Forward
|
||
|
||
1. **NO modificar la lógica de entrada**
|
||
2. **NO optimizar más los parámetros base**
|
||
3. Mantener el filtro ADX como componente estructural
|
||
4. Avanzar hacia mejoras de:
|
||
- Position sizing
|
||
- Stops dinámicos
|
||
- Portfolio multi-asset
|
||
|
||
El walk-forward valida que la señal base es **estable y explotable**, aunque no espectacular por sí sola.
|
||
|
||
## 🔄 Próximos Pasos
|
||
|
||
Después del backtesting:
|
||
1. ✅ Si resultados son buenos → Paper trading
|
||
2. ✅ Validar en out-of-sample
|
||
3. ✅ Optimizar parámetros (con cuidado)
|
||
4. ✅ Añadir gestión de riesgo
|
||
5. ✅ Testear en diferentes condiciones de mercado
|
||
|
||
## 📝 Notas
|
||
|
||
- Siempre testea en datos out-of-sample
|
||
- Un buen backtest no garantiza éxito futuro
|
||
- Considera paper trading antes de dinero real
|
||
- Mantén expectativas realistas
|
||
- El mercado puede cambiar
|
||
|
||
---
|
||
|
||
**Para dudas o problemas, consulta el README principal del proyecto.** |