Files
Trading-Bot/scripts/research/compare_stops.py
DaM f85c522f22 feat: finalize portfolio system and quantitative validation- Finalized MA_Crossover(30,100) and TrendFiltered_MA(30,100,ADX=15)
- Implemented portfolio engine with risk-based allocation (50/50)
- Added equity-based metrics for system-level evaluation
- Validated portfolio against standalone strategies
- Reduced max drawdown and volatility at system level
- Quantitative decision closed before paper trading phase
2026-02-02 14:38:05 +01:00

186 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# scripts/research/compare_stops.py
import os
import sys
from pathlib import Path
from dotenv import load_dotenv
from datetime import datetime, timedelta
import pandas as pd
import matplotlib.pyplot as plt
from loguru import logger
# Añadir raíz del proyecto al path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from src.utils.logger import log
from src.data.storage import StorageManager
from src.core.engine import Engine
from src.strategies import MovingAverageCrossover
from src.risk.sizing.fixed import FixedPositionSizer
from src.risk.stops.fixed_stop import FixedStop
from src.risk.stops.trailing_stop import TrailingStop
from src.risk.stops.atr_stop import ATRStop
# --------------------------------------------------
# Configuración común
# --------------------------------------------------
SYMBOL = "BTC/USDT"
TIMEFRAME = "1d"
DAYS_BACK = 180
INITIAL_CAPITAL = 10_000
COMISSION = 0.001
SLIPPAGE = 0.0005
# Estrategia
STRATEGY = MovingAverageCrossover(
fast_period=10,
slow_period=30,
ma_type='ema',
use_adx=False,
adx_threshold=20
)
# Position sizing fijo (para aislar el efecto del stop)
POSITION_SIZER = FixedPositionSizer(0.95)
# Stops a comparar
STOPS = {
"Fixed 2%": FixedStop(0.02),
"Trailing 2%": TrailingStop(0.02),
"ATR 14 x 2.0": ATRStop(atr_period=14, multiplier=2.0),
}
def setup_environment():
"""Carga variables de entorno"""
env_path = Path(__file__).parent.parent.parent / 'config' / 'secrets.env'
load_dotenv(dotenv_path=env_path)
def load_data():
"""
Carga datos desde la base de datos
"""
# Setup
setup_environment()
# Cargar datos
storage = StorageManager(
db_host=os.getenv("DB_HOST"),
db_port=int(os.getenv("DB_PORT", 5432)),
db_name=os.getenv("DB_NAME"),
db_user=os.getenv("DB_USER"),
db_password=os.getenv("DB_PASSWORD"),
)
end_date = datetime.now()
start_date = end_date - timedelta(DAYS_BACK)
data = storage.load_ohlcv(
symbol=SYMBOL,
timeframe=TIMEFRAME,
start_date=None,
end_date=None,
use_cache=False,
)
storage.close()
if data.empty:
raise RuntimeError("No se cargaron datos")
return data
def run():
log.info("=" * 80)
log.info("🧪 COMPARACIÓN DE STOPS (Research)")
log.info("=" * 80)
data = load_data()
log.info(f"Símbolo: {SYMBOL}")
log.info(f"Timeframe: {TIMEFRAME}")
log.info(f"Velas: {len(data)}")
log.info(f"Periodo: {data.index[0]}{data.index[-1]}")
print()
results = {}
# --------------------------------------------------
# Ejecutar backtest por cada stop
# --------------------------------------------------
for name, stop in STOPS.items():
log.info(f"▶️ Ejecutando con stop: {name}")
# Silenciar logs del engine (solo warnings o errores)
logger.disable("src.backtest.engine")
engine = Engine(
strategy=STRATEGY,
initial_capital=INITIAL_CAPITAL,
commission=COMISSION,
slippage=SLIPPAGE,
position_sizer=POSITION_SIZER,
stop_loss=stop,
)
res = engine.run(data)
results[name] = res
logger.enable("src.backtest.engine")
log.info(
f"Trades: {res['total_trades']:>4} | "
f"Max DD: {res['max_drawdown_pct']:>7.2f}% | "
f"Return: {res['total_return_pct']:>7.2f}%"
)
print()
print("\n" + "=" * 70)
print("📊 RESUMEN COMPARATIVO DE STOPS")
print("=" * 70)
for name, res in results.items():
print(
f"{name:<15} | "
f"Trades: {res['total_trades']:>4} | "
f"Max DD: {res['max_drawdown_pct']:>7.2f}% | "
f"Return: {res['total_return_pct']:>7.2f}%"
)
print("=" * 70)
# --------------------------------------------------
# Plot equity curves (comparativa visual)
# --------------------------------------------------
plt.figure(figsize=(14, 7))
for name, res in results.items():
plt.plot(
res["timestamps"],
res["equity_curve"],
label=name,
)
plt.title(f"Equity Curve Comparison {SYMBOL} {TIMEFRAME}")
plt.xlabel("Time")
plt.ylabel("Equity")
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
# --------------------------------------------------
# Guardar gráfico
# --------------------------------------------------
output_dir = Path(__file__).parent / "output"
output_dir.mkdir(parents=True, exist_ok=True)
output_path = output_dir / f"equity_comparison_{SYMBOL.replace('/', '_')}_{TIMEFRAME}.png"
plt.savefig(output_path, dpi=150)
plt.close()
log.success(f"📈 Equity curve guardada en: {output_path}")
if __name__ == "__main__":
run()