SPORTS BETTING MATH
ARTÍCULO DE INVESTIGACIÓN

Modelo Dixon-Coles Explicado: Corrección de Marcadores Bajos y Ponderación Temporal en Fútbol

Desglose matemático completo del ajuste bivariado de Dixon-Coles, matriz de corrección tau para marcadores bajos, estimación de parámetros con decaimiento temporal e ineficiencias del mercado.

22 min de lectura Avanzado Última actualización 2026-09-20
Ilustración del tema: Modelo Dixon-Coles Explicado: Corrección de Marcadores Bajos y Ponderación Temporal en Fútbol
Ilustración conceptual. Los supuestos numéricos y ejemplos se explican en el texto.

Lab de Modelado Estocástico SBM

Firma editorial: modelos de partidos y capital

Esta firma agrupa artículos sobre Poisson, Kelly y simulación. Es una etiqueta editorial, no prueba de un laboratorio con personal ni de cualificaciones verificadas.

Modelado Bivariado de Resultados por Distribución de Poisson Optimización de Crecimiento Geométrico por Criterio de Kelly Simulaciones de Monte Carlo para Riesgo de Ruina

1. Introducción: La Falla Estructural del Modelo de Poisson Independiente

En el modelado cuantitativo de fútbol profesional, el marco de Poisson independiente —desarrollado formalmente para el análisis deportivo por M. J. Maher en 1982— representa el punto de partida fundamental para calcular probabilidades de goles y matrices de marcadores exactos. La hipótesis axiomática de Maher establece que los goles convertidos por el equipo local (X) y el equipo visitante (Y) a lo largo de los 90 minutos reglamentarios constituyen dos procesos estocásticos de Poisson independientes, gobernados por las tasas de intensidad esperadas λ y μ:

P(X=x,Y=y)=P(X=x)⋅P(Y=y)=λxe−λx!⋅μye−μy!

La independencia es una hipótesis que debe evaluarse con un conjunto de datos definido. El estado del partido y la interacción táctica pueden crear dependencia, pero una corrección también puede ajustar ruido. La comparación debe distinguir cambios en la dependencia de cambios en las intensidades. Una diferencia entre modelos no establece un error de cuota ni el signo del valor esperado en una casa.

Con intensidades ilustrativas λ=1.45 y μ=1.10, Poisson independiente da P(0,0)≈7.808167% y P(1,1)≈12.454026%. Son valores calculados del modelo. Evaluar subestimación o sobrestimación exige frecuencias observadas y probabilidades de la misma muestra, con fechas, competiciones e incertidumbre indicadas. La comparación de entradas fijas siguiente aísla el efecto de la corrección de los marcadores bajos.

En su artículo canónico de 1997 publicado en el Journal of the Royal Statistical Society, los estadísticos Mark J. Dixon y Stuart G. Coles resolvieron definitivamente este problema: "Modelling Association Football Scores and Inefficiencies in the Football Betting Market". El modelo Dixon-Coles formuló un factor de dependencia de marcadores bajos τx,y(λ,μ,ρ) que ajusta de forma exacta las probabilidades de marcadores reducidos conservando la distribución marginal de Poisson, complementado por un factor de decaimiento temporal exponencial para capturar la evolución de los rendimientos de los clubes en el tiempo.

Este artículo deduce la corrección de cuatro celdas, conserva las marginales Poisson, define la verosimilitud ponderada en el tiempo y presenta un ejemplo Python con restricciones. La comparación numérica usa entradas fijas explícitas. El ajuste y las probabilidades pueden reproducirse por separado de afirmaciones sobre cuotas actuales o rentabilidad.

2. El Factor de Corrección Bivariada de Dixon-Coles

Rho es un parámetro de dependencia de los marcadores bajos, no la correlación de Pearson entre los goles. Su dominio depende de ambas intensidades. La implementación de verosimilitud exige todos los factores tau estrictamente positivos. Con lambda=10, mu=2 y rho=-0.35, tau(0,1)=-2.5; debe rechazarse todo el conjunto de parámetros, incluso al evaluar un marcador alto. Recortar la celda a cero y reescalar definiría otra distribución.

−min⁡(1/λ,1/μ)<ρ<min⁡(1,1/(λμ))

Para corregir la correlación en marcadores bajos sin abandonar las propiedades computacionales de Poisson, Dixon y Coles formularon la distribución bivariada corregida:

P(X=x,Y=y|λ,μ,ρ)=τx,y(λ,μ,ρ)⋅λxe−λx!⋅μye−μy!

El factor de ajuste τx,y(λ,μ,ρ) es un multiplicador escalar discreto aplicado a las cuatro combinaciones de marcadores reducidos {0,1}×{0,1}, gobernado por el parámetro de correlación ρ:

τx,y(λ,μ,ρ)={1−λμρpara x=0,y=01+λρpara x=0,y=11+μρpara x=1,y=01−ρpara x=1,y=11para x≥2 o y≥2

Demostración Matemática de la Conservación de la Masa de Probabilidad

Para asegurar la solidez axiomática del modelo, la matriz modificadora τx,y debe verificar estrictamente la condición de normalización total: ∑x=0∞∑y=0∞P(X=x,Y=y)=1.

Descomponemos la suma doble en la base no perturbada más la perturbación sobre los cuatro estados modificados:

∑x=0∞∑y=0∞P(X=x,Y=y)=∑x=0∞λxe−λx!∑y=0∞μye−μy!+Δ=1⋅1+Δ

Evaluamos analíticamente la perturbación Δ sobre los estados (0,0),(0,1),(1,0),(1,1):

Δ=(τ0,0−1)P0(λ)P0(μ)+(τ0,1−1)P0(λ)P1(μ)+(τ1,0−1)P1(λ)P0(μ)+(τ1,1−1)P1(λ)P1(μ)

Introduciendo las densidades P0(λ)=e−λ, P1(λ)=λe−λ, P0(μ)=e−μ, P1(μ)=μe−μ, y factorizando el exponente e−λ−μ:

Δ=e−λ−μ[(−λμρ)⋅(1)+(λρ)⋅(μ)+(μρ)⋅(λ)+(−ρ)⋅(λμ)]=e−λ−μρ[−λμ+λμ+λμ−λμ]=e−λ−μρ⋅(0)=𝟎

El término perturbativo se anula de forma exacta e idéntica. Por lo tanto, la probabilidad total acumulada suma siempre el 100% sin necesidad de renormalizaciones artificiales.

Interpretación Física del Parámetro de Correlación ρ

El ajuste siguiente elige una región de rho no positivo como ilustración, junto con las restricciones de positividad dependientes de las intensidades. Es una elección del modelo, no un signo universal inferido de datos de fútbol. La familia también puede admitir rho positivo: con lambda=1.65, mu=1.15 y rho=0.10, los cuatro factores son 0.81025, 1.165, 1.115 y 0.9, todos positivos.

−0.35≤ρ≤0,τx,y>0

En el dominio válido, rho negativo tiene los siguientes efectos algebraicos respecto a la independencia con las mismas intensidades:

  • Para el marcador (0,0): τ0,0=1−λμρ. Al ser ρ<0, el producto −λμρ>0, con lo cual τ0,0>1. La probabilidad del 0–0 se incrementa directamente.
  • Para el marcador (1,1): τ1,1=1−ρ. Como ρ<0, 1−ρ=1+|ρ|>1. La frecuencia de empates con un gol por bando aumenta de forma proporcional.
  • Para los marcadores (0,1) y (1,0): τ0,1=1+λρ<1 y τ1,0=1+μρ<1. Las victorias mínimas por 1–0 se corrigen a la baja, garantizando el equilibrio global.

3. Modelado de Fuerzas de Equipo: Ataque, Defensa y Ventaja Local

Las intensidades esperadas de goles λi,j (local i frente a visitante j) y μi,j (visitante j frente a local i) se parametrizan multiplicativamente:

λi,j=αi⋅βj⋅γ,μi,j=αj⋅βi

Donde:

  • αi>0: Coeficiente ofensivo (potencia de ataque) del equipo i. Un valor αi=1,25 implica una producción goleadora un 25% superior al promedio de la liga.
  • βj>0: Coeficiente defensivo (concesión de goles) del equipo j. Un valor βj=0,85 indica que el equipo recibe un 15% menos de goles que la media.
  • γ>0: Factor macro de ventaja de campo. En las ligas europeas de primer orden, γ oscila habitualmente entre 1,20 y 1,35.

La Restricción de Identificabilidad

Para evitar la indeterminación de escala entre parámetros de ataque y defensa, Dixon y Coles impusieron la restricción canónica sobre el conjunto de los N equipos participantes:

1N∑k=1Nαk=1,00

Fijar el ataque medio en uno elimina la simetría de escala entre ataque y defensa. No demuestra un óptimo global único de la verosimilitud, información suficiente en los enfrentamientos ni precisión predictiva. Los índices son relativos al modelo y a la muestra ajustados; la normalización por sí sola no hace directamente comparables índices de competiciones distintas.

4. Ponderación Temporal Exponencial: El Parámetro de Memoria ξ

Los resultados de hace dos temporadas no pueden tener la misma trascendencia analítica que los partidos disputados hace tres semanas. Dixon y Coles introdujeron una ponderación de decaimiento temporal continuo wk(ξ) para cada partido histórico k jugado en la fecha tk, respecto al día de análisis t:

wk(ξ)=exp⁡(−ξ⋅(t−tk))

Donde (t−tk) es el intervalo transcurrido en días y ξ>0 es el coeficiente de olvido estocástico.

La unidad temporal debe corresponder al coeficiente. Este ejemplo usa días transcurridos y una tasa ilustrativa ξ=0.0065 por día, con semivida aproximada de 106.638 días. Si las edades se expresan en semanas, la tasa equivalente es 7ξ=0.0455 por semana; mantener el mismo número cambiaría los pesos. Esta aritmética no identifica un óptimo empírico. Elija la tasa con datos cronológicos de validación separados e indique la métrica de evaluación.

t1/2=ln⁡2ξ,w(t)=e−ξt

5. Optimización por Máxima Verosimilitud (MLE)

Dado un conjunto histórico de M encuentros, la función de verosimilitud logarítmica ponderada se define como:

ln⁡L(Θ)=∑k=1Mwk(ξ)⋅ln⁡[τxk,yk(λk,μk,ρ)⋅λkxke−λkxk!⋅μkyke−μkyk!]

Omitiendo los factores factoriales constantes, el algoritmo minimiza la log-verosimilitud negativa ponderada:

minΘ⁡𝒩ℒℒ(Θ)=−∑k=1Mwk(ξ)[ln⁡τxk,yk(λk,μk,ρ)+xkln⁡λk−λk+ykln⁡μk−μk]

El ejemplo usa SLSQP para tratar la igualdad de escala del ataque y las desigualdades no lineales de tau. Comprueba el éxito del optimizador y los márgenes de viabilidad antes de guardar los parámetros. Una terminación numérica satisfactoria no certifica un óptimo global ni el rendimiento en partidos futuros.

6. Comparación Numérica con Intensidades Fijas

Este cálculo usa parámetros fijos λ=1.65, μ=1.15 y ρ=−0.12. Las probabilidades se suman hasta 45 goles. Las cuotas son los inversos de las probabilidades de cada modelo. El ejemplo es reproducible sin precios de cierre observados. Una diferencia entre dos modelos no establece una ventaja frente al mercado.

ResultadoProbabilidad independienteCuota del modelo independienteProbabilidad Dixon-ColesCuota del modelo Dixon-ColesCambio, puntos porcentuales
0–06.081006%16.44467.465651%13.3947+1.384645
1–111.538709%8.666512.923355%7.7379+1.384645
1–010.033660%9.96658.649015%11.5620-1.384645
0–16.993157%14.29975.608512%17.8300-1.384645
Victoria local49.026329%2.039747.641684%2.0990-1.384645
Empate24.395242%4.099227.164533%3.6813+2.769290
Victoria visitante26.578428%3.762425.193783%3.9692-1.384645
Menos de 2.546.945368%2.130146.945368%2.1301+0.000000

Under 2.5 contiene las cuatro celdas corregidas, cuyos cambios se cancelan. Su probabilidad es del 46.945368% en ambos modelos, con cuota aproximada de 2.1301. Over 2.5 tampoco cambia. La dependencia puede alterar un total que contenga solo parte de esas celdas, como Under 1.5, además de los empates y ambos marcan. Reestimar las intensidades puede cambiar Under 2.5 a través de ellas. Es un efecto distinto de aplicar rho manteniéndolas fijas.

A=−ρλμe−(λ+μ),ΔP00=ΔP11=A,ΔP01=ΔP10=−A ΔP(Under 2.5)=A−A−A+A=0

7. Implementación en Python: DixonColesModel

El ejemplo restringe los cuatro factores de marcadores bajos para cada par ordenado de equipos conocidos, fija el ataque medio en uno y comprueba éxito y viabilidad del optimizador. Las probabilidades inválidas se rechazan, no se recortan. La cuadrícula predeterminada usa cuantiles Poisson para dejar masa omitida inferior a aproximadamente 2e-12, informa esa masa y conserva las probabilidades sin renormalización. Xi es una tasa diaria ilustrativa; elegirla requiere validación separada.

import numpy as np
from scipy.optimize import minimize
from scipy.stats import poisson


class DixonColesModel:
    def __init__(self, xi=0.0065):
        # Illustrative daily decay rate, not a validated universal optimum.
        if not np.isfinite(xi) or xi < 0:
            raise ValueError("xi must be a finite nonnegative daily rate")
        self.xi = xi
        self.teams = []
        self.team_indices = {}
        self.params = None
        self.gamma = 1.25
        self.rho = -0.11

    @staticmethod
    def tau(x, y, lambda_, mu, rho):
        # Check the whole joint model, including unobserved low-score cells.
        if not np.all(np.isfinite([lambda_, mu, rho])) or min(lambda_, mu) <= 0:
            raise ValueError("Intensities must be finite and positive")
        factors = [1 - lambda_ * mu * rho, 1 + lambda_ * rho,
                   1 + mu * rho, 1 - rho]
        if min(factors) <= 0:
            raise ValueError("rho is outside the strictly positive tau domain")
        return {(0, 0): factors[0], (0, 1): factors[1],
                (1, 0): factors[2], (1, 1): factors[3]}.get((x, y), 1.0)

    def bivariate_prob(self, x, y, lambda_, mu, rho):
        if not np.all(np.isfinite([x, y])) or min(x, y) < 0 or int(x) != x or int(y) != y:
            raise ValueError("Goals must be nonnegative integers")
        factor = self.tau(x, y, lambda_, mu, rho)
        return factor * poisson.pmf(x, lambda_) * poisson.pmf(y, mu)

    def _unpack_params(self, params, n_teams):
        return (params[:n_teams], params[n_teams:2*n_teams],
                params[2*n_teams], params[2*n_teams + 1])

    def fit(self, matches):
        self.params = None
        if not matches:
            raise ValueError("At least one match is required")
        for m in matches:
            goals = [m['home_goals'], m['away_goals']]
            age = m.get('days_ago', 0.0)
            if (m['home'] == m['away'] or not np.all(np.isfinite(goals + [age]))
                    or min(goals) < 0 or any(int(v) != v for v in goals) or age < 0):
                raise ValueError("Invalid teams, goals or match age")
        self.teams = sorted({m['home'] for m in matches} | {m['away'] for m in matches})
        self.team_indices = {team: i for i, team in enumerate(self.teams)}
        n_teams = len(self.teams)
        init = np.concatenate([np.ones(n_teams), np.ones(n_teams), [1.25, -0.10]])

        def tau_constraints(params):
            alpha, beta, gamma, rho = self._unpack_params(params, n_teams)
            values = []
            # Every ordered pairing of known teams must define a valid model.
            for h in range(n_teams):
                for a in range(n_teams):
                    if h == a:
                        continue
                    lam, mu = alpha[h] * beta[a] * gamma, alpha[a] * beta[h]
                    values.extend([1 - lam * mu * rho, 1 + lam * rho,
                                   1 + mu * rho, 1 - rho])
            return np.asarray(values) - 1e-8

        def loss_fn(params):
            alpha, beta, gamma, rho = self._unpack_params(params, n_teams)
            nll = 0.0
            for m in matches:
                h, a = self.team_indices[m['home']], self.team_indices[m['away']]
                lam, mu = alpha[h] * beta[a] * gamma, alpha[a] * beta[h]
                try:
                    factor = self.tau(m['home_goals'], m['away_goals'], lam, mu, rho)
                except ValueError:
                    return 1e12  # Reject an infeasible optimizer trial, never clip a probability.
                weight = np.exp(-self.xi * m.get('days_ago', 0.0))
                log_prob = (np.log(factor) + poisson.logpmf(m['home_goals'], lam)
                            + poisson.logpmf(m['away_goals'], mu))
                nll -= weight * log_prob
            return float(nll)

        constraints = [
            {'type': 'ineq', 'fun': tau_constraints},
            {'type': 'eq', 'fun': lambda p: np.mean(p[:n_teams]) - 1.0},
        ]
        bounds = [(0.05, 3.5)] * (2*n_teams) + [(1.0, 1.8), (-0.35, 0.0)]
        res = minimize(loss_fn, init, method='SLSQP', bounds=bounds,
                       constraints=constraints, options={'maxiter': 1000, 'ftol': 1e-9})
        if (not res.success or not np.all(np.isfinite(res.x)) or not np.isfinite(res.fun)
                or np.min(tau_constraints(res.x)) < -1e-9
                or abs(np.mean(res.x[:n_teams]) - 1) > 1e-8):
            raise RuntimeError(f"No valid converged fit: {res.message}")
        self.params = res.x
        _, _, self.gamma, self.rho = self._unpack_params(res.x, n_teams)
        return res

    def predict_score_matrix(self, home_team, away_team, max_goals=None):
        if self.params is None:
            raise ValueError("Fit the model first")
        alpha, beta, gamma, rho = self._unpack_params(self.params, len(self.teams))
        h, a = self.team_indices[home_team], self.team_indices[away_team]
        lam, mu = alpha[h] * beta[a] * gamma, alpha[a] * beta[h]
        self.tau(0, 0, lam, mu, rho)
        if max_goals is None:
            max_goals = int(max(poisson.ppf(1 - 1e-12, lam), poisson.ppf(1 - 1e-12, mu)))
        if not isinstance(max_goals, (int, np.integer)) or max_goals < 1:
            raise ValueError("max_goals must be an integer of at least one")
        goals = np.arange(max_goals + 1)
        matrix = np.outer(poisson.pmf(goals, lam), poisson.pmf(goals, mu))
        for x, y in [(0, 0), (0, 1), (1, 0), (1, 1)]:
            matrix[x, y] *= self.tau(x, y, lam, mu, rho)
        # Keep the original probability mass; disclose the omitted grid tail.
        self.omitted_mass = float(max(0.0, 1 - matrix.sum()))
        return matrix, lam, mu

    def predict_1x2(self, home_team, away_team):
        matrix, lam, mu = self.predict_score_matrix(home_team, away_team)
        home, draw, away = np.tril(matrix, -1).sum(), np.trace(matrix), np.triu(matrix, 1).sum()
        return {'home_win': float(home), 'draw': float(draw), 'away_win': float(away),
                'fair_odds_home': float(1/home), 'fair_odds_draw': float(1/draw),
                'fair_odds_away': float(1/away), 'lambda': float(lam), 'mu': float(mu),
                'omitted_probability_mass': self.omitted_mass}

8. Conclusiones Cuantitativas

La formulación de Dixon-Coles constituye una piedra angular indispensable en las apuestas de valor y el arbitraje estadístico de fútbol. Eliminar la suposición errónea de independencia en tanteos cerrados permite descubrir ineficiencias monetizables sistemáticas en los mercados de cuotas de partido, hándicaps asiáticos y totales de goles.

Preguntas Frecuentes

INVESTIGACIÓN RELACIONADA

Estudios con Referencia Cruzada

Compara el mismo mercado

Enlaces de socios

1win Sports: consulta evento, cuotas y reglas de liquidación

Qué incluye el precio → Visitar 1win Sports 18+ · Condiciones en el sitio

Stake Sportsbook: compara el mismo evento, mercado y reglas

Qué incluye el precio → Visitar Stake 18+ · Condiciones en el sitio

Consulta la cuota actual, el mercado exacto, las reglas y la disponibilidad en tu país. El resultado depende de los supuestos y no confirma una oportunidad en vivo.