Simulación Montecarlo

Recordamos el experimento de la caminata aleatoria.


In [1]:
from IPython.display import YouTubeVideo
YouTubeVideo('Y77WnkLbT2Q')


Out[1]:

¿Qué es una simulación montecarlo?

Revisitamos el concepto de simulación montecarlo.

La idea de una simulación montecarlo es probar muchos resultados posibles. En la realidad, solo uno de esos resultados posibles se dará, pero, en términos de evaluación de riesgos, cualquiera de las posibilidades podría ocurrir.

Los simuladores montecarlo se usan usualmente para evaluar el riesgo de una estrategia de negocios dada con opciones y acciones.

Los simuladores montecarlo pueden ayudar a tomar decisiones exitosas, y que el resultado de una decisión no sea la única medida de si dicha decisión fue buena. Las decisiones no deben ser evaluadas después del resultado. Por el contrario, los riesgos y beneficios solo deben ser considerados en el momento en que se debe tomar la decisión, sin prejuicios retrospectivos. Un simulador montecarlo puede ayudar a visualizar muchos (o en algunos casos, todos) de los resultados potenciales para tener una mejor idea de los riesgos de una decisión.

Usamos montecarlo para evaluar el resultado de la caminata aleatoria

  • Describir, de nuevo, el proceso de la caminata aleatoria en el pizarrón y ver el valor esperado de la caminata después de N pasos.

  • Luego, evaluar el proceso utilizando montecarlo y comparar resultados.


In [2]:
# Hacer código acá
import random
import numpy as np
import matplotlib.pyplot as plt

In [15]:
def caminata_aleatoria(N):
    s = 0
    ss = [s]
    for i in range(N):
        z = random.choice([-1,1])
        s += z
        ss.append(s)
    return ss, s

In [28]:
N = 100
n = 10000
ultimo = []
for i in range(n):
    ss, s = caminata_aleatoria(N)
    ultimo.append(s)
    #plt.plot(ss)

#plt.show()
print("La media muestral del ultimo valor de la caminata aleatoria es:", np.mean(ultimo))


La media muestral del ultimo valor de la caminata aleatoria es: -0.0924

Ejemplo

Ahora recapitulemos el ejemplo básico del apostador.

Referencia:

Supongamos que estamos en un casino especial, donde el usuario puede tirar un dado metafórico que puede dar como resultado del número uno (1) al número cien (100).

Si el usuario tira cualquier número entre 1 y 50, el casino gana. Si el usuario tira cualquier número entre 51 y 99, el usuario gana. Si el usuario tira 100, pierde.

Con esto, el casino mantiene un margen del 1%, el cual es mucho más pequeño que el margen típico en casinos, al igual que el margen de mercado cuando se incorporan costos por transacción.

Por ejemplo, Scottrade cobra \$7 USD por transacción. Si se invierten \$1000 USD por acción, esto significa que tienes que pagar \$7 USD para entrar, y \$7 USD para salir, para un total de \$14 USD.

Esto pone el margen en 1.4\%. Esto significa, que a largo plazo, las ganancias tienen que ser mayores a 1.4\% en promedio, de otra manera se estará perdiendo dinero. Aunque este porcentaje es pequeño, las probabilidades ya están en contra. La comercialización de acciones es un juego 50/50, especialmente en el corto plazo.

De nuevo, con nuestro ejemplo en mente, 1-50, la casa gana. 51-99 el usuario gana. Un 100 significa que la casa gana.

Ahora, comencemos. Primero tenemos que crear nuestro dado.


In [30]:
# Explorar función randint de la librería random y crear una función que simule la tirada de un dado
def tirar_dado():
    return random.randint(1, 100)

In [31]:
# Tirar el dado 100 veces para comprobar que nuestra función si trabaja correctamente
N = 100
for i in range(N):
    print("resultado:", tirar_dado())


resultado: 42
resultado: 62
resultado: 70
resultado: 68
resultado: 10
resultado: 32
resultado: 75
resultado: 58
resultado: 4
resultado: 15
resultado: 94
resultado: 13
resultado: 56
resultado: 17
resultado: 79
resultado: 29
resultado: 43
resultado: 79
resultado: 91
resultado: 3
resultado: 75
resultado: 17
resultado: 18
resultado: 31
resultado: 20
resultado: 5
resultado: 62
resultado: 17
resultado: 17
resultado: 84
resultado: 12
resultado: 89
resultado: 40
resultado: 58
resultado: 46
resultado: 51
resultado: 43
resultado: 40
resultado: 77
resultado: 49
resultado: 11
resultado: 82
resultado: 30
resultado: 22
resultado: 19
resultado: 15
resultado: 10
resultado: 77
resultado: 18
resultado: 51
resultado: 34
resultado: 86
resultado: 62
resultado: 68
resultado: 93
resultado: 60
resultado: 23
resultado: 94
resultado: 57
resultado: 45
resultado: 9
resultado: 82
resultado: 88
resultado: 61
resultado: 71
resultado: 99
resultado: 86
resultado: 26
resultado: 39
resultado: 67
resultado: 60
resultado: 23
resultado: 41
resultado: 100
resultado: 49
resultado: 27
resultado: 68
resultado: 81
resultado: 6
resultado: 87
resultado: 8
resultado: 7
resultado: 71
resultado: 96
resultado: 26
resultado: 65
resultado: 69
resultado: 74
resultado: 53
resultado: 73
resultado: 47
resultado: 3
resultado: 91
resultado: 33
resultado: 86
resultado: 7
resultado: 94
resultado: 100
resultado: 40
resultado: 10

Sin embargo, el dado por si solo no nos es útil. Necesitamos una función que nos devuelva sólamente si ganamos o perdemos.


In [36]:
# Cambiar/usar la anterior función para obtener una que devuelva simplemente ganar(true) o perder(false)
def tirar_dado():
    x = random.randint(1, 100)
    if x <= 50:
        return False
    elif 51<= x< 100:
        return True
    elif x == 100:
        return False

In [42]:
# Probar la función creada para ver que funcione
N = 100
contador_ganar = 0
contador_perder = 0
for i in range(N):
    if tirar_dado():
        print("Felicidades!")
        contador_ganar += 1
    else:
        print("Sigue intentando")
        contador_perder += 1

print("Ganamos", contador_ganar, " veces y perdimos", contador_perder, " veces.")


Felicidades!
Sigue intentando
Sigue intentando
Felicidades!
Sigue intentando
Sigue intentando
Sigue intentando
Sigue intentando
Sigue intentando
Sigue intentando
Felicidades!
Felicidades!
Sigue intentando
Felicidades!
Sigue intentando
Sigue intentando
Sigue intentando
Felicidades!
Felicidades!
Felicidades!
Felicidades!
Sigue intentando
Sigue intentando
Sigue intentando
Sigue intentando
Sigue intentando
Sigue intentando
Sigue intentando
Felicidades!
Felicidades!
Sigue intentando
Sigue intentando
Felicidades!
Sigue intentando
Sigue intentando
Felicidades!
Felicidades!
Felicidades!
Felicidades!
Sigue intentando
Felicidades!
Felicidades!
Sigue intentando
Felicidades!
Felicidades!
Felicidades!
Sigue intentando
Felicidades!
Felicidades!
Sigue intentando
Felicidades!
Felicidades!
Felicidades!
Felicidades!
Sigue intentando
Sigue intentando
Sigue intentando
Felicidades!
Sigue intentando
Felicidades!
Felicidades!
Sigue intentando
Felicidades!
Sigue intentando
Sigue intentando
Sigue intentando
Felicidades!
Sigue intentando
Felicidades!
Sigue intentando
Sigue intentando
Sigue intentando
Sigue intentando
Sigue intentando
Felicidades!
Felicidades!
Sigue intentando
Felicidades!
Sigue intentando
Felicidades!
Sigue intentando
Sigue intentando
Felicidades!
Sigue intentando
Felicidades!
Sigue intentando
Felicidades!
Felicidades!
Felicidades!
Sigue intentando
Sigue intentando
Sigue intentando
Sigue intentando
Felicidades!
Felicidades!
Felicidades!
Sigue intentando
Felicidades!
Felicidades!
Sigue intentando
Ganamos 47  veces y perdimos 53  veces.

Ahora, necesitamos crear un apostador. Empezaremos con uno extremadamente básico por ahora. Veremos, que aún con un apostador muy básico, veremos cosas muy reveladoras usando un simulador montecarlo.


In [44]:
# Crearemos un apostador simple. Las caracterísitcas son: se empieza con un capital inicial, siempre se apuesta lo mismo,
# y se va a apostar un número determinado de veces.
def apostador(cap_inicial, apuesta, n_apuestas):
    capital = cap_inicial
    ccapital = [cap_inicial]
    
    for i in range(n_apuestas):
        if tirar_dado():
            capital += apuesta
            ccapital.append(capital)
        else:
            capital -= apuesta
            ccapital.append(capital)
    
    return ccapital

In [47]:
# Ver como evolucionan los fondos de nuestro apostador al jugar 100 veces
plt.plot(apostador(10000, 100, 100))
plt.show()


En realidad no nos importa tanto como evolucionan estos fondos. Nos importa más cuáles son los fondos al final (al largo plazo). Modificar la función anterior para ver esto.


In [54]:
# Función de apostador que devuelve los fondos al final de apostar una cantidad determinadad de veces
def apostador(cap_inicial, apuesta, n_apuestas):
    capital = cap_inicial
    #ccapital = [cap_inicial]
    
    for i in range(n_apuestas):
        if tirar_dado():
            capital += apuesta
            #ccapital.append(capital)
        else:
            capital -= apuesta
            #ccapital.append(capital)
        
        if capital<=0:
            capital = "Banca rota"
            return capital
    
    return capital

In [55]:
# (Montecarlo) Simular varios (100) escenarios en que se apuestan 50, 100, 1000 y 10000 veces. ¿Qué pasa?
N = 100
n = 10000
for i in range(N):
    print("Capital al final:", apostador(10000, 100, n))


Capital al final: Banca rota
Capital al final: 1800
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: 800
Capital al final: 1000
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: 7000
Capital al final: Banca rota
Capital al final: 15200
Capital al final: Banca rota
Capital al final: 5400
Capital al final: 3800
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: 18000
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota
Capital al final: Banca rota

Vemos que al largo plazo la mayoría de los apostadores quebraron. Sin embargo, esta forma de visualizar los resultados no es adecuada. Utilicemos matplotlib.


In [56]:
# Modificar la función para que no devuelva los fondos al final, sino que grafique los fondos a través del tiempo
def apostador(cap_inicial, apuesta, n_apuestas):
    capital = cap_inicial
    ccapital = [cap_inicial]
    
    for i in range(n_apuestas):
        if tirar_dado():
            capital += apuesta
            ccapital.append(capital)
        else:
            capital -= apuesta
            ccapital.append(capital)
    
    return ccapital

In [60]:
# (Montecarlo) Simular varios (100) escenarios en que se apuestan 50, 100, 1000 y 10000 veces. ¿Qué pasa?
N = 100
n = 10000
for i in range(N):
    plt.plot(apostador(10000, 100, n))
    
plt.show()


Por esto los apostadores pierden. Normalmente las probabilidades no están evidentemente muy en contra de ellos, solo un poco. Los casinos únicamente entienden psicología básica: ganar es extremadamente adictivo. Por ello, los casino se construyen para mantenerte jugando.

En el corto plazo, la mayoría de los jugadores no se dan cuenta que son más propensos a perder. Las veces que ganan y pierden son muy parejas. Estadísticamente, casi la mitad de las personas terminarán con ganancias después de jugar unas pocas veces. El problema es la adicción, y que ellos continuarán apostando, y por ende perdiendo sus ganancias. Es matemática extremadamente básica, pero la psicología humana es débil.

Created with Jupyter by Esteban Jiménez Rodríguez.