Métodos de Monte Carlo con Google Cloud Dataproc y Apache Spark

Cloud Dataproc y Apache Spark proporcionan infraestructura y capacidad para ejecutar las simulaciones de Monte Carlo escritas en Java, Python o Scala.

Los métodos de Monte Carlo pueden ayudar a resolver un amplio rango de cuestiones sobre comercio, ingeniería, ciencia y matemáticas, entre otros campos. A través de muestras aleatorias repetidas que crean una probabilidad de distribución para una variable, una simulación de Monte Carlo puede brindar respuestas a preguntas que, de otro modo, serían imposibles de resolver. Por ejemplo, en finanzas, poner un precio a las opciones sobre acciones implica analizar las miles de formas en las que el precio de las acciones puede cambiar en el futuro. Los métodos de Monte Carlo permiten simular los cambios de precio de las acciones con un amplio rango de resultados posibles sin perder el control del dominio de las posibles causas del problema.

Antes, la ejecución de miles de simulaciones podía tardar mucho tiempo y generar grandes costos. Cloud Dataproc permite aprovisionar capacidad a pedido y pagar por minuto. Con Apache Spark, puedes usar clústeres de decenas, cientos o miles de servidores para ejecutar simulaciones de manera intuitiva y escalable con el fin de cumplir con tus necesidades. Esto significa que puedes ejecutar más simulaciones con mayor rapidez, lo que ayuda a que tu negocio innove y administre mejor los riesgos.

La seguridad siempre es importante cuando se trabaja con datos financieros. Cloud Dataproc se ejecuta en GCP, lo que ayuda a mantener tus datos seguros y privados de muchas formas. Por ejemplo, todos los datos se encriptan durante la transmisión y cuando están en reposo. Además, Google Cloud Platform cumple con los estándares ISO 27001, SOC3, FINRA y PCI.

Objetivos

  • Crear un clúster de Cloud Dataproc administrado (con Apache Spark preinstalado)
  • Ejecutar una simulación de Monte Carlo con Python que haga un cálculo aproximado del crecimiento de una cartera de valores en el tiempo
  • Ejecutar una simulación de Monte Carlo con Scala que simule la forma en que un casino gana dinero

Requisitos previos

  • Configurar un proyecto

    1. Sign in to your Google Account.

      If you don't already have one, sign up for a new account.

    2. Select or create a Google Cloud Platform project.

      Go to the Manage resources page

    3. Comprueba que la facturación esté habilitada en tu proyecto.

      Descubre cómo puedes habilitar la facturación

    4. {% dynamic if "no_credentials" in setvar.task_params %} {% dynamic setvar credential_type %}NO_AUTH{% dynamic endsetvar %} {% dynamic if not setvar.redirect_url %} {% dynamic setvar redirect_url %}https://console.cloud.google.com{% dynamic endsetvar %} {% dynamic endif %}{% dynamic endif %} {% dynamic if setvar.in_henhouse_no_auth_whitelist %} {% dynamic if not setvar.credential_type %}{% dynamic setvar credential_type %}NO_AUTH{% dynamic endsetvar %} {% dynamic endif %} {% dynamic elif setvar.in_henhouse_service_account_whitelist %} {% dynamic if not setvar.credential_type %}{% dynamic setvar credential_type %}SERVICE_ACCOUNT{% dynamic endsetvar %} {% dynamic endif %}{% dynamic endif %} {% dynamic if not setvar.service_account_roles and setvar.credential_type == "SERVICE_ACCOUNT" %} {% dynamic setvar service_account_roles %} {% dynamic endsetvar %} {% dynamic endif %} {% dynamic setvar console %} {% dynamic if "no_steps" not in setvar.task_params %}
    5. {% dynamic endif %} {% dynamic if setvar.api_list %} {% dynamic if setvar.in_henhouse_no_auth_whitelist or setvar.in_henhouse_service_account_whitelist %} Configurar un proyecto de GCP Console.

      Configurar un proyecto

      Haz clic para realizar alguna de las siguientes acciones:

      • Crear o seleccionar un proyecto.
      • Habilitar las {% dynamic if setvar.api_names %}{% dynamic print setvar.api_names %}{% dynamic else %}{% dynamic endif %}{% dynamic if "," in setvar.api_list %} API{% dynamic elif "API" in setvar.api_names %}{% dynamic else %} API{% dynamic endif %} necesarias para el proyecto.
      • {% dynamic if setvar.credential_type == 'SERVICE_ACCOUNT' %}
      • Crear una cuenta de servicio.
      • Descargar una clave privada como JSON.
      • {% dynamic endif %}

      Puedes visualizar y administrar estos recursos en GCP Console en cualquier momento.

      {% dynamic else %}{% dynamic if "no_text" not in setvar.task_params %} Habilita las {% dynamic if setvar.api_names %}{% dynamic print setvar.api_names %}{% dynamic else %}{% dynamic endif %}{% dynamic if "," in setvar.api_list %} API{% dynamic elif "API" in setvar.api_names %}{% dynamic else %} API{% dynamic endif %} necesarias. {% dynamic endif %}

      Habilita las {% dynamic if "," in setvar.api_list %} API{% dynamic else %} API{% dynamic endif %}

      {% dynamic endif %}{% dynamic endif %}{% dynamic if "no_steps" not in setvar.task_params %}
    6. {% dynamic endif %}{% dynamic endsetvar %}{% dynamic print setvar.console %}
    7. Install and initialize the Cloud SDK.

  • Crear un depósito de Cloud Storage en tu proyecto

    1. In the GCP Console, go to the Cloud Storage Browser page.

      Go to the Cloud Storage Browser page

    2. Click Create bucket.
    3. In the Create bucket dialog, specify the following attributes:
    4. Click Create.

Crea un clúster de Cloud Dataproc

Sigue los pasos para crear un clúster de Cloud Dataproc en Google Cloud Platform Console. La configuración predeterminada del clúster, que incluye dos nodos trabajadores, son suficientes para seguir este instructivo.

Inhabilita el registro para advertencias

Por configuración predeterminada, Apache Spark muestra el registro detallado en la ventana de la consola. Te resultará más fácil seguir este instructivo si cambias el nivel de registro para que solo registre errores. Lleva a cabo los pasos siguientes:

Establece una conexión SSH al nodo principal del clúster de Cloud Dataproc

  1. En la consola de Cloud Platform, ve a la página Instancias de VM.

    Ir a la página Instancias de VM

  2. En la lista de instancias de máquinas virtuales, haz clic en el botón SSH de la fila de la instancia a la que quieres conectarte.

Se abrirá una ventana del navegador en tu directorio principal del nodo.

Connected, host fingerprint: ssh-rsa 2048 ...
...
user@clusterName-m:~$

Cambia la configuración de registro

Edita /etc/spark/conf/log4j.properties.

sudo nano /etc/spark/conf/log4j.properties

Configura log4j.rootCategory para que sea igual a ERROR.

# Set only errors to be logged to the console
log4j.rootCategory=ERROR, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n

Guarda los cambios y cierra el editor. Si quieres volver a habilitar el registro detallado, puedes restablecer el valor de .rootCategory a su valor original (INFO) a fin de revertir el cambio.

Lenguajes de programación de Spark

Spark admite Python, Scala y Java como lenguajes de programación de aplicaciones independientes y proporciona intérpretes interactivos para Python y Scala. El lenguaje que elijas depende de tu preferencia. En este instructivo, se usan intérpretes interactivos porque proporcionan una forma rápida y fácil de experimentar si cambias el código, si pruebas distintos valores de entrada y si luego ves los resultados.

Estima el crecimiento de la cartera

En finanzas, los métodos de Monte Carlo a veces se usan para ejecutar simulaciones que intentan predecir el rendimiento de una inversión. Un método de Monte Carlo produce muestras aleatorias de resultados en un rango de condiciones posibles de mercado para resolver preguntas sobre el rendimiento de una cartera en promedio o en los peores casos.

Sigue estos pasos para crear una simulación que use los métodos de Monte Carlo a fin de intentar calcular el crecimiento de una inversión financiera según los factores de mercado comunes.

  1. Inicia el intérprete de Python desde el nodo principal de Cloud Dataproc.

    pyspark
    

    Espera a que aparezca el indicador de Spark (>>>).

  2. Ingresa el siguiente código. Ten cuidado, como siempre que programas en Python, de mantener las sangrías en la definición de función.

    >>> import random
    >>> import time
    >>> from operator import add
    
    >>> def grow(seed):
            random.seed(seed)
            portfolio_value = INVESTMENT_INIT
            for i in range(TERM):
                growth = random.normalvariate(MKT_AVG_RETURN, MKT_STD_DEV)
                portfolio_value += portfolio_value * growth + INVESTMENT_ANN
            return portfolio_value
    
  3. Presiona Intro hasta que vuelva a aparecer el indicador de Spark.

    En el código anterior, se define una función que modela lo que puede ocurrir cuando un inversionista tiene una cuenta de retiro existente que se invirtió en el mercado de valores y a la que se le agrega dinero todos los años. La función genera un retorno de la inversión aleatorio, como porcentaje, cada año por un período específico y toma un valor inicial como parámetro. Este valor se usa para reiniciar el generador de números aleatorios a fin de garantizar que la función no obtenga la misma lista de números cada vez que se ejecuta. La función random.normalvariate garantiza que los valores aleatorios aparezcan en una distribución normal para la media y desviación estándar especificadas. La función aumenta el valor de la cartera según el nivel de crecimiento que puede ser positivo o negativo y agrega una suma anual que representa las inversiones adicionales.

    Definirás las constantes requeridas en otro paso.

  4. El próximo paso es crear muchos valores iniciales para la función. En el indicador de Spark, ingresa este código que genera 10,000 valores iniciales:

    >>> seeds = sc.parallelize([time.time() + i for i in xrange(10000)])
    

    El resultado de una operación parallelize son los conjuntos de datos resilientes y distribuidos (RDD), que es una colección de elementos optimizados para el procesamiento paralelo. En este caso, el RDD contiene valores iniciales que se basan en la hora actual del sistema.

    Cuando Spark crea el RDD, divide los datos según el número de trabajadores y núcleos disponibles. En este caso, Spark usa ocho divisiones, una por cada núcleo. Es lo adecuado para esta simulación que tiene datos de 10,000 elementos. Sin embargo, en simulaciones de mayor tamaño, cada división puede superar el límite predeterminado. En este caso, puedes especificar un segundo parámetro en parallelize para aumentar el número de divisiones. Esto ayuda a que el tamaño de cada una se mantenga manejable y, a la vez, que Spark aproveche los ocho núcleos.

  5. Provee valores al RDD que contiene los valores iniciales de la función de crecimiento. Ingresa este código:

    >>> results = seeds.map(grow)
    

    El método map pasa cada valor inicial del RDD a la función grow y agrega cada resultado a un RDD nuevo que se guarda en results. Ten en cuenta que está operación que hace una transformación, no produce los resultados de inmediato. Spark no hará este trabajo hasta que se necesiten los resultados. Esta evaluación diferida es la razón por la que puedes ingresar el código sin definir las constantes.

  6. Especifica algunos valores con los que trabajará la función. Ingresa este código:

    >>> INVESTMENT_INIT = 100000  # starting amount
    >>> INVESTMENT_ANN = 10000  # yearly new investment
    >>> TERM = 30  # number of years
    >>> MKT_AVG_RETURN = 0.11 # percentage
    >>> MKT_STD_DEV = 0.18  # standard deviation
    
  7. Llama a reduce para agregar los valores del RDD. Ingresa el siguiente código a fin de sumar los resultados en el RDD:

    >>>  sum = results.reduce(add)
    
  8. Ingresa el siguiente código para hacer un cálculo aproximado y mostrar el resultado promedio:

    >>> print sum / 10000.
    

    Asegúrate de incluir el carácter del punto (.) al final. Significa la aritmética de coma flotante.

  9. Ahora modifica la suposición y verás cómo cambian los resultados. Por ejemplo, puedes ingresar un valor nuevo para el resultado promedio del mercado:

    >>> MKT_AVG_RETURN = 0.07
    
  10. Vuelve a ejecutar la simulación. Puedes ejecutar todo con el siguiente código:

    >>> print sc.parallelize([time.time() + i for i in xrange(10000)]) \
                .map(grow).reduce(add)/10000.
    
  11. Cuando termines de experimentar, presiona CTRL+D para salir del intérprete de Python.

Carga una simulación de Monte Carlo en Scala

Montecarlo es un destino de juego famoso. En esta sección, usarás Scala para crear una simulación que modele una ventaja matemática que podría usar un casino en un juego de azar. La "ventaja de la casa" es un casino real varía mucho según el juego; en keno, por ejemplo, puede ser de alrededor del 20%. En este instructivo, se crea un juego sencillo en el que la ventaja de la casa es solo del uno por ciento. Así es cómo funciona el juego:

  • El jugador hace una apuesta que consiste de un número de fichas del fondo de dinero.
  • Luego, lanza un dado de 100 lados (¿no sería estupendo?).
  • Si el resultado es un número del 1 al 49, el jugador recupera lo que apostó.
  • Si está entre el 50 y el 100, pierde la apuesta.

Como se ve, en este juego se crea una desventaja del uno por ciento para el jugador: en 51 de los 100 posibles resultados pierde.

Sigue los siguientes pasos para crear y ejecutar el juego:

  1. Inicia el intérprete de Scala desde el nodo principal de Cloud Dataproc.

    spark-shell
    
  2. Copia y pega este código para crear el juego. Scala no tiene los mismos requerimientos que Python en cuanto a las sangrías, por lo que puedes solo copiar y pegar el código en el mensaje de scala>.

    val STARTING_FUND = 10
    val STAKE = 1   // the amount of the bet
    val NUMBER_OF_GAMES = 25
    
    def rollDie: Int = {
        val r = scala.util.Random
        r.nextInt(99) + 1
    }
    
    def playGame(stake: Int): (Int) = {
        val faceValue = rollDie
        if (faceValue < 50)
            (2*stake)
        else
            (0)
    }
    
    // Function to play the game multiple times
    // Returns the final fund amount
    def playSession(
       startingFund: Int = STARTING_FUND,
       stake: Int = STAKE,
       numberOfGames: Int = NUMBER_OF_GAMES):
       (Int) = {
    
        // Initialize values
        var (currentFund, currentStake, currentGame) = (startingFund, 0, 1)
    
        // Keep playing until number of games is reached or funds run out
        while (currentGame <= numberOfGames && currentFund > 0) {
    
            // Set the current bet and deduct it from the fund
            currentStake = math.min(stake, currentFund)
            currentFund -= currentStake
    
            // Play the game
            val (winnings) = playGame(currentStake)
    
            // Add any winnings
            currentFund += winnings
    
            // Increment the loop counter
            currentGame += 1
        }
        (currentFund)
    }
    
  3. Presiona volver hasta que veas el mensaje de scala>.

  4. Ingresa el siguiente código para jugar 25 veces al juego, que es el número predeterminado de NUMBER_OF_GAMES.

    playSession()
    

    Tus fondos comienzan con un valor de 10 unidades. ¿Ahora, es mayor o menor?

  5. Simula 10,000 jugadores que apuestan 100 fichas por juego. Juega 10,000 juegos en una sesión. Esta simulación de Monte Carlo calcula la probabilidad de perder todo tu dinero antes de que termine la sesión. Ingresa el código:

    (sc.parallelize(1 to 10000, 500)
      .map(i => playSession(100000, 100, 250000))
      .map(i => if (i == 0) 1 else 0)
      .reduce(_+_)/10000.0)
    

    Ten en cuenta que la sintaxis .reduce(_+_) en Scala es la abreviación para agregar con una función de suma. Su función equivale a la sintaxis .reduce(add) que viste en el ejemplo de Python.

    El código anterior realiza las siguientes acciones:

    • Crea un RDD con los resultados de juego de una sesión.
    • Reemplaza los resultados de los jugadores en quiebra con el número 1 y los resultados que no son cero con el número 0.
    • Suma la cantidad de jugadores en quiebra.
    • La divide por el número de jugadores.

Un resultado típico puede ser:

0.998

Este representa casi una garantía de que perderás todo tu dinero, incluso si la ventaja del casino solo era del uno por ciento.

Pasos siguientes

Consulta Cloud Dataproc: Envía un trabajo para obtener información sobre cómo enviar trabajos de Spark al servicio de Cloud Dataproc sin tener que usar SSH para conectar al clúster.

  • Revisa nuestros instructivos y prueba otras funciones de Google Cloud Platform tú mismo.
  • Aprende a usar los productos de Google Cloud Platform para compilar soluciones de extremo a extremo.
¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...