# Librería para cálculo numérico
import numpy as np

# Valores dados en el enunciado
C_0, meta, i = 10000, 11000, 0.008
# Despejamos k tal que C_k=meta
k = np.ceil((meta/C_0 - 1)/i)   # Notar el uso de la función ceil (no se puede tener un número no entero de periodos)
k = k.astype(int)               # Conversión a entero (para visualización)
C_k = C_0*(1+k*i)               # Cálculo del capital al final del periodo k
C_k = round(C_k, 2)             # Redondeo con dos cifras decimales

# Imprimimos respuesta en pantalla
print("El número de periodos que se debe dejar invertido el dinero es ", k,". Al final del periodo ", k,
      ", el capital es ", C_k, ".", sep="")

"""
Explanation: Modelo del rendimiento de una cuenta de ahorro
<img style="center" src="https://static.pexels.com/photos/9660/business-money-pink-coins.jpg" width="500px" height="200px" alt="atom"/>

¿Tiene el dinero el mismo valor a lo largo del tiempo? La respuesta es no. Todos lo hemos vivido. 
Dos situaciones básicas:
1. <font color=blue>Inflación</font>: ¿Cuánto dinero necesitabas para comprar unas papas y un refresco hace 10 años? ¿Cuánto necesitas hoy?
2. <font color=blue>Interés</font>: no es lo mismo tener \$10000 MXN disponibles hoy a recibir \$10000 MXN en un año, pues los primeros pueden ser invertidos en un negocio o una cuenta bancaria para generar interés. Por lo tanto los \$10000 MXN disponibles hoy valen más que los \$10000 MXN que se recibirán en un año.

Referencia:
- Vidaurri Aguirre, Héctor Manuel. Ingeniería económica básica, ISBN: 978-607-519-017-4. (Disponible en biblioteca)
Referencias:
- http://www.sympy.org
- http://matplotlib.org
- http://www.numpy.org
- http://ipywidgets.readthedocs.io/en/latest/index.html

Interés
Nos centraremos en como cambia el valor del dinero en el tiempo debido al interés. Existen dos tipos:
Capitalización por interés simple
Este tipo de interés se calcula <font color=red>única y exclusivamente sobre la cantidad original que se invirtió</font>. Como consecuencia, el interés generado no forma parte del dinero que se invierte, es decir, los <font color=blue>intereses no ganan intereses</font>.
Suponga que se tiene un capital inicial $C_0$ y se invierte a un plazo de $k$ periodos (pueden ser meses, trimestres, semestres, años...) a una tasa de interés simple por periodo $i$. Al final del primer periodo, el capital $C_1$ que se obtiene es:
$$C_1=C_0+iC_0=C_0(1+i).$$
De la misma manera, como el interés solo se calcula sobre el capital inicial, al final del segundo periodo, el capital $C_2$ que se obtiene es:
$$C_2=C_1+iC_0=C_0+iC_0+iC_0=C_0(1+2i).$$
Así, al final del $k-$ésimo periodo, el capital $C_k$ que se obtiene es:
$$C_k=C_{k-1}+iC_0=C_0+kiC_0=C_0(1+ki).$$

Ejemplo. Suponga que se tiene un capital de \$10000 MXN, el cual se pone en un fondo de inversión que paga una tasa de interés simple del 0.8% mensual. 
Si se tiene una meta de ahorro de \$11000 MXN sin inversiones adicionales, ¿cuántos meses se debería dejar invertido el dinero?
End of explanation
"""


# Solución

"""
Explanation: <font color=blue>Actividad.</font>
- ¿Qué pasa si el interés no es del 0.8% mensual sino del 1% mensual?
- ¿Qué pasa si la meta no son \$11000 MXN si no \$12000 MXN?
End of explanation
"""


# Librerías para gráficos
import matplotlib.pyplot as plt
# Para que se muestren las gráficas en la misma ventana
%matplotlib inline
# Librería para widgets de jupyter
from ipywidgets import *

def interes_simple(C_0, meta, i):
    # Despejamos k
    k = np.ceil((meta/C_0 - 1)/i)           # Notar el uso de la función ceil
    k = k.astype(int)                       # Conversión a entero
    C_k = C_0*(1+k*i)                       # Cálculo del capital al final del periodo k
    C_k = round(C_k, 2)                     # Redondeo con dos cifras decimales
    # Vector de periodos
    kk = np.linspace(0,k,k+1)               
    # Vector de capitales por periodo
    CC = C_0*(1+kk*i)
    # Gráfico
    plt.figure(num=1); plt.clf()            # Figura 1, borrar lo que contenga
    plt.plot(kk, CC,'*',linewidth=3.0)      # Se grafica la evolución de los capitales
    plt.plot(kk,meta*np.ones(k+1),'--k')    # Se grafica la meta
    plt.xlabel('k')                         # Etiqueta eje x
    plt.ylabel('C_k')                       # Etiqueta eje y
    plt.grid(True)                          # Malla en la gráfica
    plt.show()                              # Mostrar la figura
    print("El número de periodos que se debe dejar invertido el dinero para llegar a la meta de ", meta," es ", k,
          ". Al final del periodo ", k,", el capital es ", C_k, ".", sep="")
    
interact_manual(interes_simple, C_0=fixed(10000), meta=(10000,12000,100), i=fixed(0.008));

"""
Explanation: Una gráfica que nos permite ilustrar la situación anterior se puede realizar de la siguiente manera.
End of explanation
"""


def interes_compuesto(C_0, meta, i):
    # Despejamos k
    k = np.ceil(np.log(meta/C_0)/np.log(1+i))
    k = k.astype(int)
    C_k = C_0*(1+i)**k                      # Cálculo del capital al final del periodo k
    C_k = round(C_k,2)                      # Redondeo con dos cifras decimales
    # Vector de periodos
    kk = np.linspace(0,k,k+1)
    # Vector de capitales por periodo
    CC = C_0*(1+i)**kk
    # Gráfico
    plt.figure(num=1); plt.clf()            # Figura 1, borrar lo que contenga
    plt.plot(kk, CC,'*',linewidth=3.0)      # Se grafica la evolución de los capitales
    plt.plot(kk,meta*np.ones(k+1),'--k')    # Se grafica la meta
    plt.xlabel('k')                         # Etiqueta eje x
    plt.ylabel('C_k')                       # Etiqueta eje y
    plt.grid(True)                          # Malla en la gráfica
    plt.show()                              # Mostrar la figura
    print("El número de periodos que se debe dejar invertido el dinero para llegar a la meta de ", meta," es ", k,
          ". Al final del periodo ", k,", el capital es ", C_k, ".", sep="")
    
interact_manual(interes_compuesto, C_0=fixed(10000), meta=(10000,12000,100), i=fixed(0.008));

"""
Explanation: Como se esperaba, el capital en el $k-$ésimo periodo $C_k=C_0(1+ki)$ crece linealmente con $k$.
Capitalización por interés compuesto
El capital que genera el interés simple permanece constante todo el tiempo de duración de la inversión. En cambio, el que produce el interés compuesto en un periodo se <font color=red>convierte en capital en el siguiente periodo</font>. Esto es, el interés generado al final de un periodo <font color=blue>se reinvierte para el siguiente periodo para también producir interés</font>.
Suponga que se tiene un capital inicial $C_0$, y se va a ceder el uso de este capital por un periodo de tiempo determinado a una tasa de interés $i$. El capital que se obtiene al final del primer periodo $C_1$ se puede calcular por
$$C_1=C_0(1+i).$$ 
Si la anterior suma se vuelve a ceder a la misma tasa de interés, al final del periodo dos el capital $C_2$ es 
$$C_2=C_1(1+i)=C_0(1+i)^2.$$
Si se repite el anterior proceso $k$ veces, el capital al final del $k-$ésimo periodo $C_k$ es 
$$C_k=C_{k-1}(1+i)=C_0(1+i)^k.$$
Referencia:
- https://es.wikipedia.org/wiki/Inter%C3%A9s_compuesto.

Ejemplo. Suponga que se tiene un capital de \$10000 MXN, el cual se pone en un fondo de inversión que paga una tasa de interés del 0.8% mensual. 
Si se tiene una meta de ahorro de \$11000 MXN sin inversiones adicionales, ¿cuántos meses se debería dejar invertido el dinero?
Muestre una gráfica que ilustre la situación.
End of explanation
"""


# Librería de cálculo simbólico
import sympy as sym

# Para imprimir en formato TeX
from sympy import init_printing; init_printing(use_latex='mathjax')

# Símbolos t(para el tiempo) y r(para el interés instantáneo)
#t, r = sym.symbols('t r')
# Otra forma de hacer lo anterior
sym.var('t r')

C = sym.Function('C')

eqn = sym.Eq(sym.Derivative(C(t),t) - r*C(t), 0) # Ecuación diferencial
display(eqn) # Mostrar ecuación
sym.dsolve(eqn, C(t)) # Resolver

"""
Explanation: El capital en el $k-$ésimo periodo $C_k=C_0(1+i)^k$ crece de manera exponencial con $k$.

<font color=blue>Actividad.</font>
- Modificar el código anterior para dejar fija la meta de ahorro y variar la tasa de interés compuesta.

Capitalización continua de intereses
La capitalización continua se considera un tipo de capitalización compuesta, en la que a cada instante de tiempo $t$ se se capitalizan los intereses. Es decir, la frecuencia de capitalización es infinita (o, equivalentemente, el periodo de capitalización tiende a cero).
Suponga que se tiene un capital inicial $C_0$, y que el capital acumulado en el tiempo $t$ es $C(t)$. Queremos saber cuanto será el capital pasado un periodo de tiempo $\Delta t$, dado que la tasa de interés efectiva para este periodo de tiempo es $i$. De acuerdo a lo anterior tenemos
$$C(t+\Delta t)=C(t)(1+i)=C(t)(1+r\Delta t),$$
donde $r=\frac{i}{\Delta t}$ es la tasa de interés instantánea. Manipulando la anterior expresión, obtenemos
$$\frac{C(t+\Delta t)-C(t)}{\Delta t}=r\; C(t).$$
Haciendo $\Delta t\to 0$, obtenemos la siguiente ecuación diferencial 
$$\frac{d C(t)}{dt}=r\; C(t),$$
sujeta a la condición inicial (monto o capital inicial) $C(0)=C_0$.
La anterior, es una ecuación diferencial lineal de primer orden, para la cual se puede calcular la solución analítica.
End of explanation
"""


# Librerías para integración numérica
from scipy.integrate import odeint

# Modelo de capitalización continua
def cap_continua(C, t, r):
    return r * C

def interes_continuo(C_0, meta, r):
    # Despejamos t
    t = np.log(meta/C_0)/r
    # Vector de periodos
    tt = np.linspace(0,t,100)
    # Vector de capitales por periodo
    CC = odeint(cap_continua, C_0, tt, args = (r,))
    # Gráfico
    plt.figure(num=1); plt.clf()                # Figura 1, borrar lo que contenga
    plt.plot(tt, CC,'-',linewidth=3.0)          # Se grafica la evolución de los capitales
    plt.plot(tt,meta*np.ones(len(tt)),'--k')    # Se grafica la meta
    plt.xlabel('t')                             # Etiqueta eje x
    plt.ylabel('C(t)')                          # Etiqueta eje y
    plt.grid(True)                              # Malla en la gráfica
    plt.show()                                  # Mostrar la figura
    print("El tiempo que se debe dejar invertido el dinero para llegar a la meta de ", meta," es ", t, " meses.", sep="")
    
interact_manual(interes_continuo, C_0=fixed(10000), meta=(10000,20000,100), r=fixed(np.log(1+i)));

"""
Explanation: con $C_1=C_0$.
La equivalencia entre la tasa de interés compuesta $i$ y la tasa de interés instantánea $r$ viene dada por
$$e^r=1+i.$$

¿Cómo podemos calcular la solución numérica?

Ejemplo. Suponga que se tiene un capital de \$10000 MXN, el cual se pone en un fondo de inversión que paga una tasa de interés del 0.8% mensual. 
Si se tiene una meta de ahorro de \$11000 MXN sin inversiones adicionales, ¿cuánto tiempo se debe dejar invertido el dinero?
Muestre una gráfica que ilustre la situación.
End of explanation
"""


# Librería de análisis de datos
import pandas as pd
# Librería para manipulación de fechas
from datetime import date

# Datos para la cuenta de ahorro
Tasa_interes = 0.1
Anos = 2
Abonos_ano = 12
Inicial = 10000
Meta = 100000
fecha_inicio = (date(2018,1,31))

# Cálculo de abonos mensuales iguales para llegar a la meta de ahorro en el tiempo deseado
Abono = (Tasa_interes/Abonos_ano)*(Meta - Inicial*(1+Tasa_interes/Abonos_ano)**(Anos*Abonos_ano))/((1+Tasa_interes/Abonos_ano)**(Anos*Abonos_ano)-1)

# Rango de fechas en la tabla
rng = pd.date_range(fecha_inicio, periods=Anos*Abonos_ano, freq='MS')
rng.name = "Fecha del abono"

# Columnas de abonos y extracto de cuenta en las fechas respectivas
df = pd.DataFrame(index=rng,columns=['Abono', 'Balance'], dtype='float')

# Para poner el indice con los periodos y no con las fechas
df.reset_index(inplace=True)
df.index += 1
df.index.name = "Periodo"  # Columna correspondiente al periodo

# Los abonos son iguales
df["Abono"] = Abono

# Vector indizador de los periodos de los abonos
index_vector = np.arange(1,len(rng)+1)
# Extracto de la cuenta mes a mes
df["Balance"] = Inicial*(1+Tasa_interes/Abonos_ano)**index_vector+Abono*(((1+Tasa_interes/Abonos_ano)**index_vector-1)/(Tasa_interes/Abonos_ano))

# Redondear con dos cifras decimales
df = df.round(2)
df

# La siguiente solución es también válida. Sin embargo, evitar el uso de 'for'

#df.loc[1, "Balance"] = Inicial*(1+Tasa_interes/Abonos_ano)**1 + df.loc[1, "Abono"]

#for i in range(2, len(df)+1):
#    # Get the previous balance as well as current payments
#    prev_balance = df.loc[i-1, "Balance"]

#    df.loc[i, "Balance"] = prev_balance*(1+Tasa_interes/Abonos_ano)**1 + df.loc[i, "Abono"]

df.plot()

"""
Explanation: Ver que lo anterior es una aproximación continua del modelo discreto de interés continuo cuando la frecuencia de capitalización tiende a infinito

Tabla de abonos
Como aplicación importante del concepto de interés compuesto se encuentra la creación de un modelo de cuenta de ahorro.
Referencia:
- Vidaurri Aguirre, Héctor Manuel. Ingeniería económica básica, ISBN: 978-607-519-017-4. (Disponible en biblioteca)
- http://pbpython.com/amortization-model.html
End of explanation
"""


