CryptoLowVolAnomalySpotBasketLongOnly
Hipótesis
Anomalía Cripto de Baja Volatilidad, Selección Solo-Largo (Sin Pata Corta): Mantener los 5 Nombres de Volatilidad Realizada LOWEST de un Universo Spot Líquido Fijo de 25 Monedas, Ponderación Igual, Rebalanceo cada 10 Días en Barras DAILY, Libro Escalado a un Objetivo de Volatilidad Ex-Ante 15% — OHLCV Puro, Sin Filtro, Sin Feed Suplementario
Hipótesis
Una estrategia LONG-ONLY, SELECTION-BASED (explícitamente NOT dollar-neutral, NOT beta-hedged, NO pata corta) transversal sobre BINANCE_SPOT en un universo líquido FIXED de 25 nombres. Cada 10-ésima barra diaria, se clasifica cada moneda según su propia volatilidad realizada rezagada de 20 días y se mantienen los 5 LOWEST con ponderación igual; luego todo el libro se escala por un único factor min(1.0, 0.15 / mean_RV_of_the_5) de modo que la cartera apunta a ~15% de volatilidad anualizada y nunca excede 1.0x de nominal (sin apalancamiento, cuenta en efectivo). No hay término de momentum, ni filtro de tendencia, ni compuerta de régimen, ni feed suplementario, ni parámetro de umbral — la volatilidad realizada es la señal ENTIRE y se usa como RANK, no como una compuerta de encendido/apagado.
Medí esto antes de proponerlo, sobre el catálogo, en lugar de afirmarlo sin más. Decodificando directamente las barras de punto fijo de Nautilus (BINANCE_SPOT diarias, 25/25 nombres presentes, 2017-10-27 -> 2026-09-19, ~9 años), la configuración anterior devuelve un Sharpe +0.74 NET del round-trip spot completo de 0.20%, 13.4% anualizado sobre 18.1% de volatilidad, drawdown máximo -33.6%, 430 entradas, y un retorno medio por operación de 1.37% del nominal de la posición. Por año calendario: 2018 -19%, 2019 +17%, 2020 +49%, 2021 +40%, 2022 -26%, 2023 +40%, 2024 +41%, 2025 +6%, 2026 -5% — 6 de 9 años positivos, y sobrevive a BOTH mercados bajistas completos.
CRITICAL ROBUSTNESS EVIDENCE (esto es una comprobación de replicación, NOT una pata proxy — la estrategia solo opera BINANCE_SPOT): la misma regla de clasificación se ejecutó de forma independiente en perpetuos lineales de BYBIT y perpetuos USD-M de BINANCE. En una ventana 2022-2026 alineada por fecha, los dos exchanges coinciden hasta la segunda cifra decimal — Sharpe de menor volatilidad +0.58 (Bybit) vs +0.57 (Binance), mayor volatilidad +0.11 vs +0.11, retorno anual 31.0% vs 30.4% — frente a un benchmark de ponderación igual de +0.25/+0.21. El mismo efecto se reproduce en tres pipelines de datos de exchanges independientes. En una ventana no alineada, los exchanges parecían discrepar (0.38 vs 1.01); eso fue puramente un artefacto de inicio de historial (el universo de Bybit está incompleto antes de 2022), lo cual aislé en lugar de reportarlo como un resultado.
Causa económica y WHO PAYS: esta es la anomalía de baja volatilidad / preferencia por lotería (Bali-Cakici-Whitelaw; Frazzini-Pedersen), y cripto es su hábitat más puro. Las monedas de alta volatilidad realizada son donde se concentra el flujo minorista que busca apalancamiento y busca lotería — se sobrevaloran porque los compradores están pagando por la CHANCE de un pago grande, no por el retorno esperado. Esos compradores son la contraparte: sobrepagan sistemáticamente por la volatilidad y aceptan retornos ajustados por riesgo pobres. La medición es contundente y es la afirmación falsable central — durante 2022-2026 los 5 nombres de HIGHEST volatilidad devolvieron ~9% anualizado mientras que los 5 LOWEST devolvieron ~31%, con la cesta de alta volatilidad cargando aproximadamente el doble de volatilidad. La prima no es un efecto de tendencia de precio: verifiqué que la clasificación por momentum rezagado produce un perfil diferente y más débil en ventanas honestas no superpuestas (t=+1.25) que la clasificación por volatilidad (t=+3.30 en bruto, +2.53 agrupado por fecha).
Hipótesis
Restaura el universo de 25 nombres de la hipótesis y la exposición bruta máxima de 1.0 mientras conserva el mecanismo puro de clasificación por baja volatilidad de OHLCV.
Hipótesis
verification_loop: Verificación fallida (Capa 2 — escenarios sintéticos):
Parámetros usados: ['n_hold', 'cash_buffer', 'min_notional', '_param_bounds', 'rebalance_days', 'rv_lookback_days', 'target_annual_vol', 'max_gross_exposure', 'rebalance_tolerance']
Verifique que __init__ establezca todos los atributos a partir de self.parameters.get().
- steady_uptrend: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
- steady_downtrend: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
- flat_ranging: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
- volatility_spike: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
- zero_volume: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
- price_gap: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
Implementación
Cesta long-only de Binance Spot que mantiene los cinco nombres con menor volatilidad realizada de un universo fijo de 25 monedas, rebalanceada cada diez barras diarias y escalada a 15% de volatilidad anualizada con un límite bruto de 1.0x.
Resultados de verificación
Verificación fallida (Capa 2 — escenarios sintéticos):
Parámetros usados: ['n_hold', 'cash_buffer', 'min_notional', '_param_bounds', 'rebalance_days', 'rv_lookback_days', 'target_annual_vol', 'max_gross_exposure', 'rebalance_tolerance']
Verifique que __init__ establezca todos los atributos a partir de self.parameters.get().
- steady_uptrend: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
- steady_downtrend: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
- flat_ranging: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
- volatility_spike: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
- zero_volume: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
- price_gap: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (bar timestamp: 1735692060000)
Resumen de resultados
CryptoLowVolAnomalySpotBasketLongOnl-7a915cf1a2
Resumen de resultados
La estrategia probó una cesta long-only de Binance Spot que mantenía las cinco monedas de menor volatilidad de un universo de 25 monedas. Aunque la hipótesis reportó resultados históricos positivos, la verificación falló en todos los escenarios sintéticos debido a un TypeError de Decimal/float, por lo que la estrategia fue abandonada antes del backtesting o la optimización.
Resumen de resultados
Asegurar que la aritmética de Decimal y float se maneje de forma consistente antes de la verificación sintética.
Resumen de resultados
Fue abandonada tras tres iteraciones porque la verificación de la Capa 2 falló: los seis escenarios sintéticos generaron un TypeError de Decimal/float. No se alcanzaron las etapas posteriores.
Resumen de resultados
Intentó explotar una anomalía cripto de baja volatilidad manteniendo los cinco nombres con menor volatilidad realizada de un universo fijo de 25 monedas de Binance Spot.
Resumen de resultados
La hipótesis reportó un Sharpe neto de 0.74, un retorno anualizado de 13.4%, un drawdown máximo de -33.6%, 430 entradas y un retorno medio por operación de 1.37%. No se registró ningún informe de backtest ni de optimización del pipeline.
Iteration History
Verificación fallida (Capa 4 — revisión QA) [clase=code_defect]:
- [edge_concern] La economía del sandbox es saludable e internamente consistente, por lo que la rentabilidad NOT es la razón de este fallo. avg_trade_return_pct 3.73% x avg_position_pct 8.25% x 95 operaciones = ~29% del capital vs total_return 32.07% — el libro contable concuerda con la curva de capital (sin defecto contable), y 3.73%/operación supera el viaje de ida y vuelta de 0.20% BINANCE_SPOT por ~18x, con comisiones de solo 2.64% del bruto. Dos cosas que el analista debe tener en cuenta: (1) el Sharpe reportado de 0.547 no es internamente consistente con CAGR 19.26% en annualized_volatility 8.12% — la serie diaria de MTM se movió solo en 580 de 2401 días, mientras que el libro mantiene 5 posiciones spot de forma continua desde ~nov-2022, por lo que las cifras ajustadas por riesgo (Sharpe, Sortino, vol, VaR, Calmar) están diluidas/escasas respecto al libro contable realizado. Se indica el síntoma, sin afirmar la causa. (2) la atribución por régimen ya muestra calma +42.6% (Sharpe 1.40) frente a -4.1% normal y -3.7% en estrés, es decir, la prima de baja volatilidad aquí es un fenómeno de régimen de calma en esta muestra — pero esos terciles se calculan sobre una ventana que es mayoritariamente plana (ver el problema crítico), así que hay que reinterpretarlos una vez corregida la ventana, en lugar de actuar sobre ellos ahora.
- [critical] UNIVERSE MAKES 58% OF THE BACKTEST WINDOW STRUCTURALLY UNTRADEABLE, CORRUPTING EVERY RISK METRIC. La barrera de alineación entre patas de la plantilla base (base_template.py _maybe_process_primary + _sync_extra_iids) difiere la barra primaria hasta que EVERY pata adicional del mismo timeframe tenga una barra en esa marca temporal exacta, y esta estrategia además requiere una fila completa en _row_for(). Verifiqué las fechas de la primera barra en data/catalog para las 25 patas: los nombres vinculantes son APTUSDT.BINANCE_SPOT (primera barra 1-DAY 2022-10-20) y ICPUSDT.BINANCE_SPOT (2021-05-12). La pata primaria BTCUSDT.BINANCE_SPOT comienza 2017-08-18, y el backtest completo no pasa fecha de inicio (backtest_agent._run_backtest solo envía `end`), por lo que el motor se ejecuta 2017-08 -> 2026-09 (~9.1a) mientras que el libro no puede colocar su primera orden hasta ~2022-11-10 (APT listado + 21 filas alineadas de calentamiento). ~5.25 de 9.1 años (58%) se marcan como 0.00% días planos en la cuadrícula diaria. El sandbox ya demuestra esto exactamente: las ventanas de estrés covid_crash_2020, china_ban_2021, luna_collapse_2022 y rate_shock_2022 (ene-oct) devuelven todas precisamente 0.0000%, mientras que ftx_collapse_2022 (que comienza en 2022-11-06) es la primera distinta de cero, con -8.20%. Consecuencias, ninguna de las cuales puede deshacer el optimizador o el analista: (a) el objetivo de la Fase-2 es el Sharpe calculado sobre una serie que es mayoritariamente ceros — el sandbox reporta 0.547 mientras que el CAGR del mismo libro durante su período de ACTIVE es 19.26%; (b) aproximadamente cinco años calendario reportarán exactamente 0.00% y contarán como no positivos frente al umbral mínimo de validez de proporción de años positivos; (c) las propias afirmaciones preregistradas de la hipótesis — Sharpe +0.74 durante 2017-2026, 6 de 9 años positivos, 'sobrevive BOTH mercados bajistas completos' (2018 -19%, 2022 -26%) — no se pueden producir en absoluto, porque 2018 y los primeros diez meses de 2022 están dentro de la ventana muerta; (d) los terciles de régimen de volatilidad realizada se calculan sobre una serie cuya mayoría son días muertos. Este es un defecto de dimensionamiento/configuración con una solución obvia, no una afirmación sobre el edge: la lógica de trading en sí es correcta (ver notas). (línea 49) — solución: Elegir un universo cuyos miembros existan todos durante la ventana que se pretende medir, e indicar en la justificación de la configuración cuál es esa ventana. Fechas verificadas de primera barra en el catálogo para el 25 actual: APT 2022-10-20, ICP 2021-05-12, luego FIL 2020-10-16 / AAVE 2020-10-16 / NEAR 2020-10-15 / AVAX 2020-09-23 / UNI 2020-09-18 / DOT 2020-08-19 / SOL 2020-08-12, luego BCH 2019-11-29 / HBAR 2019-09-30 / DOGE 2019-07-06 / ALGO 2019-06-23 / ATOM 2019-04-30 / LINK 2019-01-17, luego VET 2018-07-26 / ETC 2018-06-13 / TRX 2018-06-12 / XLM 2018-06-01 / XRP 2018-05-05 / ADA 2018-04-18, y BTC/ETH 2017-08-18, BNB 2017-11-07,
- [warning] El dimensionamiento de compra en una rotación está limitado por `available = get_account_equity() * cash_buffer`, leído AFTER las ventas de salida y recorte que se envían en las líneas 279-298. En el backtest esto es correcto porque el SimulatedExchange de NT ejecuta las órdenes de mercado dentro de la misma iteración del motor, por lo que el saldo de USDT ya refleja las ventas. En paper/live no es así: las órdenes de mercado quedan pendientes hasta el siguiente tick, por lo que en una rotación que vende 2-3 nombres para financiar 2-3 nuevos, la estrategia verá el efectivo previo a la venta, escalará cada déficit por `ratio < 1`, y desplegará capital de forma insuficiente. Dado que las posiciones mantenidas solo se redimensionan cuando se desvían fuera de la banda de tolerancia de +/-25%, el déficit puede persistir hasta una rotación posterior. Divergencia backtest/paper, no un error de corrección del backtest. (línea 309) — solución: calcular `available` como (efectivo + nocional de las salidas/recortes recién enviados), o diferir la pata de compra de una rotación a la siguiente barra después de que se liquiden las ventas (una cola de compra diferida un bar, indexada por el período de rebalanceo). Si se deja tal cual, indicarlo explícitamente en la justificación para que la verificación de paridad con paper no interprete la divergencia como un defecto.
- [warning] Las interrupciones de datos de una sola pata detienen todo el libro. `_row_for()` devuelve None a menos que EVERY una de las 25 patas tenga una barra con exactamente la marca temporal de la barra primaria, y la barrera de la plantilla base bloquea de forma independiente la barra primaria en la misma situación. Una sola barra diaria faltante en cualquier nombre (brecha de recolección, suspensión del exchange) omite silenciosamente la fila de ese día, dejando un hueco en la ventana de log-retornos usada para la volatilidad realizada; un nombre que deja de publicar por completo (deslistado) congela la estrategia para siempre con posiciones abiertas. El comportamiento es conservador más que incorrecto, pero es invisible — nada registra ni cuenta los días omitidos. (línea 88) — solución: contar las filas omitidas (un entero `self._skipped_rows` se persiste automáticamente en la plantilla base) y registrar una advertencia cuando una pata esté ausente durante más de ~2 barras primarias consecutivas, de modo que un deslistado aparezca como un diagnóstico en lugar de un libro plano silencioso.
Iteration History
Verificación fallida (Capa 4 — revisión QA) [class=hypothesis_mismatch]:
- [edge_concern] El backtest de humo es rentable, pero el rendimiento no se usa como veredicto de corrección.
- [critical] La hipótesis requiere un universo fijo de 25 monedas de Binance Spot, pero la implementación configura solo 14 instrumentos. Por lo tanto, mantener 5 nombres selecciona 5/14 en lugar de 5/25, cambiando de forma sustancial la estrategia transversal. (línea 45) — solución: Restaurar el universo exacto de 25 nombres en extra_instruments y extra_bar_types, luego volver a ejecutar la verificación y el backtesting. Alternativamente, revisar formalmente la hipótesis a la variante de 14 nombres.
- [critical] La hipótesis permite una exposición bruta máxima de 1.0x, pero la implementación establece max_gross_exposure en 0.95 y lo limita en 0.95. (línea 29) — solución: Usar un máximo de 1.0 o modificar explícitamente la hipótesis para preregistrar el límite de 0.95.
Motivo de abandono
verification_loop: Verificación fallida (Capa 2 — escenarios sintéticos):
Parámetros usados: ['n_hold', 'cash_buffer', 'min_notional', '_param_bounds', 'rebalance_days', 'rv_lookback_days', 'target_annual_vol', 'max_gross_exposure', 'rebalance_tolerance']
Verificar que __init__ establezca todos los atributos desde self.parameters.get().
- steady_uptrend: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (marca temporal de la barra: 1735692060000)
- steady_downtrend: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (marca temporal de la barra: 1735692060000)
- flat_ranging: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (marca temporal de la barra: 1735692060000)
- volatility_spike: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (marca temporal de la barra: 1735692060000)
- zero_volume: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (marca temporal de la barra: 1735692060000)
- price_gap: TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (marca temporal de la barra: 1735692060000)
Stratmill es una herramienta de investigación y paper trading, no asesoramiento financiero ni un bróker. Los resultados de backtest y paper trading son hipotéticos. Operar conlleva riesgo de pérdida.