Compartir a través de



Junio de 2016

Volumen 31, número 6

Ejecución de pruebas: Introducción a los mercados de predicción

Por James McCaffrey

James McCaffreySuponga que quiere predecir el resultado de un partido próximo de fútbol americano entre los equipos Xrays y Yanks. Encuentra un grupo de 20 expertos en fútbol americano y le da a cada uno de ellos 500 $ en tokens. Los expertos pueden comprar y vender acciones de cada uno de los dos equipos, de forma similar a cómo funciona la bolsa.

Cuando un experto compra acciones de un equipo, por ejemplo los Xrays, el precio de una acción de ese equipo se incrementa y el precio de una acción del otro equipo disminuye. Con el tiempo, los expertos comprarán y venderán acciones de los dos equipos hasta que los precios se estabilicen; entonces, podrá inferir la probabilidad de que cada uno de los equipos gane.

Las transacciones se detienen el día anterior al partido. Cuando el partido termina y se ha determinado el ganador, paga a los expertos que tienen acciones en el equipo vencedor según el último precio del equipo cuando se cerraron las transacciones. Como los expertos saben que se les pagará, tienen la motivación de dar sus opiniones honestas durante las transacciones.

Lo que hemos descrito se conoce como un mercado de predicción. En este artículo, describiré las matemáticas detrás de los mercados de predicción y mostraré cómo implementar las funciones clave en el código. No es probable que deba crear un mercado de predicción en su trabajo diario, pero creo que estas ideas le resultarán muy interesantes. Además, algunas de las técnicas de programación que se explican en este artículo se pueden usar en escenarios de desarrollo más habituales.

En este artículo se supone que tiene conocimientos sobre programación como mínimo de nivel principiante, pero no se presupone que conozca nada sobre mercados de predicción. Se presenta un programa de demostración completo y puede obtener el código fuente con la descarga que se incluye con el artículo. En la demostración se utiliza C#, pero no debería tener problemas para refactorizar el código a otro lenguaje si lo desea.

Tenga en cuenta que se trata de una introducción informal a los mercados de predicción, pensada principalmente para desarrolladores de software. Me he tomado algunas libertades en cuanto a la terminología y las definiciones para que las ideas principales queden lo más claras posibles.

Un ejemplo

Los mercados de predicción quizá se explican mejor con un ejemplo concreto. Eche un vistazo al programa de demostración de la Figura 1. Después de unos mensajes preliminares, la salida de la demostración comienza así:

Setting liquidity parameter = 100.0

Initial number of shares owned of teams [0] and [1] are:
0 0

Initial inferred probabilities of winning are:
 0.5000  0.5000

Una demostración de mercado de predicción
Figura 1. Una demostración de mercado de predicción

El parámetro liquidity (liquidez) se explicará en detalle más adelante, pero por ahora es suficiente saber que liquidity controla en qué proporción reaccionan los precios del mercado a las compras y ventas. A mayores valores de liquidity, menores serán los cambios en los precios.

Inicialmente, los expertos no poseen acciones. Como el número de acciones que se poseen de cada equipo es el mismo (cero), es lógico que la probabilidad inferida inicial de que un equipo gane sea 0,50.

La siguiente parte de la salida de la demostración es:

Current costs for one share of each team are:
 $0.5012  $0.5012

En cualquier momento, una acción de cada equipo tiene un precio determinado. Los expertos necesitan conocer este precio ya que están jugando con dinero real. Como las probabilidades iniciales de ganar son idénticas, es razonable que los precios de una acción de cada equipo también sean idénticos.

La siguiente parte de la salida de la demostración es:

Update: expert [01] buys 20 shares of team [0]

Cost of transaction to expert was: $10.50

El experto número 1 cree que el equipo 0, los Xrays, ganará y compra 20 acciones del equipo 0. El costo para el experto es de 10,50 $. Observe que el precio de 20 acciones (10,50 $) no es el mismo que 20 veces el precio de una única acción (20 * 0,5012 $ = 10,02 $). A medida que se compra cada acción, el precio de una acción adicional del equipo se incrementa. La siguiente parte de la salida de la demostración es:

New number of shares owned of teams [0] and [1] are:
20 0

New inferred probabilities of winning are:
 0.5498  0.4502

La demostración muestra el número actualizado de acciones en circulación de cada equipo, (x, y) = (20, 0) y calcula y muestra las probabilidades inferidas actualizadas de que cada equipo gane (0,55; 0,45). Como los expertos han comprado más acciones del equipo 0 que del equipo 1, la probabilidad inferida de que el equipo 0 gane debe ser mayor que la del equipo 1. El cálculo de las probabilidades se explicará más adelante.

A continuación, la demostración muestra:

Current costs for one share of each team are:
 $0.5511  $0.4514

Update: expert [02] buys 20 shares of team [1]

Cost of transaction to expert was: $9.50

El nuevo costo por acción de cada equipo se calcula y se muestra. Observe que el precio de una acción del equipo 0 (0,55 $) ahora es bastante más caro que el de una del equipo 1 (0,45 $). Esto da a los expertos un incentivo para comprar acciones del equipo 1 si piensan que están a buen precio en relación con la probabilidad de que el equipo 1 gane. En este caso, la demostración simula que el experto número 2 compra 20 acciones del equipo 1 con un costo de 9,50 $. A continuación:

New number of shares owned of teams [0] and [1] are:
20 20

New inferred probabilities of winning are:
 0.5000  0.5000

Ahora hay 20 acciones en circulación de cada equipo, por lo que las probabilidades inferidas de que cada equipo gane vuelven a 0,50 y 0,50.

La siguiente parte de la salida de la demostración es:

Current costs for one share of each team are:
 $0.5012  $0.5012

Update: expert [03] buys 60 shares of team [0]
Cost of transaction to expert was: $34.43

New number of shares owned of teams [0] and [1] are:
80 20

New inferred probabilities of winning are:
 0.6457  0.3543

El experto 3 está convencido de que el equipo 0 ganará, por lo que compra 60 acciones del equipo 0 con un costo de 34,43 $. Esta transacción cambia el número de acciones en circulación a (80, 20) y provoca que las nuevas probabilidades inferidas de victoria se muevan notablemente a favor del equipo 0 (0,65; 0,35).

A continuación, el experto número 1 ve que el valor de sus acciones del equipo 0 ha aumentado enormemente hasta aproximadamente 0,6468 $ por acción.

Current costs for one share of each team are:
 $0.6468  $0.3555

Update: expert [01] sells 10 shares of team [0]
Cost of transaction to expert was: $-6.34

New number of shares owned of teams [0] and [1] are:
70 20

New inferred probabilities of winning are:
 0.6225  0.3775

El experto número 1 considera que el equipo 0 está ahora algo sobrevalorado en relación a sus probabilidades de victoria y vende 10 de sus 20 acciones, tras lo que obtiene 6,34 $ (que se indica con el símbolo negativo). Las nuevas probabilidades inferidas se ajustan de forma que quedan algo más parejas, pero aún se predice que el equipo 0 ganará con una probabilidad de 0,63.

La demostración finaliza con el cierre de las transacciones. Las probabilidades finales son el objetivo del mercado de predicción. Después de que se dispute el partido entre los Xrays y los Yanks, se pagará a los expertos por las acciones que tengan del equipo ganador, según el precio de acción final del equipo vencedor. Los pagos animan a los expertos a que den sus opiniones honestas.

Las cuatro ecuaciones clave de los mercados de predicción

Un mercado de predicción básico utiliza cuatro ecuaciones matemáticas, como se muestra en la Figura 2. No se asuste, las ecuaciones no son tan complicadas como podría parecer a primera vista. Existen varios modelos matemáticos que se pueden usar para definir un mercado de predicción. El modelo que se describe en este artículo se basa en lo que se conoce como Logarithmic Market Scoring Rule (regla logarítmica de puntuación de mercado, LMSR).

Las cuatro ecuaciones clave de los mercados de predicción
Figura 2. Las cuatro ecuaciones clave de los mercados de predicción

La ecuación 1 es la función de costo asociada con un conjunto de acciones en circulación (x, y). La ecuación, que no es nada clara, viene de la teoría económica. Desde el punto de vista de un desarrollador, puede considerar que la ecuación es una función auxiliar. Toma 'x', que es el número de acciones que se poseen de la opción 0, e 'y', que es el número de acciones que se poseen de la opción 1, y devuelve un valor. La variable 'b' de las cuatro ecuaciones es el parámetro liquidity. Suponga que x = 20 e y = 10. Si b = 100,0, entonces C(x,y) = 100,0 * ln(exp(20/100) + exp(10/100)) = 100,0 * ln(1,22 + 1,11) = 100,0 * 0,8444 = 84,44 $. El valor devuelto se usa en la ecuación 2.

La ecuación 2 es el costo de una transacción para un comprador. Suponga que un conjunto de acciones en circulación actualmente es (20, 10) y un experto compra 30 acciones de la opción 0. El costo de esa transacción para el experto se calcula mediante la ecuación 2 como C(20+30, 10) - C(20, 10) = C(50, 10) - C(20, 10) = 101,30 - 84,44 = 16,86 $. Si un experto vende acciones, el costo de la transacción será un valor negativo que indica que se paga al experto.

Técnicamente, la ecuación 3 es el precio marginal de la opción 0 basado en un conjunto de acciones en circulación (x, y). Pero un precio marginal se puede interpretar aproximadamente como la probabilidad de que una opción gane. La ecuación 4 es el precio marginal (la probabilidad) de la opción 1. Si observa las dos ecuaciones con detenimiento, se dará cuenta de que su suma debe ser 1,0, como se requiere en un conjunto de probabilidades.

La implementación de las cuatro ecuaciones clave de los mercados de predicción es sencilla. El programa de demostración implementa el costo (la ecuación 1) como sigue:

static double Cost(int[] outstanding, double liq)
{
  double sum = 0.0;
  for (int i = 0; i < 2; ++i)
    sum += Math.Exp(outstanding[i] / liq);
  return liq * Math.Log(sum);
}

El método Cost es prácticamente una traducción exacta de la ecuación 1. Observe que el método Cost presupone que solo existen dos opciones. Por simplicidad, no se realizan comprobaciones de errores.

La ecuación 2 también es bastante sencilla de implementar:

static double CostOfTrans(int[] outstanding, int idx, int nShares, double liq)
{
  int[] after = new int[2];
  Array.Copy(outstanding, after, 2);
  after[idx] += nShares;
  return Cost(after, liq) - Cost(outstanding, liq);
}

La matriz denominada after contiene el nuevo número de acciones en circulación después de una transacción, y entonces el método simplemente llama al método auxiliar Cost dos veces. Con un método para calcular el costo de una transacción a nuestra disposición, es fácil escribir un método que calcule el costo de compra de una única acción de cada una de las dos opciones:

static double[] CostForOneShare(int[] outstanding, double liq)
{
  double[] result = new double[2];
  result[0] = CostOfTrans(outstanding, 0, 1, liq);
  result[1] = CostOfTrans(outstanding, 1, 1, liq);
  return result;
}

Los expertos pueden usar el costo de una única acción para obtener una aproximación de cuánto costaría comprar n acciones de una opción.

El método Probabilities devuelve los dos precios marginales (las probabilidades inferidas) de la victoria de cada opción en una matriz:

static double[] Probabilities(int[] outstanding, double liq)
{
  double[] result = new double[2];
  double denom = 0.0;
  for (int i = 0; i < 2; ++i)
    denom += Math.Exp(outstanding[i] / liq);
  for (int i = 0; i < 2; ++i)
    result[i] = Math.Exp(outstanding[i] / liq) / denom;
  return result;
}

Si compara el código del método Probabilities con las ecuaciones 3 y 4, verá que, de nuevo, el código surge directamente de la definición matemática.

El programa de demostración

Para crear el programa de demostración, inicié Visual Studio y seleccioné la plantilla de programa de aplicación de consola C#. Asigné como nombre del proyecto PredictionMarket. La demostración no tiene dependencias significativas de Microsoft .NET Framework, por lo que funcionará con cualquier versión de Visual Studio.

Cuando se cargó el código de la plantilla, en la ventana del Explorador de soluciones cambié el nombre del archivo Program.cs por PredictionMarketProgram.cs (que es más descriptivo) y permití que Visual Studio cambiara el nombre de la clase Program automáticamente. En la parte superior del código fuente, eliminé todas las instrucciones using que hacían referencia a espacios de nombres de .NET innecesarios y dejé solo la referencia al espacio de nombres System de nivel superior.

El código de demostración completo, con unos cambios menores y algunas instrucciones WriteLine eliminadas para guardar espacio, se presenta en la Figura 3. Toda la lógica de control del programa está en el método Main. Toda la funcionalidad del mercado de predicción se encuentra en cuatro métodos estáticos y existen dos métodos de visualización auxiliares ShowVector.

Figura 3. Demostración del mercado de predicción

using System;
namespace PredictionMarket
{
  class PredictionMarketProgram
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Begin prediction market demo ");
      Console.WriteLine("Goal is to predict winner of Xrays");
      Console.WriteLine("vs. Yanks using expert opinions");
      double liq = 100.0;
      Console.WriteLine("Setting liquidity parameter = " +
        liq.ToString("F1"));
      int[] outstanding = new int[] { 0, 0 };
      Console.WriteLine("Initial number of shares owned are:");
      ShowVector(outstanding);
      double[] probs = Probabilities(outstanding, liq);
      Console.WriteLine("Initial probabilities of winning:");
      ShowVector(probs, 4, " ");
      Console.WriteLine("=================================");
      double[] costPerShare = CostForOneShare(outstanding, liq);
      Console.WriteLine("Current costs for one share are: ");
      ShowVector(costPerShare, 4, " $");
      Console.WriteLine("Update: expert [01] buys 20 shares " +
        "of team [0]");
      double costTrans = CostOfTrans(outstanding, 0, 20, liq);
      Console.WriteLine("Cost of transaction to expert was: $" +
        costTrans.ToString("F2"));
      outstanding = new int[] { 20, 0 };
      Console.WriteLine("New number of shares owned are: ");
      ShowVector(outstanding);
      probs = Probabilities(outstanding, liq);
      Console.WriteLine("New inferred probs of winning:");
      ShowVector(probs, 4, " ");
      Console.WriteLine("=================================");
      costPerShare = CostForOneShare(outstanding, liq);
      Console.WriteLine("Current costs for one share are:");
      ShowVector(costPerShare, 4, " $");
      Console.WriteLine("Update: expert [02] buys 20 shares " +
        "of team [1]");
      costTrans = CostOfTrans(outstanding, 1, 20, liq);
      Console.WriteLine("Cost of transaction to expert was: $" +
        costTrans.ToString("F2"));
      outstanding = new int[] { 20, 20 };
      Console.WriteLine("New number of shares owned are:");
      ShowVector(outstanding);
      probs = Probabilities(outstanding, liq);
      Console.WriteLine("New inferred probs of winning:");
      ShowVector(probs, 4, " ");
      Console.WriteLine("=================================");
      costPerShare = CostForOneShare(outstanding, liq);
      Console.WriteLine("Current costs for one share are:");
      ShowVector(costPerShare, 4, " $");
      Console.WriteLine("Update: expert [03] buys 60 shares " +
        "of team [0]");
      costTrans = CostOfTrans(outstanding, 0, 60, liq);
      Console.WriteLine("Cost of transaction to expert was: $" +
        costTrans.ToString("F2"));
      outstanding = new int[] { 80, 20 };
      Console.WriteLine("New number of shares owned are:");
      ShowVector(outstanding);
      probs = Probabilities(outstanding, liq);
      Console.WriteLine("New inferred probs of winning:");
      ShowVector(probs, 4, " ");
      Console.WriteLine("=================================");
      costPerShare = CostForOneShare(outstanding, liq);
      Console.WriteLine("Current costs for one share are: ");
      ShowVector(costPerShare, 4, " $");
      Console.WriteLine("Update: expert [01] sells 10 shares " +
        "of team [0]");
      costTrans = CostOfTrans(outstanding, 0, -10, liq);
      Console.WriteLine("Cost of transaction to expert was: $" +
        costTrans.ToString("F2"));
      outstanding = new int[] { 70, 20 };
      Console.WriteLine("New number of shares owned are:");
      ShowVector(outstanding);
      probs = Probabilities(outstanding, liq);
      Console.WriteLine("New inferred probs of winning:");
      ShowVector(probs, 4, " ");
      Console.WriteLine("=================================");
      Console.WriteLine("Update: Market Closed");
      Console.WriteLine("\nEnd prediction market demo \n");
      Console.ReadLine();
    } // Main()
    static double[]Probabilities(int[] outstanding,
      double liq)
    {
      double[] result = new double[2];
      double denom = 0.0;
      for (int i = 0; i < 2; ++i)
        denom += Math.Exp(outstanding[i] / liq);
      for (int i = 0; i < 2; ++i)
        result[i] = Math.Exp(outstanding[i] / liq) / denom;
      return result;
    }
    static double Cost(int[] outstanding, double liq)
    {
      double sum = 0.0;
      for (int i = 0; i < 2; ++i)
        sum += Math.Exp(outstanding[i] / liq);
      return liq * Math.Log(sum);
    }
    static double CostOfTrans(int[] outstanding, int idx,
      int nShares, double liq)
    {
      int[] after = new int[2];
      Array.Copy(outstanding, after, 2);
      after[idx] += nShares;
      return Cost(after, liq) - Cost(outstanding, liq);
    }
    static double[] CostForOneShare(int[] outstanding,
      double liq)
    {
      double[] result = new double[2];
      result[0] = CostOfTrans(outstanding, 0, 1, liq);
      result[1] = CostOfTrans(outstanding, 1, 1, liq);
      return result;
    }
    static void ShowVector(double[] vector, int dec, string pre)
    {
      for (int i = 0; i < vector.Length; ++i)
        Console.Write(pre + vector[i].ToString("F" + dec) + " ");
      Console.WriteLine("\n");
    }
    static void ShowVector(int[] vector)
    {
      for (int i = 0; i < vector.Length; ++i)
        Console.Write(vector[i] + " ");
      Console.WriteLine("\n");
    }
  } // Program class
} // ns

Después de mostrar algunos mensajes preliminares, la ejecución del programa en el método Main comienza así:

double liq = 100.0;
int[] outstanding = new int[] { 0, 0 };
ShowVector(outstanding);

La variable liq es el parámetro liquidity. Un valor de 100,0 es normal, pero si experimenta ajustando el valor, verá cómo afecta al cambio en los precios de las acciones después de una transacción. Cuanto mayor sea el valor de liquidity, menores serán los cambios. La matriz denominada outstanding contiene el número total de acciones que poseen todos los expertos, en cada uno de los dos equipos. Observe que el parámetro liquidity debe pasarse a los cuatro métodos estáticos de predicción de mercados. Un diseño alternativo consiste en encapsular los métodos en una clase de C# y definir liquidity como un campo de miembro.

A continuación, el número de acciones en circulación se usa para determinar las probabilidades inferidas de victoria de cada equipo:

double[] probs = Probabilities(outstanding, liq);
Console.WriteLine("Initial probabilities of winning:");
ShowVector(probs, 4, " ");

Después, la demostración muestra los costos de compra de una única acción de cada uno de los dos equipos:

double[] costPerShare = CostForOneShare(outstanding, liq);
Console.WriteLine("Current costs for one share are: ");
ShowVector(costPerShare, 4, " $");

En un mercado de predicción realista, esta información resultaría útil como ayuda para que los expertos de mercado evalúen si el precio de una acción de un equipo es demasiado alto o demasiado bajo en relación a la percepción del experto sobre si el equipo ganará.

El programa de demostración simula que uno de los expertos compra algunas acciones, así:

Console.WriteLine("Update: expert [01] buys 20 shares of team [0]");
double costTrans = CostOfTrans(outstanding, 0, 20, liq);
Console.WriteLine("Cost of transaction to expert was: $" +
  costTrans.ToString("F2"));

En un mercado de predicción real, el sistema debería mantener una gran cantidad de información sobre los saldos de las cuentas de los expertos y el número de acciones que poseen.

A continuación, el número de acciones en circulación se actualiza, de la siguiente manera:

outstanding = new int[] { 20, 0 };
Console.WriteLine("New number of shares owned on teams [0] " +
  "and [1] are: ");
ShowVector(outstanding);

Si consulta más atrás las ecuaciones matemáticas de la Figura 2, observará que el número de acciones en circulación de cada equipo u opción, (x, y), es necesario para todas las ecuaciones.

Cuando se haya actualizado el número de acciones en circulación, la información se usa para estimar las probabilidades revisadas de victoria de cada equipo u opción:

probs = Probabilities(outstanding, liq);
Console.WriteLine("New inferred probabilities of
  winning are: ");
ShowVector(probs, 4, " ");

Recuerde que estos valores son en realidad precios marginales, pero resulta útil considerarlos probabilidades. Por último, el objetivo de un mercado de predicción es generar la probabilidad de victoria de cada uno de los equipos u opciones, de forma que lo que se está buscando es el conjunto final de probabilidades cuando el mercado se estabilice.

El programa de demostración concluye repitiendo las cinco operaciones siguientes tres veces más:

  • se muestra el costo actual de una acción de cada equipo;
  • se realiza una transacción de compra o venta;
  • se muestra el costo de la transacción;
  • se actualiza el número total de acciones en circulación;
  • se actualiza la probabilidad de victoria de cada equipo.

Observe que el programa de demostración comienza con unas probabilidades iguales para ambos equipos. Esto no es realista en muchos escenarios reales de mercados de predicción. Es posible inicializar un mercado de predicción con probabilidades desiguales si se calculan los valores de 'x' e 'y' en las ecuaciones 3 y 4.

Resumen

La información de este artículo se basa en el artículo de investigación de 2002 de Robin Hanson, "Logarithmic Market Scoring Rules for Modular Combinatorial Information Aggregation". Puede encontrar una versión en PDF del artículo en varias ubicaciones de Internet con cualquier herramienta de búsqueda.

Los mercados de predicción no son solo una idea teórica abstracta. De hecho, en los últimos años han surgido varias empresas que implementan mercados de predicción con dinero real.

Un área activa de investigación es lo que se denominan mercados de predicción combinatorios. En lugar de elegir solo una o dos opciones para ganar, los expertos pueden comprar acciones en eventos combinados, como por ejemplo que el equipo A vencerá al equipo B y que el equipo J vencerá al equipo K. Los mercados de predicción combinatorios son mucho más complejos que los mercados simples.


Dr. James McCaffreytrabaja para Microsoft Research en Redmond, Washington. Ha colaborado en el desarrollo de varios productos de Microsoft como, por ejemplo, Internet Explorer y Bing. Dr. Puede ponerse en contacto James en jammc@microsoft.com.

Gracias a los siguientes expertos técnicos de Microsoft por revisar este artículo: Pallavi Choudhury, Gaz Iqbal, Umesh Madan y Tien Suwandy