O Dataproc e o Apache Spark oferecem a infraestrutura e a capacidade para executar simulações Monte Carlo desenvolvidas em Java, Python ou Scala.
Os métodos Monte Carlo ajudam a responder 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 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 Dataproc é executado no Google Cloud, o que ajuda a manter os 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 Google Cloud está em conformidade com ISO 27001, SOC3 e PCI.
Objetivos
- Criar um cluster gerenciado do Dataproc com o Apache Spark pré-instalado.
- Executar uma simulação 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 documento, você usará os seguintes componentes faturáveis do Google Cloud:
Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços.
Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Saiba mais em Limpeza.
Antes de começar
- Configurar um projeto do Google Cloud
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Dataproc and Compute Engine APIs.
- Install the Google Cloud CLI.
-
To initialize the gcloud CLI, run the following command:
gcloud init
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Dataproc and Compute Engine APIs.
- Install the Google Cloud CLI.
-
To initialize the gcloud CLI, run the following command:
gcloud init
Como criar um cluster do Dataproc
Siga as etapas para criar um cluster do Dataproc usando o Console do Google Cloud. 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:
Use ssh
para se conectar ao nó principal do cluster do Dataproc
O nó principal do cluster do Dataproc tem o sufixo -m
no nome da VM.
- In the Google Cloud console, go to the VM instances page.
- In the list of virtual machine instances, click SSH in the row of the instance that you want to connect to.
Uma janela SSH é aberta conectada ao nó principal.
Connected, host fingerprint: ssh-rsa 2048 ... ... user@clusterName-m:~$
Alterar a configuração de geração de registros
No diretório inicial do nó principal, edite
/etc/spark/conf/log4j.properties
.sudo nano /etc/spark/conf/log4j.properties
Defina
log4j.rootCategory
igual aERROR
.# 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
Salve as alterações e saia do editor. Se você quiser reativar a geração de registros detalhados, reverta a alteração restaurando 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.
Inicie o interpretador de Python a partir do nó principal do Dataproc.
pyspark
Aguarde o prompt do Spark
>>>
.Insira o código a seguir. Mantenha 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
Pressione
return
até ver novamente o prompt do Spark.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. A função
random.normalvariate
garante que esses valores aleatórios ocorram 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.
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 range(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 sementes 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, especificar um segundo parâmetro para
parallelize
pode aumentar o número de frações, o que pode ajudar a manter o tamanho de cada fração sob controle, enquanto o Spark ainda aproveita todos os oito núcleos.Alimente o RDD que contém as sementes para a função de crescimento.
results = seeds.map(grow)
O método
map
passa cada semente no RDD para a funçãogrow
e anexa cada resultado a um novo RDD, que é armazenado emresults
. 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.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
Chame
reduce
para agregar os valores no RDD. Insira o seguinte código para somar os resultados no RDD:sum = results.reduce(add)
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.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
Execute a simulação novamente.
print (sc.parallelize([time.time() + i for i in range(10000)]) \ .map(grow).reduce(add)/10000.)
Quando terminar os testes, pressione
CTRL+D
para sair do interpretador de Python.
Como programar uma simulação Monte Carlo em 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:
Inicie o interpretador de Scala a partir do nó principal do Dataproc.
spark-shell
Copie e cole o código a seguir para criar o jogo Scala não tem os mesmos requisitos que Python quando se trata de recuo. Portanto, basta copiar e colar esse 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) }
Pressione
return
até ver o promptscala>
.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 10 unidades. E agora, está mais alto ou mais baixo?
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 em Scala para agregação usando uma 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úmero0
; - 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%.
Limpar
Excluir o projeto
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
A seguir
- Para saber mais sobre como enviar jobs do Spark para o Dataproc sem precisar usar o
ssh
para se conectar ao cluster, leia Dataproc: enviar um job.