Motor de backtesting de estrategias de trading en Python, diseñado con Domain-Driven Design y arquitectura hexagonal (ports & adapters). Tres interfaces sobre el mismo núcleo: CLI, TUI (Textual) y GUI de escritorio (PySide6, estética terminal).
Simular una estrategia contra datos históricos: "si hubiera operado con estas reglas, ¿cuánto habría ganado o perdido?" El motor recorre velas OHLCV en orden cronológico, la estrategia emite señales, un broker simulado ejecuta órdenes (comisiones + slippage) y se calculan métricas: retorno, drawdown máximo, Sharpe, Sortino, profit factor, expectancy.
flowchart LR
subgraph Interfaces["🖥️ interfaces (adaptadores de entrada)"]
CLI[CLI · argparse]
TUI[TUI · Textual]
GUI[GUI · PySide6]
end
subgraph Application["⚙️ application (casos de uso)"]
RB[RunBacktest]
CS[CompareStrategies]
WF[WalkForwardAnalysis]
end
subgraph Domain["🧠 domain (núcleo — cero imports hacia fuera)"]
PF["Portfolio (agregado raíz)<br/>invariantes: cash, margen, no-cruce"]
OR["Order (entidad)<br/>MARKET · LIMIT · STOP"]
RK["RiskPolicy (VO)<br/>SL/TP espejo + OCO"]
EX["ExecutionSimulator<br/>(servicio de dominio)"]
AN["PerformanceAnalyzer<br/>(servicio de dominio)"]
PT["Puertos: MarketDataFeed<br/>Strategy · ResultRepository"]
end
subgraph Infra["🔌 infrastructure (adaptadores de salida)"]
CSV[CsvMarketDataFeed]
LST[ListMarketDataFeed]
SQL[SqliteResultRepository]
FUT[/"futuro: CryptoWatch / Binance feed"/]
end
STR["📈 strategies<br/>SMA Cross · RSI · registry"]
CLI --> Application
TUI --> Application
GUI --> Application
Application --> Domain
STR -. implementan Strategy .-> PT
CSV -. implementa MarketDataFeed .-> PT
LST -. implementa MarketDataFeed .-> PT
SQL -. implementa ResultRepository .-> PT
FUT -. implementa MarketDataFeed .-> PT
Regla de dependencia: las flechas de import solo apuntan hacia dentro.
domain/ no conoce a nadie; las interfaces y la infraestructura conocen a todos.
sequenceDiagram
participant F as MarketDataFeed
participant U as RunBacktest
participant S as Strategy
participant E as ExecutionSimulator
participant P as Portfolio
loop por cada vela
F->>U: Candle (OHLCV)
U->>E: ¿protecciones pendientes? (SL/TP vs high/low)
E-->>P: fill + cancelación OCO de la hermana
U->>S: on_candle(candle)
S-->>U: Signal (ENTER/EXIT · LONG/SHORT · HOLD)
U->>E: orden MARKET (± slippage, comisión)
E-->>P: apply_fill() — invariantes del agregado
P-->>U: equity, drawdown
end
U->>U: métricas finales (Sharpe, Sortino, PF, expectancy)
gantt
dateFormat X
axisFormat %s
title Ventanas rodantes: optimiza en train, valida en test
section Ventana 1
train (elige mejor config) :a1, 0, 100
test (fuera de muestra) :crit, 100, 150
section Ventana 2
train :a2, 50, 150
test :crit, 150, 200
En cada ventana se ejecutan todas las configuraciones sobre el tramo train, se elige la mejor por Sharpe, y esa ganadora (instancia fresca) se evalúa sobre el tramo test — datos que no ha visto. El informe final incluye retorno medio fuera de muestra, % de ventanas positivas y veredicto.
No hay main.py: el punto de entrada se declara en pyproject.toml
([project.scripts]). El composition root es interfaces/cli.py.
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev,gui]"
pytest # 27 tests
quantlab strategies
quantlab run data/sample_btc.csv --strategy sma_cross --params fast=8,slow=21 \
--stop-loss 0.05 --take-profit 0.10 --db runs.db
quantlab run data/sample_btc.csv --strategy sma_cross \
--params fast=5,slow=15,mode=both --allow-short
quantlab compare data/sample_btc.csv --spec sma_cross:fast=8,slow=21 \
--spec rsi:period=14 --db runs.db
quantlab walkforward data/sample_btc.csv --spec sma_cross:fast=5,slow=15 \
--spec sma_cross:fast=8,slow=21 --train 100 --test 50
quantlab history --db runs.db
quantlab tui # interfaz de terminal (Textual)
quantlab gui # aplicación de escritorio (PySide6, estética terminal)- Dominio rico:
Portfoliocomo agregado raíz con invariantes reales (cash, margen 100 % para cortos, prohibición de cruzar por cero),Ordercon ciclo de vida, Value Objects validados, eventos de dominio. - Gestión de riesgo: stop-loss / take-profit automáticos con grupos OCO; protecciones espejo para posiciones cortas.
- Short selling: posiciones con cantidad negativa, PnL invertido, reversión automática long⇄short desde las señales.
- Analytics: Sharpe, Sortino, volatilidad anualizada, profit factor,
expectancy — con
Decimalen todo el dinero, nuncafloat. - Walk-forward analysis: ventanas rodantes train/test.
- Persistencia vía puerto
ResultRepository+ adaptador SQLite. - Tres interfaces, una capa visual: los renderables Rich del presenter se comparten entre CLI, TUI (Textual) y GUI (PySide6 exportando a HTML).
- CI: GitHub Actions con ruff + mypy + pytest en Python 3.11/3.12.
src/quantlab/
├── domain/ # Núcleo: modelo, puertos, servicios, eventos
├── application/ # Casos de uso: run, compare, walk-forward
├── infrastructure/ # Adaptadores: CSV, lista en memoria, SQLite
├── strategies/ # SMA Cross, RSI + registro por nombre
└── interfaces/ # CLI, TUI, GUI + presenter compartido
- Adaptador de datos de Crypto-Watch Terminal / Binance (histórico real)
- Event sourcing: reconstruir el portfolio desde los eventos
- Grid-search de parámetros dentro del walk-forward
- Comparador y walk-forward dentro de la TUI/GUI
- Exportación de informes (HTML/PDF)
MIT © 2026 Zoel Manchón