Métodos de Monte Carlo usando o Cloud Dataproc e o Apache Spark

Usando o Cloud Dataproc e o Apache Spark, você dispõe da infraestrutura e da capacidade para executar simulações Monte Carlo desenvolvidas em Java, Python ou Scala.

Os métodos Monte Carlo ajudam a responder a uma grande variedade de perguntas relacionadas a negócios, engenharia, ciências, matemática e outros campos. Em uma simulação Monte Carlo, com o uso de amostragem aleatória repetida para criar uma distribuição de probabilidade para uma variável, você consegue respostas para perguntas que não conseguiria com outros métodos. Em finanças, por exemplo, a determinação do preço de uma opção de equidade requer a análise de milhares de possibilidades de mudança do preço da ação ao longo do tempo. Com os métodos Monte Carlo, você simula essas oscilações em uma ampla variedade de resultados possíveis e, ao mesmo tempo, mantém o controle sobre o domínio das entradas para o problema.

No passado, a execução de milhares de simulações poderia levar um longo tempo e ter um alto custo. Com o Cloud Dataproc, a capacidade é provisionada sob demanda e paga por minuto. Com o Apache Spark, você usa os clusters de dezenas, centenas ou milhares de servidores para executar simulações de maneira intuitiva, e faz o escalonamento de acordo com as suas necessidades. Isso significa que você consegue executar mais simulações com mais rapidez, o que ajuda a inovar no seu negócio com mais agilidade e possibilita um melhor gerenciamento dos riscos.

A segurança é sempre importante ao lidar com dados financeiros. O Cloud Dataproc é executado no Google Cloud Platform (GCP), o que ajuda a manter seus dados seguros, protegidos e particulares de várias maneiras. Por exemplo, todos os dados são criptografados durante a transmissão e quando em repouso, e o GCP está em conformidade com ISO 27001, SOC3 e PCI.

Objetivos

  • Criar um cluster gerenciado do Cloud Dataproc, com o Apache Spark pré-instalado.
  • Executar uma simulação de Monte Carlo com o Python que faça a estimativa do crescimento de um portfólio de ações ao longo do tempo.
  • Executar uma simulação de Monte Carlo com o Scala que simule os lucros de um cassino.

Custos

Neste tutorial, usamos os seguintes componentes faturáveis do Google Cloud Platform:

Use a calculadora de preços para gerar uma estimativa de custo com base no uso do projeto. Novos usuários do GCP são qualificados para uma avaliação gratuita.

Ao concluir este tutorial, exclua os recursos criados para evitar o faturamento contínuo. Para mais informações, consulte Como fazer a limpeza.

Antes de começar

Como criar um cluster do Cloud Dataproc

Siga as etapas para criar um cluster do Cloud Dataproc no Console do Google Cloud Platform. As configurações de cluster padrão, que incluem dois nós de trabalho, são suficientes para este tutorial.

Como desativar a geração de registros de avisos

Por padrão, um registro detalhado é impresso na janela de console pelo Apache Spark. Para os fins deste tutorial, altere o nível da geração de registro para registrar somente os erros. Siga estas etapas:

Usar o ssh para se conectar ao nó mestre do cluster do Cloud Dataproc

  1. No Console do GCP, acesse a página "Instâncias de VM".

    Acessar a página "Instâncias da VM"

  2. Na lista de instâncias de máquinas virtuais, clique em SSH na linha da instância com que você quer se conectar.

Uma janela de navegador é aberta no diretório principal do nó mestre.

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

Alterar a configuração de geração de registros

  1. No diretório principal do nó mestre, edite /etc/spark/conf/log4j.properties.

    sudo nano /etc/spark/conf/log4j.properties
    
  2. Defina log4j.rootCategory 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
    
  3. Salve as alterações e saia do editor. Se você quiser reativar a geração de registros para desfazer a alteração, restaure o valor de .rootCategory para o valor original INFO.

Linguagens de programação do Spark

O Spark é compatível com Python, Scala e Java como linguagens de programação para aplicativos autônomos, e fornece interpretadores interativos para o Python e o Scala. A escolha da linguagem é uma questão de preferência pessoal. Neste tutorial, usamos os interpretadores interativos porque assim é possível fazer testes por meio da alteração do código, tentando diferentes valores de entrada e visualizando os resultados.

Como estimar o crescimento do portfólio

Em finanças, os métodos Monte Carlo são, às vezes, usados para executar simulações que tentam prever o desempenho de um investimento. Para responder a perguntas sobre o desempenho do portfólio na média ou nos cenários mais pessimistas, amostras aleatórias dos resultados em várias condições prováveis de mercado são produzidas por essas simulações.

Siga estas etapas para criar uma simulação que usa os métodos Monte Carlo para estimar o crescimento de um investimento financeiro com base em alguns fatores de mercado comuns.

  1. Inicie o interpretador do Python a partir do nó mestre do Cloud Dataproc.

    pyspark
    

    Aguarde pelo prompt do Spark >>>.

  2. Insira o código a seguir. Certifique-se de manter o recuo na definição da função.

    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. Pressione return até que o prompt do Spark apareça novamente.

    No código acima, foi definida uma função que modela o que pode acontecer quando um investidor tem uma conta de aposentadoria existente que é investida no mercado de ações, onde eles depositam dinheiro anualmente. A função gera um retorno de investimento aleatório, como uma porcentagem, para cada ano que um termo especificado durar. Essa função gera um valor de semente como parâmetro que é usado para realimentar o gerador de números aleatórios. Isso garante que a função não receba a mesma lista de números cada vez que ela é executada. Com a função random.normalvariate, esses valores aleatórios ocorrem em uma distribuição normal para a média e o desvio padrão especificados. A função aumenta o valor do portfólio de acordo com o volume de crescimento, que pode ser positivo ou negativo, e adiciona uma soma anual que representa o investimento adicional.

    As constantes obrigatórias serão definidas em uma etapa posterior.

  4. Crie muitas sugestões para alimentar a função. No prompt do Spark, insira o código a seguir, que gera 10.000 sugestões:

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

    O resultado da operação parallelize é um conjunto de dados distribuídos resiliente (RDD, na sigla em inglês), que é uma coleção de elementos otimizados para processamento paralelo. Nesse caso, o RDD contém sugestões que são baseadas no horário atual do sistema.

    Quando o RDD é criado, os dados são divididos no Spark com base no número de workers e núcleos disponíveis. Nesse caso, o Spark escolhe usar oito partes, uma parte para cada núcleo. Isso atende a essa simulação, que tem 10.000 itens de dados. Para simulações maiores, cada parte pode ser maior do que o limite padrão. Nesse caso, basta especificar um segundo parâmetro no parallelize para aumentar o número de partes. Isso ajuda a manter o tamanho de cada parte gerenciável e, ao mesmo tempo, o Spark continua a usar oito núcleos.

  5. Alimente o RDD que contém as sugestões para a função de crescimento.

    results = seeds.map(grow)
    

    Com o método map, cada sugestão no RDD é passada para a função grow, e cada resultado é anexado a um novo RDD, que é armazenado em results. Observe que essa operação de transformação não produz os resultados imediatamente. Isso não é feito no Spark antes dos resultados serem necessários. Devido a essa avaliação lenta, o código pode ser inserido sem ter as constantes definidas.

  6. Especifique alguns valores para a função.

    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. Chame reduce para agregar os valores no RDD. Insira o código a seguir para somar os resultados no RDD:

    sum = results.reduce(add)
    
  8. Estime e exiba o retorno médio:

    print sum / 10000.
    

    Verifique se o caractere ponto (.) foi incluído no final. Ele representa um valor aritmético de ponto flutuante.

  9. Agora altere uma pressuposição e veja como os resultados mudam. Por exemplo, insira um novo valor para o retorno médio do mercado:

    MKT_AVG_RETURN = 0.07
    
  10. Execute a simulação novamente.

    print sc.parallelize([time.time() + i for i in xrange(10000)]) \
            .map(grow).reduce(add)/10000.
    
  11. Ao concluir o teste, pressione CTRL+D para sair do interpretador do Python.

Como programar uma simulação Monte Carlo no Scala

A cidade de Monte Carlo é conhecida como um destino de apostas. Nesta seção, você usa o Scala para criar uma simulação que modela a vantagem matemática que um cassino tem em um jogo de azar. O "house edge" de um cassino real varia bastante entre os jogos. Ele pode ser maior do que 20% no keno, por exemplo. Neste tutorial, um jogo simples será criado, onde a casa tem somente uma vantagem de 1%. Veja como ele funciona:

  • O jogador faz uma aposta, que consiste de algumas fichas do fundo de apostas.
  • O jogador rola um dado de cem lados (que legal, não?).
  • Se o resultado do teste for um número de 1 a 49, o jogador ganha.
  • Caso o resultado seja entre 50 e 100, o jogador perde a aposta.

Veja que, nesse jogo, o jogador tem uma desvantagem de 1%: em 51 dos 100 resultados possíveis de cada rolamento, ele perde.

Siga estas etapas para criar e executar o jogo:

  1. Inicie o interpretador do Scala do nó mestre do Cloud Dataproc.

    spark-shell
    
  2. Copie e cole o código a seguir para criar o jogo O Scala não tem os mesmos requisitos que o Python em relação ao recuo. Portanto, é possível simplesmente copiar e colar o código no prompt 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. Pressione return até que o prompt scala> apareça.

  4. Insira o código a seguir para jogar 25 vezes, que é o valor padrão de NUMBER_OF_GAMES.

    playSession()
    

    Seu fundo de apostas começou com o valor de dez unidades. E agora, está mais alto ou mais baixo?

  5. Agora simule 10.000 jogadores apostando 100 fichas por jogo. Jogue 10.000 vezes em uma sessão. Nesta simulação de Monte Carlo, calculamos a probabilidade de perder todo seu dinheiro antes do final da sessão. Insira o código a seguir:

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

    Observe que a sintaxe .reduce(_+_) é uma abreviação do Scala para a agregação usando a função de soma. A funcionalidade dela é equivalente à sintaxe .reduce(add), que você viu no exemplo do Python.

    No código anterior, as seguintes etapas foram executadas:

    • criação de RDD com os resultados da sessão jogada;
    • substituição dos resultados dos jogadores falidos pelo número 1 e dos resultados diferentes de zero pelo número 0;
    • soma da contagem de jogadores falidos;
    • divisão da contagem pelo número de jogadores.

    Um resultado típico seria:

    0.998
    

    Isso representa uma garantia quase total de que você perderia todo o dinheiro, mesmo se o cassino tivesse somente uma vantagem de 1%.

Como fazer a limpeza

Para evitar que os recursos usados neste tutorial sejam cobrados na conta do Google Cloud Platform, faça o seguinte:

Excluir o projeto

  1. No Console do GCP, acesse a página "Projetos".

    Acessar a página Projetos

  2. Na lista de projetos, selecione um e clique em Excluir projeto.
  3. Na caixa de diálogo, digite o código do projeto e clique em Encerrar para excluí-lo.

A seguir

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…