Power Law de Bitcoin: la ecuación que predice el precio con R² = 0.96
Cómo construí un dashboard interactivo que ajusta 15 años de precios de Bitcoin a una ley de potencia, descompone los ciclos con Fourier, y estima el próximo pico. Matemática real, sin astrología financiera.
R² = 0.9668
Eso es la bondad de ajuste de un modelo que predice el precio de Bitcoin usando una sola ecuación:
precio = coeficiente × días^exponente
Quince años de datos. Desde los 0.01 USD de 2009 hasta los picos de 69,000 USD. Una ley de potencia explica el 96.68% de la varianza del precio.
El modelo no predice mañana. Predice la trayectoria — un canal dentro del cual Bitcoin se mueve, con pisos y techos estadísticos que históricamente coinciden con los fondos y burbujas de cada ciclo.
Construí un dashboard interactivo para visualizar todo esto. Corre en el browser, no tiene backend, y los datos vienen de CoinGecko. Está en power-law-bitcoin.jiatech.com.ar.
Qué es una ley de potencia
Una ley de potencia describe una relación donde una cantidad varía como la potencia de otra. En escala log-log, una ley de potencia es una línea recta.
Bitcoin crece como una ley de potencia — no exponencial, no lineal. Cada orden de magnitud tarda más que el anterior. El salto de $1 a $10 fue rápido. De $10,000 a $100,000 tarda años.
La hipótesis viene del físico Giovanni Santostasi, que en 2014 observó que si graficás el precio de Bitcoin en escala log-log contra los días desde el bloque génesis, los puntos se alinean en una recta con una consistencia ridícula.
¿Por qué una ley de potencia? Las leyes de potencia aparecen en sistemas complejos con retroalimentación positiva: crecimiento de ciudades, distribución de terremotos, redes sociales. Bitcoin tiene un mecanismo de retroalimentación explícito: el halving reduce la emisión cada 210,000 bloques (~4 años), creando ciclos de escasez que alimentan demanda.
La regresión: OLS en espacio log-log
El modelo es una regresión lineal ordinaria, pero aplicada sobre los logaritmos:
log₁₀(precio) = log₁₀(coeficiente) + exponente × log₁₀(días)
Transformás una curva exponencial en una recta. Después ajustás por mínimos cuadrados y volvés al espacio original.
export function powerLawRegression(data: [number, number][]): PowerLawResult {
const points: [number, number][] = [];
for (const [ts, price] of data) {
const days = (ts - GENESIS_MS) / MS_PER_DAY;
if (days > 200 && price > 0) {
points.push([Math.log10(days), Math.log10(price)]);
}
}
const n = points.length;
let sx = 0, sy = 0, sxy = 0, sxx = 0;
for (const [x, y] of points) {
sx += x; sy += y; sxy += x * y; sxx += x * x;
}
const exponent = (n * sxy - sx * sy) / (n * sxx - sx * sx);
const intercept = (sy - exponent * sx) / n;
const coefficient = Math.pow(10, intercept);
// R² y sigma
const yMean = sy / n;
let ssTot = 0, ssRes = 0;
for (const [x, y] of points) {
const yHat = exponent * x + intercept;
ssTot += (y - yMean) ** 2;
ssRes += (y - yHat) ** 2;
}
return {
exponent,
coefficient,
rSquared: 1 - ssRes / ssTot,
sigma: Math.sqrt(ssRes / (n - 2)),
};
}
El filtro days > 200 descarta los primeros meses donde Bitcoin se intercambiaba entre un puñado de personas a precios de centavos. Esos datos son ruido de mercado inexistente.
El resultado: un exponente de ~5.6, un coeficiente de ~5.5×10⁻¹⁷, y un sigma de ~0.28. El sigma define las bandas de soporte (-2σ) y resistencia (+2σ) alrededor del valor justo.
Residuos y Z-Score: ¿está caro o barato?
El residuo mide cuánto se desvía el precio actual del valor justo que predice el modelo:
residuo = log₁₀(precio_real) - log₁₀(precio_modelo)
z_score = residuo / sigma
Un Z-Score de -0.92 significa que el precio está 0.92 desviaciones estándar por debajo del valor justo. Históricamente, el 83.5% de las veces fue más caro que ahora.
Los picos de burbuja coinciden con Z-Scores extremos. Noviembre 2021 (69K) fue un Z-Score de ~+1.5. Los fondos de bear market bajan de -1.5.
El percentil histórico complementa: te dice en qué posición relativa está el precio actual respecto a toda la historia del modelo. Si el percentil es 16.5%, quiere decir que el 83.5% de los días registrados, Bitcoin cotizó más caro en términos relativos al modelo.
Fourier: desarmando los ciclos
Los residuos no son ruido aleatorio. Tienen periodicidad. Si le aplicás una Transformada Rápida de Fourier (FFT), la señal se descompone en componentes sinusoidales con frecuencias, amplitudes y fases definidas.
La implementación usa el algoritmo Cooley-Tukey radix-2, que es la FFT clásica con permutación bit-reversal y operaciones butterfly:
export function fft(re: Float64Array, im: Float64Array): void {
const n = re.length;
// Bit-reversal permutation
for (let i = 1, j = 0; i < n; i++) {
let bit = n >> 1;
for (; j & bit; bit >>= 1) j ^= bit;
j ^= bit;
if (i < j) {
[re[i], re[j]] = [re[j], re[i]];
[im[i], im[j]] = [im[j], im[i]];
}
}
// Butterfly operations
for (let len = 2; len <= n; len *= 2) {
const angle = (-2 * Math.PI) / len;
const wRe = Math.cos(angle);
const wIm = Math.sin(angle);
for (let i = 0; i < n; i += len) {
let curRe = 1, curIm = 0;
for (let j = 0; j < len / 2; j++) {
const k = i + j + len / 2;
const tRe = curRe * re[k] - curIm * im[k];
const tIm = curRe * im[k] + curIm * re[k];
re[k] = re[i + j] - tRe;
im[k] = im[i + j] - tIm;
re[i + j] += tRe;
im[i + j] += tIm;
const newCurRe = curRe * wRe - curIm * wIm;
curIm = curRe * wIm + curIm * wRe;
curRe = newCurRe;
}
}
}
}
De todas las frecuencias que extrae la FFT, el sistema ordena por amplitud y toma las 8 más dominantes. La fundamental tiene un período de ~6.3 años. El primer armónico cae en ~4.7 años — sospechosamente cerca del ciclo del halving (~4 años).
Cada componente tiene frecuencia, amplitud y fase. Con esos tres parámetros podés reconstruir la señal original y — más interesante — extrapolar hacia el futuro:
export function forecastSignal(
frequencies: FrequencyComponent[],
startIndex: number,
forecastLength: number,
sampleIntervalDays: number,
activeIndices?: Set<number>
): number[] {
const signal = new Array<number>(forecastLength).fill(0);
for (const comp of frequencies) {
if (activeIndices && !activeIndices.has(comp.index)) continue;
for (let t = 0; t < forecastLength; t++) {
const days = (startIndex + t) * sampleIntervalDays;
signal[t] += comp.amplitude * Math.cos(
2 * Math.PI * comp.frequency * days + comp.phase
);
}
}
return signal;
}
El forecast extiende 730 días hacia adelante. El dashboard permite activar y desactivar armónicos individuales para ver cómo contribuye cada uno a la forma de la señal reconstruida.
Fases de mercado
Con la señal reconstruida y su derivada, el sistema clasifica automáticamente la fase del mercado:
| Fase | Señal | Derivada | Significado |
|---|---|---|---|
| Acumulación | < 0 | > 0 | Precio bajo, empezando a subir |
| Alcista | ≥ 0 | > 0 | Precio alto, sigue subiendo |
| Distribución | ≥ 0 | ≤ 0 | Precio alto, empezando a bajar |
| Bajista | < 0 | ≤ 0 | Precio bajo, sigue bajando |
export function detectPhase(signalValue: number, derivative: number): MarketPhase {
if (signalValue < 0 && derivative > 0) return 'acumulación';
if (signalValue >= 0 && derivative > 0) return 'alcista';
if (signalValue >= 0 && derivative <= 0) return 'distribución';
return 'bajista';
}
Es un clasificador de cuatro cuadrantes basado en posición y momentum de la señal Fourier. Nada sofisticado, pero la combinación con el Z-Score produce señales de trading sorprendentemente coherentes con la historia.
La señal de trading
Todo converge en una función que combina Z-Score y fase de mercado para producir COMPRA, VENTA o HOLD con un porcentaje de confianza:
export function computeSignal(zScore: number, phase: MarketPhase): TradingSignal {
if (zScore < -1.5)
return { type: 'COMPRA', confidence: 95,
reason: 'Z-Score extremadamente bajo: precio muy por debajo del valor justo' };
if (zScore > 1.5)
return { type: 'VENTA', confidence: 95,
reason: 'Z-Score extremadamente alto: precio muy por encima del valor justo' };
if (zScore < 0 && (phase === 'acumulación' || phase === 'alcista')) {
const conf = phase === 'acumulación' ? 70 : 60;
return { type: 'COMPRA', confidence: conf,
reason: `Precio bajo valor justo en fase ${phase}` };
}
// ... más reglas para VENTA y HOLD
return { type: 'HOLD', confidence: 50, reason: 'Sin señal clara' };
}
Los extremos (±1.5σ) dan 95% de confianza. Son eventos raros — las burbujas y los fondos absolutos de bear market. En el medio, la fase de mercado modula la confianza: un Z-Score negativo en fase de acumulación es más convincente que en fase de distribución.
El stack
React 19 + TypeScript + Vite 8
├── Canvas API → gráfico principal log-log (pixel-perfect)
├── Lightweight Charts 5 → residuos y Fourier (interactivo)
├── CoinGecko API → precios en vivo (cada 60s)
├── Datos históricos → bundleados desde enero 2009
└── Tailwind CSS → UI oscura estilo terminal
Decisiones de arquitectura que importan:
Canvas nativo para el gráfico principal. Lightweight Charts no soporta escala log-log doble. El gráfico de Power Law necesita mapear coordenadas en espacio logarítmico en ambos ejes, con zoom interactivo, tooltips y anotaciones de halvings y picos de burbuja. Canvas es la única opción que da el control necesario.
Lightweight Charts para los paneles secundarios. Residuos y Fourier son series temporales convencionales. Lightweight Charts maneja zoom, crosshair y responsividad gratis.
Sin backend. Toda la matemática corre en el browser. Los datos históricos (2009-hoy) están bundleados en un archivo de 1,431 líneas. Los últimos 365 días se refrescan desde CoinGecko cada 5 minutos. El precio actual se actualiza cada 60 segundos.
Un solo useMemo. Todo el análisis — regresión, residuos, FFT, reconstrucción, fases, señal, estimación de pico — se computa en un hook memorizado que depende de tres variables: datos, precio actual, y los armónicos activos. React no recalcula nada hasta que algo cambie.
Lo que el modelo dice hoy
El dashboard muestra en tiempo real:
- Señal: COMPRA 70% — precio bajo valor justo en fase de acumulación
- Precio actual: $66,677
- Valor justo: $128,647
- Z-Score: -0.92 — bajo valor justo
- Percentil histórico: 16.5% — el 83.5% de las veces fue más caro
- Próximo pico estimado: 31 de mayo 2027, ~$212,400
Estos números se actualizan cada vez que entra un precio nuevo de CoinGecko.
Lo que el modelo no puede decirte
Timing exacto. El forecast de Fourier estima ventanas de meses, no días. “Mayo 2027” puede ser marzo o septiembre.
Cisnes negros. Regulaciones, hackeos de exchanges, o una recesión global no están en los residuos históricos. El modelo asume que el futuro se parece al pasado — hasta que no se parece.
El fin del patrón. Una ley de potencia con R² = 0.96 sobre 15 años es impresionante, pero no hay garantía de que continúe. Bitcoin podría saturarse, la adopción podría frenarse, o un competidor podría cambiar la dinámica. El modelo describe lo que pasó, no promete lo que va a pasar.
Consejo financiero. Esto es una herramienta de análisis cuantitativo construida como proyecto personal. Las señales son el producto de una ecuación matemática, no la opinión de un analista certificado.
El punto
La matemática detrás de Bitcoin es más interesante que la especulación. Una regresión en log-log, una FFT sobre los residuos, y una clasificación de fases produce un modelo que explica 15 años de historia con un R² de 0.96 y estima ciclos futuros con un margen razonable.
Todo corre en el browser. ~200KB gzipped. Sin servidor, sin base de datos, sin suscripción.
La herramienta está abierta en power-law-bitcoin.jiatech.com.ar.