mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-30 05:52:23 +00:00
Merge pull request #132 from DidierRLopes/ets
This commit is contained in:
@@ -4,6 +4,8 @@ This menu aims to predict the share price of a pre-loaded stock, and the usage o
|
||||
|
||||
* [sma](#sma)
|
||||
- simple moving average
|
||||
* [ets](#ets)
|
||||
- Exponential Smoothing (e.g. Holt-Winters)
|
||||
* [knn](#knn)
|
||||
- k-Nearest Neighbors
|
||||
* [linear](#linear)
|
||||
@@ -38,6 +40,18 @@ Simple Moving Average:
|
||||
|
||||

|
||||
|
||||
## ets <a name="ets"></a>
|
||||
```
|
||||
usage: ets [-t TREND] [-s SEASONAL] [-p SEASONAL_PERIODS] [-d N_DAYS]
|
||||
```
|
||||
Exponential Smoothing (based on trend+seasonality, see https://otexts.com/fpp2/taxonomy.html):
|
||||
* -t : trend component: N: None, A: Additive, Ad: Additive Damped. Default N.
|
||||
* -s : seasonality component: N: None, A: Additive, M: Multiplicative. Default N.
|
||||
* -p : seasonal periods. Default 5.
|
||||
* -d : prediciton days. Default 5.
|
||||
|
||||

|
||||
|
||||
## knn <a name="knn"></a>
|
||||
```
|
||||
usage: knn [-i N_INPUTS] [-d N_DAYS] [-j N_JUMPS] [-n N_NEIGHBORS]
|
||||
@@ -125,7 +139,7 @@ Facebook's Prophet:
|
||||
|
||||
## mlp <a name="mlp"></a>
|
||||
```
|
||||
usage: mlp [-d N_DAYS] [-i N_INPUTS] [-j N_JUMPS] [-e N_EPOCHS] [-p {normalization,standardization,none}]
|
||||
usage: mlp [-d N_DAYS] [-i N_INPUTS] [-j N_JUMPS] [-e N_EPOCHS] [-p {normalization,standardization,none}]
|
||||
[-o {adam,adagrad,adadelta,adamax,ftrl,nadam,optimizer,rmsprop,sgd}] [-l {mae,mape,mse,msle}]
|
||||
```
|
||||
MulitLayer Perceptron:
|
||||
@@ -140,15 +154,15 @@ MulitLayer Perceptron:
|
||||
Due to the complexity of defining a model through command line, one can define it in: [config_neural_network_models.txt](/config_neural_network_models.py)
|
||||
```
|
||||
MultiLayer_Perceptron \
|
||||
= [ {'Dense':
|
||||
= [ {'Dense':
|
||||
{'units':50, 'activation':'relu'} },
|
||||
{'Dense':
|
||||
{'Dense':
|
||||
{'units':100, 'activation':'relu'} },
|
||||
{'Dense':
|
||||
{'Dense':
|
||||
{'units':80, 'activation':'relu'} },
|
||||
{'Dense':
|
||||
{'Dense':
|
||||
{'units':30, 'activation':'relu'} },
|
||||
{'Dense':
|
||||
{'Dense':
|
||||
{'activation':'linear'} }]
|
||||
```
|
||||
|
||||
@@ -156,7 +170,7 @@ MultiLayer_Perceptron \
|
||||
|
||||
## rnn <a name="rnn"></a>
|
||||
```
|
||||
usage: rnn [-d N_DAYS] [-i N_INPUTS] [-j N_JUMPS] [-e N_EPOCHS] [-p {normalization,standardization,none}]
|
||||
usage: rnn [-d N_DAYS] [-i N_INPUTS] [-j N_JUMPS] [-e N_EPOCHS] [-p {normalization,standardization,none}]
|
||||
[-o {adam,adagrad,adadelta,adamax,ftrl,nadam,optimizer,rmsprop,sgd}] [-l {mae,mape,mse,msle}]
|
||||
```
|
||||
Recurrent Neural Network:
|
||||
@@ -171,15 +185,15 @@ Recurrent Neural Network:
|
||||
Due to the complexity of defining a model through command line, one can define it in: [config_neural_network_models.txt](/config_neural_network_models.py)
|
||||
```
|
||||
Recurrent_Neural_Network \
|
||||
= [ {'SimpleRNN':
|
||||
= [ {'SimpleRNN':
|
||||
{'units':100, 'activation':'linear', 'return_sequences':True} },
|
||||
{'SimpleRNN':
|
||||
{'SimpleRNN':
|
||||
{'units':50, 'activation':'linear', 'return_sequences':True} },
|
||||
{'Dropout':
|
||||
{'Dropout':
|
||||
{'rate':0.2} },
|
||||
{'SimpleRNN':
|
||||
{'SimpleRNN':
|
||||
{'units':21, 'activation':'linear', 'return_sequences':False} },
|
||||
{'Dense':
|
||||
{'Dense':
|
||||
{'activation':'linear'} }]
|
||||
```
|
||||
|
||||
@@ -187,7 +201,7 @@ Recurrent_Neural_Network \
|
||||
|
||||
## lstm <a name="lstm"></a>
|
||||
```
|
||||
usage: lstm [-d N_DAYS] [-i N_INPUTS] [-j N_JUMPS] [-e N_EPOCHS] [-p {normalization,standardization,none}]
|
||||
usage: lstm [-d N_DAYS] [-i N_INPUTS] [-j N_JUMPS] [-e N_EPOCHS] [-p {normalization,standardization,none}]
|
||||
[-o {adam,adagrad,adadelta,adamax,ftrl,nadam,optimizer,rmsprop,sgd}] [-l {mae,mape,mse,msle}]
|
||||
```
|
||||
Long-Short Term Memory:
|
||||
@@ -202,11 +216,11 @@ Long-Short Term Memory:
|
||||
Due to the complexity of defining a model through command line, one can define it in: [config_neural_network_models.txt](/config_neural_network_models.py)
|
||||
```
|
||||
Long_Short_Term_Memory \
|
||||
= [ {'LSTM':
|
||||
= [ {'LSTM':
|
||||
{'units':25, 'activation':'tanh', 'return_sequences':True} },
|
||||
{'LSTM':
|
||||
{'LSTM':
|
||||
{'units':15, 'activation':'tanh', 'return_sequences':False} },
|
||||
{'Dense':
|
||||
{'Dense':
|
||||
{'activation':'linear'} }]
|
||||
```
|
||||
|
||||
|
||||
304
gamestonk_terminal/prediction_techniques/ets.py
Normal file
304
gamestonk_terminal/prediction_techniques/ets.py
Normal file
@@ -0,0 +1,304 @@
|
||||
import argparse
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from pandas.plotting import register_matplotlib_converters
|
||||
from statsmodels.tsa.holtwinters import ExponentialSmoothing
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
check_positive,
|
||||
get_next_stock_market_days,
|
||||
parse_known_args_and_warn,
|
||||
print_pretty_prediction,
|
||||
)
|
||||
|
||||
register_matplotlib_converters()
|
||||
|
||||
trend = "Ad" # Additive damped, A Additive, N None
|
||||
seasonal = "N" # None, A Additive, M Multiplicative
|
||||
seasonal_periods = 5
|
||||
|
||||
|
||||
def check_valid_trend(trend: str) -> str:
|
||||
if trend == "N" or trend == "A" or trend == "Ad":
|
||||
return trend
|
||||
raise argparse.ArgumentTypeError(
|
||||
"Invalid trend selected. Choose between 'N', 'A', and 'Ad'"
|
||||
)
|
||||
|
||||
|
||||
def check_valid_seasonal(seasonal: str) -> str:
|
||||
if seasonal == "N" or seasonal == "A" or seasonal == "M":
|
||||
return seasonal
|
||||
raise argparse.ArgumentTypeError(
|
||||
"Invalid seasonal selected. Choose between 'N', 'A', and 'M'"
|
||||
)
|
||||
|
||||
|
||||
def exponential_smoothing(l_args, s_ticker, df_stock):
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="ets",
|
||||
description="""
|
||||
Exponential Smoothing, see https://otexts.com/fpp2/taxonomy.html
|
||||
|
||||
Trend='N', Seasonal='N': Simple Exponential Smoothing
|
||||
Trend='N', Seasonal='A': Exponential Smoothing
|
||||
Trend='N', Seasonal='M': Exponential Smoothing
|
||||
Trend='A', Seasonal='N': Holt’s linear method
|
||||
Trend='A', Seasonal='A': Additive Holt-Winters’ method
|
||||
Trend='A', Seasonal='M': Multiplicative Holt-Winters’ method
|
||||
Trend='Ad', Seasonal='N': Additive damped trend method
|
||||
Trend='Ad', Seasonal='A': Exponential Smoothing
|
||||
Trend='Ad', Seasonal='M': Holt-Winters’ damped method
|
||||
|
||||
Trend component: N: None, A: Additive, Ad: Additive Damped
|
||||
Seasonality component: N: None, A: Additive, M: Multiplicative
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--days",
|
||||
action="store",
|
||||
dest="n_days",
|
||||
type=check_positive,
|
||||
default=5,
|
||||
help="prediction days.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--trend",
|
||||
action="store",
|
||||
dest="trend",
|
||||
type=check_valid_trend,
|
||||
default="N",
|
||||
help="Trend component: N: None, A: Additive, Ad: Additive Damped.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--seasonal",
|
||||
action="store",
|
||||
dest="seasonal",
|
||||
type=check_valid_seasonal,
|
||||
default="N",
|
||||
help="Seasonality component: N: None, A: Additive, M: Multiplicative.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--periods",
|
||||
action="store",
|
||||
dest="seasonal_periods",
|
||||
type=check_positive,
|
||||
default=5,
|
||||
help="Seasonal periods.",
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
|
||||
model, title = get_exponential_smoothing_model(
|
||||
df_stock["5. adjusted close"].values,
|
||||
ns_parser.trend,
|
||||
ns_parser.seasonal,
|
||||
ns_parser.seasonal_periods,
|
||||
)
|
||||
|
||||
if model.mle_retvals.success:
|
||||
forecast = model.forecast(ns_parser.n_days)
|
||||
|
||||
l_pred_days = get_next_stock_market_days(
|
||||
last_stock_day=df_stock["5. adjusted close"].index[-1],
|
||||
n_next_days=ns_parser.n_days,
|
||||
)
|
||||
df_pred = pd.Series(forecast, index=l_pred_days, name="Price")
|
||||
|
||||
if ~np.isnan(forecast).any():
|
||||
|
||||
print(f"\n{title}")
|
||||
print("\nFit model parameters:")
|
||||
for key, value in model.params.items():
|
||||
print(f"{key} {' '*(18-len(key))}: {value}")
|
||||
|
||||
print("\nAssess fit model:")
|
||||
print(f"AIC: {round(model.aic, 2)}")
|
||||
print(f"BIC: {round(model.bic, 2)}")
|
||||
print(f"SSE: {round(model.sse, 2)}\n")
|
||||
|
||||
# Plotting
|
||||
plt.figure()
|
||||
plt.plot(df_stock.index, df_stock["5. adjusted close"], lw=2)
|
||||
plt.title(f"{title} on {s_ticker}")
|
||||
|
||||
plt.xlim(
|
||||
df_stock.index[0],
|
||||
get_next_stock_market_days(df_pred.index[-1], 1)[-1],
|
||||
)
|
||||
plt.xlabel("Time")
|
||||
plt.ylabel("Share Price ($)")
|
||||
plt.grid(b=True, which="major", color="#666666", linestyle="-")
|
||||
plt.minorticks_on()
|
||||
plt.grid(
|
||||
b=True, which="minor", color="#999999", linestyle="-", alpha=0.2
|
||||
)
|
||||
plt.plot(
|
||||
[df_stock.index[-1], df_pred.index[0]],
|
||||
[df_stock["5. adjusted close"].values[-1], df_pred.values[0]],
|
||||
lw=1,
|
||||
c="tab:green",
|
||||
linestyle="--",
|
||||
)
|
||||
plt.plot(df_pred.index, df_pred, lw=2, c="tab:green")
|
||||
plt.axvspan(
|
||||
df_stock.index[-1],
|
||||
df_pred.index[-1],
|
||||
facecolor="tab:orange",
|
||||
alpha=0.2,
|
||||
)
|
||||
_, _, ymin, ymax = plt.axis()
|
||||
plt.vlines(
|
||||
df_stock.index[-1],
|
||||
ymin,
|
||||
ymax,
|
||||
linewidth=1,
|
||||
linestyle="--",
|
||||
color="k",
|
||||
)
|
||||
plt.ion()
|
||||
plt.show()
|
||||
|
||||
# Print prediction data
|
||||
print_pretty_prediction(
|
||||
df_pred, df_stock["5. adjusted close"].values[-1]
|
||||
)
|
||||
print("")
|
||||
|
||||
else:
|
||||
print("RuntimeWarning: invalid value encountered in double_scalars.")
|
||||
else:
|
||||
print("ConvergenceWarning: Optimization failed to converge.")
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("")
|
||||
|
||||
|
||||
def get_exponential_smoothing_model(data, trend, seasonal, seasonal_periods):
|
||||
if trend == "N": # None
|
||||
if seasonal == "N": # None
|
||||
title = "Trend='N', Seasonal='N': Simple Exponential Smoothing"
|
||||
ETS = ExponentialSmoothing(
|
||||
data,
|
||||
trend=None,
|
||||
damped_trend=False,
|
||||
seasonal=None,
|
||||
)
|
||||
model = ETS.fit(
|
||||
smoothing_level=None, smoothing_trend=None, damping_trend=None
|
||||
)
|
||||
|
||||
elif seasonal == "A": # Additive
|
||||
title = "Trend='N', Seasonal='A': Exponential Smoothing"
|
||||
ETS = ExponentialSmoothing(
|
||||
data,
|
||||
trend=None,
|
||||
damped_trend=False,
|
||||
seasonal="add",
|
||||
seasonal_periods=seasonal_periods,
|
||||
)
|
||||
model = ETS.fit(smoothing_level=None, smoothing_seasonal=None)
|
||||
|
||||
elif seasonal == "M": # Multiplicative
|
||||
title = "Trend='N', Seasonal='M': Exponential Smoothing"
|
||||
ETS = ExponentialSmoothing(
|
||||
data,
|
||||
trend=None,
|
||||
damped_trend=False,
|
||||
seasonal="mul",
|
||||
seasonal_periods=seasonal_periods,
|
||||
)
|
||||
model = ETS.fit(smoothing_level=None, smoothing_seasonal=None)
|
||||
|
||||
elif trend == "A": # Additive
|
||||
if seasonal == "N": # None
|
||||
title = "Trend='A', Seasonal='N': Holt’s linear method"
|
||||
ETS = ExponentialSmoothing(
|
||||
data,
|
||||
trend="add",
|
||||
damped_trend=False,
|
||||
seasonal=None,
|
||||
)
|
||||
model = ETS.fit(
|
||||
smoothing_level=None, smoothing_trend=None, damping_trend=None
|
||||
)
|
||||
|
||||
elif seasonal == "A": # Additive
|
||||
title = "Trend='A', Seasonal='A': Additive Holt-Winters’ method"
|
||||
ETS = ExponentialSmoothing(
|
||||
data,
|
||||
trend="add",
|
||||
damped_trend=False,
|
||||
seasonal="add",
|
||||
seasonal_periods=seasonal_periods,
|
||||
)
|
||||
model = ETS.fit(
|
||||
smoothing_level=None, smoothing_trend=None, smoothing_seasonal=None
|
||||
)
|
||||
|
||||
elif seasonal == "M": # Multiplicative
|
||||
title = "Trend='A', Seasonal='M': Multiplicative Holt-Winters’ method"
|
||||
ETS = ExponentialSmoothing(
|
||||
data,
|
||||
trend="add",
|
||||
damped_trend=False,
|
||||
seasonal="mul",
|
||||
seasonal_periods=seasonal_periods,
|
||||
)
|
||||
model = ETS.fit(
|
||||
smoothing_level=None, smoothing_trend=None, smoothing_seasonal=None
|
||||
)
|
||||
|
||||
elif trend == "Ad": # Additive damped
|
||||
if seasonal == "N": # None
|
||||
title = "Trend='Ad', Seasonal='N': Additive damped trend method"
|
||||
ETS = ExponentialSmoothing(
|
||||
data,
|
||||
trend="add",
|
||||
damped_trend=True,
|
||||
seasonal=None,
|
||||
)
|
||||
model = ETS.fit(
|
||||
smoothing_level=None, smoothing_trend=None, damping_trend=None
|
||||
)
|
||||
|
||||
elif seasonal == "A": # Additive
|
||||
title = "Trend='Ad', Seasonal='A': Exponential Smoothing"
|
||||
ETS = ExponentialSmoothing(
|
||||
data,
|
||||
trend="add",
|
||||
damped_trend=True,
|
||||
seasonal="add",
|
||||
seasonal_periods=seasonal_periods,
|
||||
)
|
||||
model = ETS.fit(
|
||||
smoothing_level=None,
|
||||
smoothing_trend=None,
|
||||
damping_trend=None,
|
||||
smoothing_seasonal=None,
|
||||
)
|
||||
|
||||
elif seasonal == "M": # Multiplicative
|
||||
title = "Trend='Ad', Seasonal='M': Holt-Winters’ damped method"
|
||||
ETS = ExponentialSmoothing(
|
||||
data,
|
||||
trend="add",
|
||||
damped_trend=True,
|
||||
seasonal="mul",
|
||||
seasonal_periods=seasonal_periods,
|
||||
)
|
||||
model = ETS.fit(
|
||||
smoothing_level=None,
|
||||
smoothing_trend=None,
|
||||
damping_trend=None,
|
||||
smoothing_seasonal=None,
|
||||
)
|
||||
|
||||
return model, title
|
||||
@@ -1,6 +1,7 @@
|
||||
import argparse
|
||||
|
||||
from gamestonk_terminal.prediction_techniques import sma
|
||||
from gamestonk_terminal.prediction_techniques import ets
|
||||
from gamestonk_terminal.prediction_techniques import knn
|
||||
from gamestonk_terminal.prediction_techniques import regression
|
||||
from gamestonk_terminal.prediction_techniques import arima
|
||||
@@ -27,6 +28,7 @@ def print_prediction(s_ticker, s_start, s_interval):
|
||||
print(" quit quit to abandon program")
|
||||
print("")
|
||||
print(" sma simple moving average")
|
||||
print(" ets exponential smoothing (e.g. Holt-Winters)")
|
||||
print(" knn k-Nearest Neighbors")
|
||||
print(" linear linear regression (polynomial 1)")
|
||||
print(" quadratic quadratic regression (polynomial 2)")
|
||||
@@ -51,6 +53,7 @@ def pred_menu(df_stock, s_ticker, s_start, s_interval):
|
||||
"q",
|
||||
"quit",
|
||||
"sma",
|
||||
"ets",
|
||||
"knn",
|
||||
"linear",
|
||||
"quadratic",
|
||||
@@ -96,6 +99,9 @@ def pred_menu(df_stock, s_ticker, s_start, s_interval):
|
||||
elif ns_known_args.cmd == "sma":
|
||||
sma.simple_moving_average(l_args, s_ticker, df_stock)
|
||||
|
||||
elif ns_known_args.cmd == "ets":
|
||||
ets.exponential_smoothing(l_args, s_ticker, df_stock)
|
||||
|
||||
elif ns_known_args.cmd == "knn":
|
||||
knn.k_nearest_neighbors(l_args, s_ticker, df_stock)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user