Con base en lo aprendido en la última clase estamos preparados para crear una red neuronal y realizar la predicción de los precios de una acción. Antes de ello será necesario abordar un poco sobre una librería nueva.
Esta librería extrae información actualizada de yahoo finance referente a los principales mercados financieros en el mundo. Así, podemos extraer información sobre precios de acciones de diversas compañias. Para acceder a la información de google utilizares el símbolo 'goog'
; para el caso de Apple el símbolo es 'aapl'
. Puedes chacar más índices en el siguiente Link.
Para extraer información deberemos de utilizar primero el método Ticker()
, donde dentro del paréntesis colocaremos la cadena de texto con el símbolo de nuestro interés. Por ejemplo, para google tenemos:
# importación necesaria
import yfinance as yf
# Accedemos a la info de google
goog = yf.Ticker('goog')
Luego, para obtener los precios históricos de las acciones de dicha compañia utilizamos el método history()
al cual le pasaremos 3 parámetros:
Por ejemplo, extraigamos la información de los precios de las acciones de google para todo el 2021
# configuramos el intervalo en 1 dia (1d)
# para obtener los precios diarios
df_goog = goog.history(interval='1d', start='2021-01-01', end='2021-12-31')
df_goog.head()
Open | High | Low | Close | Volume | Dividends | Stock Splits | |
---|---|---|---|---|---|---|---|
Date | |||||||
2021-01-04 | 87.876999 | 88.032501 | 85.392502 | 86.412003 | 38038000 | 0 | 0 |
2021-01-05 | 86.250000 | 87.383499 | 85.900749 | 87.045998 | 22906000 | 0 | 0 |
2021-01-06 | 85.131500 | 87.400002 | 84.949997 | 86.764503 | 52042000 | 0 | 0 |
2021-01-07 | 87.002998 | 89.419998 | 86.852501 | 89.362503 | 45300000 | 0 | 0 |
2021-01-08 | 89.399002 | 90.491997 | 88.676750 | 90.360497 | 41012000 | 0 | 0 |
y lo que obtenemos con ello es un dataframe con la siguiente información importante:
Veamos un gráfico para Date versus Open:
# resetamos el indice y cambiamos el nombre de la columna resultante
df_goog = df_goog.reset_index().rename(columns={'index': 'Date'})
# graficamos
ax = df_goog.plot(x='Date', y='Open')
Podemos graficar Date versus cada una de las demás variables del dataframe (a excepción de Dividend y Stock Splits):
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme(style="darkgrid")
sns.lineplot(x = "Date", y = "Open", data = df_goog, label='Open')
sns.lineplot(x = "Date", y = "Close", data = df_goog, label='Close')
sns.lineplot(x = "Date", y = "Low", data = df_goog, label='Low')
sns.lineplot(x = "Date", y = "High", data = df_goog, label='High')
plt.show()
Ahora realizaremos el modelo de la red neuronal, así como le hicimos en la clase pasada, para realizar la predicción del precio de cierre (Close) de las acciones de google. Dicha predicción será sobre los precios de las acciones de diciembre del 2022.
Ahora bien, para realizar la predicción ocuparemos las variables Open, High y Low como variables predictorias y la variable Clase será la variable de respuesta. Así:
# inicialmente cargamos de nuevo los datos con la informacion
# del primero de enero del 2022 hasta el ultimo dia de noviembre
# de ese agno
df_goog = goog.history(interval='1d', start='2022-01-01', end='2022-11-30')
df_goog.head()
Open | High | Low | Close | Volume | Dividends | Stock Splits | |
---|---|---|---|---|---|---|---|
Date | |||||||
2022-01-03 | 144.475494 | 145.550003 | 143.502502 | 145.074493 | 25214000 | 0 | 0.0 |
2022-01-04 | 145.550507 | 146.610001 | 143.816147 | 144.416504 | 22928000 | 0 | 0.0 |
2022-01-05 | 144.181000 | 144.298004 | 137.523499 | 137.653503 | 49642000 | 0 | 0.0 |
2022-01-06 | 137.497498 | 139.686005 | 136.763504 | 137.550995 | 29050000 | 0 | 0.0 |
2022-01-07 | 137.904999 | 138.254745 | 135.789001 | 137.004501 | 19408000 | 0 | 0.0 |
# variables predictorias
X = df_goog.drop(['Dividends', 'Stock Splits', 'Volume', 'Close'], axis=1).values
# variable de respuesta
y = df_goog['Close'].values.reshape(-1,1)
Después realizamos la división de los datos
# Importacion necesaria
from sklearn.model_selection import train_test_split
# division de los datos
X_train, X_test, y_train, y_test = train_test_split(X, y, # conjunto completo
test_size=0.25, # 25% para pruebas
random_state=123) # semilla aleatoria
Y traeremos la función modelo()
definida en la clase anterior
# importaciones necesarias
from keras.models import Sequential
from keras.layers import Dense
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
import pandas as pd
def modelo(n1, n2, activacion, epocas, boolean):
"""
n1: número de neuronas en la primer capa
n2: número de neuronas en la segunda capa
activacion: función de activación en la última capa
epocas: número de épocas
boolean: booleano para determinar el valor de verbose
"""
# Instanciamos el modelo
model = Sequential()
# Capa de entrada de 1 neurona y capa oculta de n1 neuronas.
# Para la capa oculta utilizamos la funcion de activacion relu
model.add(Dense(n1, activation='relu', input_shape=(3,)))
# Capa oculta 2:
# n2 neuronas y funcion de activacion relu
model.add(Dense(n2, activation='relu'))
# Capa de salida con funcion de activacion "activacion"
model.add(Dense(1, activation=activacion))
# Compilacion. Utilizaremos ahora el error cuadratico medio
model.compile(optimizer='adam', loss='mean_squared_error')
# Entrenamiento
modelo = model.fit(X_train, y_train, epochs=epocas, verbose = boolean)
# prediccion
y_pred = model.predict(X_test)
# dataframe con la informacion
df = pd.DataFrame(y_pred).rename(columns = {0: 'y_pred'})
df['y_test'] = y_test
df = df.reset_index()
df['diff'] = abs(df.y_pred - df.y_test)
df['error'] = (df['diff'] * 100) / df['y_test']
# estadisticas
df_stats = df.describe()
# grafica
plt.plot(df.index, df.y_pred, color='blue', label='prediccioón')
plt.plot(df.index, df.y_test, color='red', label='real')
plt.legend()
plt.grid()
plt.show()
# MSE
mse = mean_squared_error(y_pred, y_test)
print(f'mse = {mse}')
# hacemos que la funcion retorne 4 valores
# |modelo de la red neuronal
return modelo, df, df_stats, model
Luego, utilizaremos los mismos parámetros (referentes a los de la clase anterior) para la función definida antes
modelo1, df1, df_stats1, model = modelo(40, 25, 'relu', 13, True)
df_stats1
Epoch 1/13 6/6 [==============================] - 0s 1ms/step - loss: 11172.4160 Epoch 2/13 6/6 [==============================] - 0s 2ms/step - loss: 8778.2803 Epoch 3/13 6/6 [==============================] - 0s 2ms/step - loss: 6640.5557 Epoch 4/13 6/6 [==============================] - 0s 1ms/step - loss: 4760.3193 Epoch 5/13 6/6 [==============================] - 0s 2ms/step - loss: 3164.4153 Epoch 6/13 6/6 [==============================] - 0s 2ms/step - loss: 1876.5985 Epoch 7/13 6/6 [==============================] - 0s 2ms/step - loss: 919.3132 Epoch 8/13 6/6 [==============================] - 0s 2ms/step - loss: 282.8372 Epoch 9/13 6/6 [==============================] - 0s 2ms/step - loss: 33.2030 Epoch 10/13 6/6 [==============================] - 0s 1ms/step - loss: 16.0118 Epoch 11/13 6/6 [==============================] - 0s 2ms/step - loss: 50.3454 Epoch 12/13 6/6 [==============================] - 0s 2ms/step - loss: 45.8178 Epoch 13/13 6/6 [==============================] - 0s 2ms/step - loss: 20.2811 2/2 [==============================] - 0s 2ms/step
mse = 8.290976731999825
index | y_pred | y_test | diff | error | |
---|---|---|---|---|---|
count | 58.000000 | 58.000000 | 58.000000 | 58.000000 | 58.000000 |
mean | 28.500000 | 120.414383 | 117.967120 | 2.458504 | 2.113276 |
std | 16.886879 | 16.744480 | 16.533656 | 1.512003 | 1.337491 |
min | 0.000000 | 87.110794 | 83.489998 | 0.009026 | 0.007565 |
25% | 14.250000 | 107.735193 | 105.547503 | 1.434198 | 1.224794 |
50% | 28.500000 | 119.347202 | 116.684498 | 2.241558 | 1.858940 |
75% | 42.750000 | 136.508518 | 135.368870 | 3.769310 | 3.229663 |
max | 57.000000 | 152.891418 | 148.036499 | 6.004059 | 5.293557 |
Notamos que el modelo obtenido es bastante bueno. Intentaremos mejorar el modelo anterior aumentando el número de épocas
modelo2, df2, df_stats2, model2 = modelo(40, 25, 'relu', 20, False)
df_stats2
2/2 [==============================] - 0s 2ms/step
mse = 1.780634913191095
index | y_pred | y_test | diff | error | |
---|---|---|---|---|---|
count | 58.000000 | 58.000000 | 58.000000 | 58.000000 | 58.000000 |
mean | 28.500000 | 117.840652 | 117.967120 | 1.091763 | 0.942146 |
std | 16.886879 | 16.403910 | 16.533656 | 0.773962 | 0.673513 |
min | 0.000000 | 85.007462 | 83.489998 | 0.006187 | 0.005394 |
25% | 14.250000 | 105.638510 | 105.547503 | 0.378515 | 0.354370 |
50% | 28.500000 | 116.582172 | 116.684498 | 0.947289 | 0.838506 |
75% | 42.750000 | 133.619869 | 135.368870 | 1.592861 | 1.362022 |
max | 57.000000 | 148.566010 | 148.036499 | 2.639015 | 2.273301 |
con lo que sí conseguimos una gran mejora en el modelo. Luego, realizaremos la predicción de los precios de las acciones para diciembre (hasta el 6 de diciembre), para ello
# extraemos la informacion
data2 = goog.history(interval='1d', start='2022-11-30', end='2022-12-06')
data2
Open | High | Low | Close | Volume | Dividends | Stock Splits | |
---|---|---|---|---|---|---|---|
Date | |||||||
2022-11-30 | 141.399994 | 148.720001 | 140.550003 | 148.029999 | 111224400 | 0 | 0 |
2022-12-01 | 148.210007 | 149.130005 | 146.610001 | 148.309998 | 71250400 | 0 | 0 |
2022-12-02 | 145.960007 | 148.000000 | 145.649994 | 147.809998 | 65421400 | 0 | 0 |
2022-12-05 | 147.770004 | 150.919998 | 145.770004 | 146.630005 | 68826400 | 0 | 0 |
# convertimos esos valores en un array
X_new = data2.drop(['Volume', 'Dividends', 'Stock Splits', 'Close'], axis=1).values
# hacemos las predicciones a partir del segundo modelo obtenido
y_pred = model2.predict(X_new)
1/1 [==============================] - 0s 21ms/step
Después, comparamos los valores reales con los predichos:
df_eval = pd.DataFrame(y_pred).rename(columns={0:'val_pred'})
df_eval['val_real'] = data2['Close'].values
df_eval
val_pred | val_real | |
---|---|---|
0 | 142.344238 | 148.029999 |
1 | 147.913437 | 148.309998 |
2 | 146.598312 | 147.809998 |
3 | 147.499329 | 146.630005 |
Podemos observar el porcentaje de error para cada predicción:
# valor absoluto de la diferencia entre el precio
# real y el predicho
df_eval['diff'] = abs(df_eval.val_real - df_eval.val_pred)
# calculo del porcentaje de error
df_eval['error (%)'] = (df_eval['diff'] * 100) / df_eval['val_real']
df_eval
val_pred | val_real | diff | error (%) | |
---|---|---|---|---|
0 | 142.344238 | 148.029999 | 5.685760 | 3.840952 |
1 | 147.913437 | 148.309998 | 0.396561 | 0.267386 |
2 | 146.598312 | 147.809998 | 1.211685 | 0.819759 |
3 | 147.499329 | 146.630005 | 0.869324 | 0.592869 |
Veamos que los errores cometidos no son muy grandes.
Finalmente, veamos el ajuste de los datos que la red neuronal hace, es decir, realicemos la predicción sobre el precio para el conjunto de datos de entrenamiento y prueba y comparemos.
# Conjunto de datos
X_data = df_goog[['Open', 'Low', 'High']].values
y_data = df_goog['Close'].values.reshape(-1,1)
# prediccion
y_pred = model2.predict(X_data)
8/8 [==============================] - 0s 1ms/step
Graficamos el gráfico de líneas de los datos originales y contrastamos con los datos predichos
# dataframe auxiliar
df_aux = pd.DataFrame(y_pred).rename(columns={0:'val_pred'})
df_aux['val_real'] = y_data
df_aux
val_pred | val_real | |
---|---|---|
0 | 145.598526 | 145.074493 |
1 | 146.572693 | 144.416504 |
2 | 144.005722 | 137.653503 |
3 | 139.325851 | 137.550995 |
4 | 138.420609 | 137.004501 |
... | ... | ... |
224 | 97.266556 | 97.330002 |
225 | 98.877586 | 98.820000 |
226 | 99.110260 | 97.599998 |
227 | 97.892242 | 96.250000 |
228 | 96.503128 | 95.440002 |
229 rows × 2 columns
df_aux = df_aux.reset_index()
sns.lineplot(x = "index", y = "val_real", data = df_aux, label='Real')
sns.lineplot(x = "index", y = "val_pred", data = df_aux, label='Predicción')
plt.show()
vemos que el ajuste hecho por la red neuronal es muy bueno y se acerca bastante al comportamiento real de los precios.