Dentro de nuestro algoritmo propio: HRP + Long/Short + CVaR con Hull-White
📄 Este artículo se convirtió en un paper de investigación. La asignación HRP en el corazón de este pipeline se somete a una prueba controlada frente a Markowitz, el shrinkage de Ledoit-Wolf y 1/N bajo covarianza conocida (4.800 experimentos). Lee el paper en línea (versión interactiva + PDF) en hrp.marketmaker.cc, código y datos en github.com/suenot/hrp-validation.
En nuestro resumen «12 algoritmos de optimización de carteras, comparados» enfrentamos una docena de métodos de asignación uno junto al otro. Once de ellos son clásicos de manual. El duodécimo, Pipeline, es el nuestro — y recibió exactamente un punto en esa entrada. Este artículo es la inmersión profunda: qué hay dentro, de dónde viene cada fórmula y cómo la especificación se convierte en Rust.
Pipeline no inventa una nueva forma de calcular pesos. Toma la receta más robusta conocida — Hierarchical Risk Parity (HRP) — y la envuelve con las dos capas que una cuenta de trading en vivo realmente necesita pero que el HRP puro no tiene: dirección (long/short a partir de señales de estrategia) y un presupuesto de riesgo estricto (CVaR ajustado al régimen de volatilidad actual). Eso da cuatro etapas.
Las cuatro etapas
- I — retornos logarítmicos de cada activo.
- II — pesos base a partir de HRP.
- III — una división long/short a partir de señales de agentes, con las cuotas de riesgo fijadas por confianza.
- IV — una corrección CVaR con volatilidad Hull-White; el exceso de riesgo va a efectivo.
Recorrámoslas en orden.
Etapa I. Retornos logarítmicos
Todo comienza con el paso de precios a retornos logarítmicos:
donde es el activo y el paso temporal. Los retornos logarítmicos se suman a lo largo del tiempo y son más simétricos que los cambios porcentuales simples — la entrada estándar para cualquier cálculo de covarianza.
Etapa II. HRP como fundamento
HRP, propuesto por Marcos López de Prado en 2016, esquiva la dolencia central de la optimización media-varianza — invertir una matriz de covarianza mal condicionada. Nunca la invierte en absoluto. En su lugar, trabaja con la estructura de las correlaciones.
Covarianza y correlación
A partir de los retornos construimos la matriz de covarianza y la normalizamos en la matriz de correlación :
Matriz de distancias
Convertimos la correlación en una métrica de distancia, de modo que los activos fuertemente correlacionados queden "cerca":
Cuanto más cerca esté de 1, más cerca está de 0 — y más probable es que los activos compartan un clúster.
Dendrograma y orden de hojas
A partir de la matriz de distancias construimos una jerarquía de clústeres mediante average linkage y leemos el orden de hojas — una permutación de los activos en la que los similares quedan adyacentes.
Paso opcional: el número óptimo de clústeres puede elegirse mediante el coeficiente de silueta , donde es la distancia media dentro de un clúster y la distancia media al vecino más cercano. El pase base no lo necesita — la bisección recursiva ya respeta la jerarquía.
Cuasi-diagonalización
Permutamos las filas y columnas de según , agrupando los valores grandes a lo largo de la diagonal:
Bisección recursiva
Luego la recursión avanza de arriba hacia abajo. En cada paso un clúster se divide en dos mitades y , y el capital se asigna entre las mitades inversamente proporcional a sus varianzas:
La varianza de un clúster se calcula sobre su subbloque de covarianza como . El descenso continúa hasta que cada nodo contiene un solo activo. Los pesos son solo largos, no negativos, y suman 1,0.
En nuestra implementación, esta es la función hrp_from_cov(cov) -> Vec<f64>: correlación → distancia → average linkage → orden de hojas → cuasi-diagonalización → bisección recursiva. Pipeline la llama como su base — y también es el optimize() público para el caso sin señales.
Etapa III. La capa long/short
El HRP puro es una cartera "solo comprar". Pero una estrategia a menudo indica no solo cuánto sino en qué dirección. La etapa III toma señales por activo (Long/Short) del agente y construye dos subcarteras.
- Los activos se dividen en cestas long y short según la señal.
- Dentro de cada cesta, los pesos se calculan con el mismo HRP (sobre el subbloque de covarianza de esos activos), sumando 1 por cesta.
- Si el agente también emite una confianza , las cuotas de riesgo entre los lados se fijan por confianza total:
Sin confianza, las cuotas caen de vuelta al número de activos en cada cesta. El peso con signo final es para los largos y para los cortos, tras lo cual toda la exposición bruta se normaliza a 1.
Una nota honesta sobre el código. La especificación original lleva factores de corrección , — pero también los marca con un "¿realmente necesitamos este paso?". La implementación no los aplica: los dos lados se combinan directamente mediante las cuotas de riesgo , lo que mantiene la exposición bruta exactamente en 1 y no crea apalancamiento oculto. Es una simplificación deliberada de la especificación, no un descuido.
Etapa IV. CVaR con un ajuste Hull-White
El HRP equilibra el riesgo estructuralmente, pero no sabe nada sobre el nivel absoluto de riesgo en términos monetarios. La etapa final impone un techo estricto al riesgo de cola — y lo hace sensible a un cambio de régimen de mercado.
Retorno de la cartera y volatilidad EWMA
Primero colapsamos los pesos en un retorno de cartera y estimamos la volatilidad condicional con EWMA:
con (el valor clásico de RiskMetrics). EWMA da la volatilidad "de hoy" en lugar de una promediada sobre toda la historia.
Reescalado Hull-White
La idea clave: los retornos pasados no pueden tomarse tal cual — ocurrieron bajo una volatilidad diferente. El método Hull-White reescala cada retorno pasado al nivel actual:
Un mes tranquilo se "estira", uno turbulento se "comprime", y la distribución se lleva al régimen actual.
VaR y CVaR
Sobre la distribución reescalada tomamos el cuantil de pérdida y la pérdida media en la cola:
CVaR (también conocido como Expected Shortfall) no responde "qué tan malo es un día malo típico" sino "qué tan malo es en promedio a través del peor por ciento" — así que ve el grosor de la cola, no solo su borde.
Presupuesto de riesgo y efectivo
Si el CVaR supera el umbral aceptable, cada posición de riesgo se reduce con un único factor, y el capital liberado pasa a efectivo:
Así, la cartera se desapalanca de riesgo cuando el riesgo de cola crece y vuelve a entrar en el mercado cuando se calma.
De la especificación al código
Todo el algoritmo vive en un único crate de Rust, portfolio-pipeline, y obedece el contrato uniforme del workspace:
pub fn optimize(prices: &[Vec<f64>]) -> Vec<f64>
Esta es la proyección solo-larga (etapas I, II, IV sin señales) — exactamente la misma interfaz prices -> weights que los otros once algoritmos, de modo que Pipeline es un reemplazo directo para cualquiera de ellos. La versión completa con todas las etapas es una función separada:
pub fn run(
prices: &[Vec<f64>],
signals: Option<&[Side]>, // Long / Short per asset
confidence: Option<&[f64]>, // agent confidence → risk shares λ
cfg: &PipelineConfig, // CVaR / Hull-White parameters
) -> PipelineResult // signed weights + cash + cvar + σ
Los valores por defecto de la capa: cola cvar_alpha = 0.05, presupuesto cvar_max = 0.05, EWMA ewma_lambda = 0.94, ventana Hull-White hw_window = 0 (toda la historia). La implementación no tiene dependencias externas y es deliberadamente defensiva: en historiales cortos (menos de 4 puntos de precio) devuelve pesos iguales, y la capa CVaR solo se activa a partir de ≥8 observaciones de retorno — de lo contrario no hay nada de donde estimar una cola.
Por qué Rust: una única base de código determinista tanto para backtest como para producción, sin la deriva de "Python en investigación, otra cosa en producción", y lo bastante rápida para ejecutar los doce algoritmos en una sola solicitud a través del backend de comparación.
Lo que cuesta en tiempo
¿Qué tan rápido es "lo bastante rápido"? Extrajimos el núcleo de HRP (retornos logarítmicos → covarianza → average linkage → cuasi-diagonalización → pesos recursivos) a un benchmark independiente y ejecutamos exactamente la misma matemática en siete lenguajes — C, C++, Rust, Zig, Python, Node.js y Bun — bajo condiciones idénticas: Apple Silicon, un solo hilo, 365 observaciones diarias por activo, precios sintéticos, número de activos de 10 a 10.000.
Una palabra sobre la complejidad, porque define toda la forma. La versión de manual de average linkage vuelve a escanear toda la matriz de distancias en busca del par más cercano en cada fusión — eso es y se convierte en el cuello de botella con unos pocos miles de activos. El benchmark usa en su lugar el algoritmo de cadena de vecinos más cercanos (Müllner 2011) — el mismo que hay detrás de linkage(method='average') de SciPy. Con eso implementado, el clustering ya no es la etapa dominante: en son ~15 ms de un pase de ~0,5 s. El costo ahora está dominado por la matriz de covarianza, — la única etapa que ningún método tipo HRP puede evitar.
Lo que muestran las ejecuciones (las tablas completas por lenguaje y un script de reproducción de un solo comando están en el repositorio del proyecto):
- En carteras realistas es gratis. Una cesta cripto tiene decenas de activos, rara vez más de cien. En un pase completo de HRP es de milisegundos de un solo dígito incluso en Node y microsegundos en Rust/C. Recalcular pesos en cada tick no es un problema.
- Rust está dentro de ~1,0–1,3× de C — el mismo orden de magnitud, ambos compilados, y efectivamente empatados una vez que alcanza los miles. C es un poco más rápido en aritmética pura, pero Rust ofrece la misma previsibilidad sin recolector de basura y sin comportamiento indefinido.
- Escala a miles de activos. Con linkage un pase completo es de ~0,5 s en y unos pocos segundos en en los lenguajes compilados; incluso Node interpretado supera en menos de dos segundos. Lo que ahora marca el techo es la etapa de covarianza, no el clustering.
La conclusión pragmática: en nuestros tamaños de cartera, elegir Rust no se trata de "vencer a C" (C es ligeramente más rápido aquí) — se trata de una única base de código determinista para investigación y producción, sin pausas de GC y con años de margen de rendimiento. El benchmark completo de siete lenguajes, con resultados y script de reproducción, está abierto en el repositorio del proyecto.
Dónde se sitúa Pipeline entre los doce
En nuestra comparación sobre una única cesta (deliberadamente amañada), Pipeline se comportó como HRP — porque a través del punto de entrada solo-largo optimize() es HRP con una capa CVaR. Su maquinaria direccional solo cobra vida cuando se le alimentan señales de estrategia. Ese es todo el punto: Pipeline no es "otro optimizador más para hacer backtest de pesos" sino la capa de ejecución entre las señales de estrategia y las órdenes reales — toma tus decisiones de compra/venta, distribuye capital vía HRP dentro de cada lado, equilibra los lados por confianza y recorta el riesgo de cola hasta un presupuesto fijado.
Para el contexto completo — qué otros once métodos existen y en qué se diferencian — ver el resumen, «12 algoritmos de optimización de carteras, comparados». Y puedes probar todo en vivo en portfolio-optimizer.marketmaker.cc.
Referencias
- López de Prado, M. (2016). Building Diversified Portfolios that Outperform Out of Sample. The Journal of Portfolio Management.
- López de Prado, M. (2018). Advances in Financial Machine Learning. Wiley.
- Hull, J., & White, A. (1998). Incorporating Volatility Updating into the Historical Simulation Method for Value at Risk. Journal of Risk.
- Rockafellar, R. T., & Uryasev, S. (2000). Optimization of Conditional Value-at-Risk. Journal of Risk.
- RiskMetrics Group (1996). RiskMetrics — Technical Document. J.P. Morgan.
- Marketmaker.cc: marketmaker.cc
Cita
@article{soloviov2026pipeline,
author = {Soloviov, Eugen and Zhuravleva, Marina and Kiselev, Kirill},
title = {Inside Our House Algorithm: HRP + Long/Short + CVaR with Hull-White Adjustment},
year = {2026},
url = {https://marketmaker.cc/es/blog/post/portfolio-pipeline-hrp-cvar},
description = {A deep dive into Pipeline, a composite portfolio allocation algorithm built on Hierarchical Risk Parity with a signal-driven long/short overlay and a Hull-White CVaR risk-budget correction, with the full specification and its Rust implementation.}
}
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.
Financial mathematics
Fifth-year student at Bauman Moscow State Technical University (Automatic Control Systems), specializing in financial mathematics. Background in calibrating stochastic-volatility (Heston) and local-volatility (Dupire) models, fair pricing of options including exotics via both Monte-Carlo and analytic formulas, hedging-error reduction, and exposure to LSV models.
Portfolio optimization
Fourth-year student at the Faculty of Mechanics and Mathematics, Novosibirsk State University (NSU); thesis on Heston-model calibration and delta-hedging within the same model. Works on portfolio optimization.