Files
OpenBB/openbb_terminal/forecast/forecast_controller.py
martinb-bb 9d2509a000 smape
2023-02-08 16:10:31 -05:00

3219 lines
112 KiB
Python

"""Forecast Controller Module"""
__docformat__ = "numpy"
# pylint: disable=too-many-lines, too-many-branches, too-many-arguments, R0904,R0902,W0707
# flake8: noqa
import argparse
import logging
from typing import Any, Dict, List, Optional
try:
import darts
import torch
darts_latest = "0.23.0"
# check darts version
if darts.__version__ != darts_latest:
print(f"You are currently using Darts version {darts.__version__}")
print(
"Follow instructions on creating a new conda environment with the latest "
f"Darts version ({darts_latest}):"
)
print("https://docs.openbb.co/terminal/quickstart/installation")
except ModuleNotFoundError:
raise ModuleNotFoundError(
"Please install the forecast version of the terminal. Instructions can be found "
"under the python tab: https://docs.openbb.co/terminal/quickstart/installation"
)
import pandas as pd
import psutil
from openbb_terminal import feature_flags as obbff
from openbb_terminal.common import common_model
from openbb_terminal.core.config.paths import (
USER_CUSTOM_IMPORTS_DIRECTORY,
USER_EXPORTS_DIRECTORY,
)
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
from openbb_terminal.decorators import log_start_end
from openbb_terminal.forecast import (
anom_view,
autoarima_view,
autoces_view,
autoets_view,
autoselect_view,
brnn_view,
expo_model,
expo_view,
forecast_model,
forecast_view,
helpers,
linregr_view,
mstl_view,
nbeats_view,
nhits_view,
regr_view,
rnn_view,
rwd_view,
seasonalnaive_view,
tcn_view,
tft_view,
theta_view,
trans_view,
)
from openbb_terminal.helper_funcs import (
EXPORT_ONLY_FIGURES_ALLOWED,
EXPORT_ONLY_RAW_DATA_ALLOWED,
NO_EXPORT,
check_positive,
check_positive_float,
log_and_raise,
valid_date,
)
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
from openbb_terminal.rich_config import MenuText, console
logger = logging.getLogger(__name__)
empty_df = pd.DataFrame()
def check_greater_than_one(value) -> int:
"""Argparse type to check positive int above 1"""
new_value = int(value)
if new_value <= 1:
log_and_raise(
argparse.ArgumentTypeError(
f"{value} is an invalid positive int value. Must be greater than 1."
)
)
return new_value
class ForecastController(BaseController):
"""Forecast class"""
CHOICES_COMMANDS: List[str] = [
"load",
"show",
"plot",
"clean",
"combine",
"desc",
"corr",
"rename",
"delete",
"export",
"ema",
"sto",
"rsi",
"roc",
"mom",
"delta",
"atr",
"signal",
"autoselect",
"autoarima",
"autoces",
"autoets",
"mstl",
"rwd",
"seasonalnaive",
"expo",
"theta",
"rnn",
"brnn",
"nbeats",
"tcn",
"regr",
"linregr",
"trans",
"tft",
"season",
"which",
"nhits",
"anom",
]
pandas_plot_choices = [
"line",
"scatter",
"bar",
"barh",
"hist",
"box",
"kde",
"area",
"pie",
"hexbin",
]
disclaimer = """
All models are for educational purposes only. The techniques available in this menu are not
fine tuned or guaranteed to work. Backtesting results is not a guarantee of future accuracy.
Investing involves monetary risk that OpenBB does not take a role in. Please research any
prediction techniques fully before attempting to use. OpenBB is not liable for any loss or
damages."""
PATH = "/forecast/"
CHOICES_GENERATION = True
loaded_dataset_cols = "\n"
list_dataset_cols: list = list()
def __init__(
self, ticker: str = "", data: pd.DataFrame = empty_df, queue: List[str] = None
):
"""Constructor"""
super().__init__(queue)
self.files: List[str] = []
self.choices = {}
# The full file name with extension, this allows the rest command to work
self.files_full: List[List[str]] = []
self.datasets: Dict[str, pd.DataFrame] = dict()
self.MINIMUM_DATA_LENGTH = 100
if ticker and not data.empty:
data = data.reset_index()
data.columns = data.columns.map(lambda x: x.lower().replace(" ", "_"))
self.files.append(ticker)
self.datasets[ticker] = data
self.loaded_dataset_cols = "\n"
self.loaded_dataset_cols += (
f" {ticker} {(20 - len(ticker)) * ' '}: "
f"{', '.join(data.columns)}\n"
)
for col in data.columns:
self.list_dataset_cols.append(f"{ticker}.{col}")
self.DATA_TYPES: List[str] = ["int", "float", "str", "bool", "category", "date"]
self.signs: Dict[Any, Any] = {
"div": "/",
"mul": "*",
"add": "+",
"sub": "-",
"mod": "%",
"pow": "**",
}
self.DATA_FILES = forecast_model.get_default_files()
# setting device on GPU if available, else CPU
self.device = "cuda" if torch.cuda.is_available() else "cpu"
self.comp_ram = f"{round(psutil.virtual_memory().total / (1024.0 ** 3),2)}G"
self.rec_data_size = f"{(psutil.virtual_memory().total / (1024.0 ** 3))//3}G"
self.torch_version = torch.__version__
self.darts_version = darts.__version__
if session and obbff.USE_PROMPT_TOOLKIT:
choices: dict = self.choices_default
self.choices = choices
self.completer = NestedCompleter.from_nested_dict(choices)
def call_exit(self, _) -> None:
"""Process exit terminal command from forecast menu."""
self.save_class()
for _ in range(self.PATH.count("/") + 1):
self.queue.insert(0, "quit")
def get_dataset_columns(self):
return {
f"{dataset}.{column}": {column: None, dataset: None}
for dataset, dataframe in self.datasets.items()
for column in dataframe.columns
}
def update_runtime_choices(self):
# Load in any newly exported files
self.DATA_FILES = forecast_model.get_default_files()
if session and obbff.USE_PROMPT_TOOLKIT:
choices: dict = self.choices_default
self.choices = choices
self.completer = NestedCompleter.from_nested_dict(choices)
def refresh_datasets_on_menu(self):
"""Refresh datasets on menu with new columns when adding new features"""
self.list_dataset_cols = list()
maxfile = max(len(file) for file in self.files)
self.loaded_dataset_cols = "\n"
for dataset, data in self.datasets.items():
self.loaded_dataset_cols += (
f" {dataset} {(maxfile - len(dataset)) * ' '}: "
f"{', '.join(data.columns)}\n"
)
for col in data.columns:
self.list_dataset_cols.append(f"{dataset}.{col}")
def print_help(self):
"""Print help"""
mt = MenuText("forecast/")
mt.add_param("_disclaimer_", self.disclaimer)
mt.add_raw("\n")
mt.add_param(
"_data_loc",
f"\n\t{USER_EXPORTS_DIRECTORY}\n\t{USER_CUSTOM_IMPORTS_DIRECTORY}",
)
mt.add_raw("\n")
mt.add_cmd("load")
mt.add_raw("\n")
mt.add_param("_loaded", self.loaded_dataset_cols)
mt.add_info("_exploration_")
mt.add_cmd("show", self.files)
mt.add_cmd("plot", self.files)
mt.add_cmd("clean", self.files)
mt.add_cmd("combine", self.files)
mt.add_cmd("desc", self.files)
mt.add_cmd("corr", self.files)
mt.add_cmd("season", self.files)
mt.add_cmd("delete", self.files)
mt.add_cmd("rename", self.files)
mt.add_cmd("export", self.files)
mt.add_raw("\n")
mt.add_info("_feateng_")
mt.add_cmd("ema", self.files)
mt.add_cmd("sto", self.files)
mt.add_cmd("rsi", self.files)
mt.add_cmd("roc", self.files)
mt.add_cmd("mom", self.files)
mt.add_cmd("delta", self.files)
mt.add_cmd("atr", self.files)
mt.add_cmd("signal", self.files)
mt.add_raw("\n")
mt.add_info("_tsforecasting_")
mt.add_cmd("autoselect", self.files)
mt.add_cmd("autoarima", self.files)
mt.add_cmd("autoces", self.files)
mt.add_cmd("autoets", self.files)
mt.add_cmd("mstl", self.files)
mt.add_cmd("rwd", self.files)
mt.add_cmd("seasonalnaive", self.files)
mt.add_cmd("expo", self.files)
mt.add_cmd("theta", self.files)
mt.add_cmd("linregr", self.files)
mt.add_cmd("regr", self.files)
mt.add_cmd("rnn", self.files)
mt.add_cmd("brnn", self.files)
mt.add_cmd("nbeats", self.files)
mt.add_cmd("nhits", self.files)
mt.add_cmd("tcn", self.files)
mt.add_cmd("trans", self.files)
mt.add_cmd("tft", self.files)
mt.add_raw("\n")
mt.add_info("_anomaly_")
mt.add_cmd("anom", self.files)
console.print(text=mt.menu_text, menu="Forecast")
def custom_reset(self):
"""Class specific component of reset command"""
if self.files_full:
queue = ["forecast"]
for file, alias in self.files_full:
load = f"'load {file}"
if alias:
load += f" -a {alias}'"
else:
load += "'"
queue.append(load)
return queue
return []
def add_standard_args(
self,
parser: argparse.ArgumentParser,
target_dataset: bool = False,
target_column: bool = False,
period: Optional[int] = None,
n_days: bool = False,
seasonal: Optional[str] = None,
periods: bool = False,
window: bool = False,
train_split: bool = False,
input_chunk_length: bool = False,
output_chunk_length: bool = False,
force_reset: bool = False,
save_checkpoints: bool = False,
model_save_name: Optional[str] = None,
n_epochs: bool = False,
model_type: bool = False,
dropout: Optional[float] = None,
batch_size: Optional[int] = None,
learning_rate: bool = False,
past_covariates: bool = False,
all_past_covariates: bool = False,
lags: bool = False,
hidden_size: int = 0,
n_jumps: bool = False,
end: bool = False,
start: bool = False,
residuals: bool = False,
forecast_only: bool = False,
naive: bool = False,
explainability_raw: bool = False,
export_pred_raw: bool = False,
metric: bool = False,
):
if hidden_size:
parser.add_argument(
"--hidden-size",
action="store",
dest="hidden_size",
default=hidden_size,
type=check_positive,
help="Size for feature maps for each hidden RNN layer (h_n)",
)
if past_covariates:
parser.add_argument(
"--past-covariates",
action="store",
dest="past_covariates",
default=None,
type=str,
help="Past covariates(columns/features) in same dataset. Comma separated.",
)
if all_past_covariates:
parser.add_argument(
"--all-past-covariates",
action="store_true",
dest="all_past_covariates",
default=False,
help="Adds all rows as past covariates except for date and the target column.",
)
if naive:
parser.add_argument(
"--naive",
action="store_true",
dest="naive",
default=False,
help="Show the naive baseline for a model.",
)
if target_dataset:
parser.add_argument(
"-d",
"--dataset",
help="The name of the dataset you want to select",
dest="target_dataset",
type=str,
choices=list(self.datasets.keys()),
)
if target_column:
parser.add_argument(
"-c",
"--target-column",
help="The name of the specific column you want to use",
dest="target_column",
type=str,
default="close",
)
if period is not None:
parser.add_argument(
"--period",
help="The period to use",
dest="period",
type=check_greater_than_one,
default=period,
)
if n_days:
parser.add_argument(
"-n",
"--n-days",
action="store",
dest="n_days",
type=check_positive,
default=5,
help="prediction days.",
)
if seasonal is not None:
parser.add_argument(
"-s",
"--seasonal",
action="store",
dest="seasonal",
choices=expo_model.SEASONS,
default=seasonal,
help="Seasonality: N: None, A: Additive, M: Multiplicative.",
)
if periods:
parser.add_argument(
"-p",
"--periods",
action="store",
dest="seasonal_periods",
type=check_positive,
default=7,
help="Seasonal periods: 4: Quarterly, 7: Daily",
)
if window:
parser.add_argument(
"-w",
"--window",
action="store",
dest="start_window",
default=0.85,
type=check_positive_float,
help="Start point for rolling training and forecast window. 0.0-1.0",
)
if train_split:
parser.add_argument(
"-t",
"--train-split",
action="store",
dest="train_split",
default=0.85,
type=check_positive_float,
help="Start point for rolling training and forecast window. 0.0-1.0",
)
if input_chunk_length:
parser.add_argument(
"-i",
"--input-chunk-length",
action="store",
dest="input_chunk_length",
default=14,
type=check_positive,
help="Number of past time steps for forecasting module at prediction time.",
)
if output_chunk_length:
parser.add_argument(
"-o",
"--output-chunk-length",
action="store",
dest="output_chunk_length",
default=5,
type=check_positive,
help="The length of the forecast of the model.",
)
if force_reset:
parser.add_argument(
"--force-reset",
action="store",
dest="force_reset",
default=True,
type=bool,
help="""If set to True, any previously-existing model with the same name will be reset
(all checkpoints will be discarded).""",
)
if save_checkpoints:
parser.add_argument(
"--save-checkpoints",
action="store",
dest="save_checkpoints",
default=True,
type=bool,
help="Whether to automatically save the untrained model and checkpoints.",
)
if model_save_name is not None:
parser.add_argument(
"--model-save-name",
type=str,
action="store",
dest="model_save_name",
default=model_save_name,
help="Name of the model to save.",
)
if n_epochs:
parser.add_argument(
"--n-epochs",
action="store",
dest="n_epochs",
default=300,
type=check_positive,
help="Number of epochs over which to train the model.",
)
if model_type:
parser.add_argument(
"--model-type",
type=str,
action="store",
dest="model_type",
default="LSTM",
help='Enter a string specifying the RNN module type ("RNN", "LSTM" or "GRU")',
)
if dropout is not None:
parser.add_argument(
"--dropout",
action="store",
dest="dropout",
default=dropout,
type=check_positive_float,
help="Fraction of neurons affected by Dropout, from 0 to 1.",
)
if batch_size is not None:
parser.add_argument(
"--batch-size",
action="store",
dest="batch_size",
default=batch_size,
type=check_positive,
help="Number of time series (input and output) used in each training pass",
)
if end:
parser.add_argument(
"--end",
action="store",
type=valid_date,
dest="s_end_date",
default=None,
help="The end date (format YYYY-MM-DD) to select for testing",
)
if start:
parser.add_argument(
"--start",
action="store",
type=valid_date,
dest="s_start_date",
default=None,
help="The start date (format YYYY-MM-DD) to select for testing",
)
if learning_rate:
parser.add_argument(
"--learning-rate",
action="store",
dest="learning_rate",
default=1e-3,
type=check_positive_float,
help="Learning rate during training.",
)
if n_jumps:
parser.add_argument(
"-j",
"--jumps",
action="store",
dest="n_jumps",
type=check_positive,
default=1,
help="number of jumps in training data.",
)
if lags:
parser.add_argument(
"--lags",
action="store",
dest="lags",
type=check_greater_than_one,
default=14,
help="Lagged target values used to predict the next time step.",
)
if residuals:
parser.add_argument(
"--residuals",
help="Show the residuals for the model.",
action="store_true",
default=False,
dest="residuals",
)
if forecast_only:
parser.add_argument(
"--forecast-only",
help="Do not plot the historical data without forecasts.",
action="store_true",
default=False,
dest="forecast_only",
)
if explainability_raw:
parser.add_argument(
"--explainability-raw",
action="store_true",
dest="explainability_raw",
default=False,
help="Prints out a raw dataframe showing explainability results.",
)
if export_pred_raw:
parser.add_argument(
"--export-pred-raw",
action="store_true",
dest="export_pred_raw",
default=False,
help="Export predictions to a csv file.",
)
if metric:
parser.add_argument(
"--metric",
type=str,
action="store",
dest="metric",
default="mape",
choices=["rmse", "mse", "mape", "smape"],
help="Calculate precision based on a specific metric (rmse, mse, mape)",
)
# if user does not put in --dataset
return parser
def load(self, ticker: str, data: pd.DataFrame):
"""Loads news dataframes into memory"""
# check if data has minimum number of rows
if len(data) < self.MINIMUM_DATA_LENGTH:
console.print(
f"[red]Dataset is smaller than recommended minimum {self.MINIMUM_DATA_LENGTH} data points. [/red]"
)
console.print(
f"[red]Please increase the number of data points for [ {ticker} ] and try again.[/red]"
)
return
if not data.empty:
data.columns = data.columns.map(lambda x: x.lower().replace(" ", "_"))
# If the index is a date, move this into a normal column
if data.index.name == "date":
data = data.reset_index()
# Convert date to datetime
# TODO: for now we drop time, once we add handling for time remove this
if "date" in data.columns:
data["date"] = data["date"].apply(helpers.dt_format)
data["date"] = pd.to_datetime(data["date"])
# if we import a custom dataset, remove the old index "unnamed:_0"
if "unnamed:_0" in data.columns:
data = data.drop(columns=["unnamed:_0"])
self.files.append(ticker)
self.datasets[ticker] = data
# Process new datasets to be updated
self.list_dataset_cols = list()
maxfile = max(len(file) for file in self.files)
self.loaded_dataset_cols = "\n"
for dataset, x_data in self.datasets.items():
self.loaded_dataset_cols += (
f" {dataset} {(maxfile - len(dataset)) * ' '}: "
f"{', '.join(x_data.columns)}\n"
)
for col in data.columns:
self.list_dataset_cols.append(f"{dataset}.{col}")
@log_start_end(log=logger)
def call_load(self, other_args: List[str]):
"""Process load"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="load",
description="Load custom dataset (from previous export, custom imports).",
)
parser.add_argument(
"-f",
"--file",
help="File to load data in (can be custom import, may have been exported before.)",
choices=self.DATA_FILES.keys(),
type=str,
)
parser.add_argument(
"-a",
"--alias",
help="Alias name to give to the dataset",
type=str,
)
# Load in any newly exported files
self.DATA_FILES = forecast_model.get_default_files()
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-f")
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
if ns_parser.file:
file = ns_parser.file
if ns_parser.alias:
alias = ns_parser.alias
else:
if "." in ns_parser.file:
alias = ".".join(ns_parser.file.split(".")[:-1])
else:
alias = ns_parser.file
# check if this dataset has been added already
if alias in self.files:
console.print(
"[red]The file/dataset selected has already been loaded.[/red]\n"
)
return
data = common_model.load(file, self.DATA_FILES, {})
if not data.empty:
self.files_full.append([ns_parser.file, ns_parser.alias])
self.load(alias, data)
self.update_runtime_choices()
# Show selected dataframe on console
@log_start_end(log=logger)
def call_which(self, other_args: List[str]):
"""Process which command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="which",
description="Show library versions of required packages.",
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
)
if ns_parser:
console.print(
f"[green]Current Compute Device (CPU or GPU):[/green] {self.device.upper()}"
)
console.print(f"[green]Current RAM:[/green] {self.comp_ram}")
console.print(
f"[green]Recommended Max dataset size based on current RAM:[/green] {self.rec_data_size}"
)
console.print(f"[green]Torch version:[/green] {self.torch_version}")
console.print(f"[green]Darts version:[/green] {self.darts_version}")
# Show selected dataframe on console
@log_start_end(log=logger)
def call_show(self, other_args: List[str]):
"""Process show command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="show",
description="Show a portion of the DataFrame",
)
parser.add_argument(
"-n",
"--name",
type=str,
choices=self.files,
dest="name",
help="The name of the database you want to show data for",
)
parser.add_argument(
"-s",
"--sortby",
help="Sort based on a column in the DataFrame",
nargs="+",
type=str,
dest="sortby",
default="",
)
parser.add_argument(
"-r",
"--reverse",
action="store_true",
dest="reverse",
default=False,
help=(
"Data is sorted in descending order by default. "
"Reverse flag will sort it in an ascending way. "
"Only works when raw data is displayed."
),
)
parser.add_argument(
"--limit-col",
action="store",
dest="limit_col",
default=10,
type=check_positive,
help="Set the number of columns to display when showing the dataset",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-n")
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
EXPORT_ONLY_RAW_DATA_ALLOWED,
limit=10,
)
if ns_parser:
if not ns_parser.name:
dataset_names = list(self.datasets.keys())
else:
dataset_names = [ns_parser.name]
for name in dataset_names:
df = self.datasets[name]
if name in self.datasets and self.datasets[name].empty:
console.print(
f"[red]No data available for {ns_parser.name}.[/red]\n"
)
elif ns_parser.sortby:
sort_column = " ".join(ns_parser.sortby)
if sort_column not in self.datasets[name].columns:
console.print(
f"[red]{sort_column} not a valid column."
"Showing without sorting.\n[/red]"
)
else:
df = df.sort_values(by=sort_column, ascending=ns_parser.reverse)
forecast_view.show_df(
df, ns_parser.limit, ns_parser.limit_col, name, ns_parser.export
)
@log_start_end(log=logger)
def call_rename(self, other_args: List[str]):
"""Process rename"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="rename",
description="The column you want to rename from a dataset.",
)
parser.add_argument(
"-d",
"--dataset",
help="Dataset that will get a column renamed",
dest="dataset",
choices=self.datasets.keys(),
type=str,
)
parser.add_argument(
"-o",
"--oldcol",
help="Old column from dataset to be renamed",
dest="oldcol",
type=str,
required="-h" not in other_args,
)
parser.add_argument(
"-n",
"--newcol",
help="New column from dataset to be renamed",
dest="newcol",
type=str,
required="-h" not in other_args,
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-d")
ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT)
if ns_parser:
dataset = ns_parser.dataset
column_old = ns_parser.oldcol
column_new = ns_parser.newcol
if dataset not in self.datasets:
console.print(
f"Not able to find the dataset {dataset}. Please choose one of "
f"the following: {', '.join(self.datasets)}"
)
else:
self.datasets[dataset] = forecast_model.rename_column(
self.datasets[dataset], column_old, column_new
)
self.update_runtime_choices()
self.refresh_datasets_on_menu()
console.print(
f"[green]Successfully renamed {column_old} into {column_new}, in {dataset}[/green]"
)
# Show selected dataframe on console
@log_start_end(log=logger)
def call_desc(self, other_args: List[str]):
"""Process descriptive stats command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="desc",
description="Show descriptive statistics of a dataset",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(parser, target_dataset=True)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
EXPORT_ONLY_RAW_DATA_ALLOWED,
)
if ns_parser:
# check proper file name is provided
if not ns_parser.target_dataset:
console.print("[red]Please enter valid dataset.\n[/red]")
return
df = self.datasets[ns_parser.target_dataset]
forecast_view.describe_df(
df,
ns_parser.target_dataset,
ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_plot(self, other_args: List[str]):
"""Process plot command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="plot",
description="Plot data based on the index",
)
parser.add_argument(
"-v",
"--values",
help="Dataset.column values to be displayed in a plot. Use comma to separate multiple",
choices=self.get_dataset_columns(),
dest="values",
type=str,
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-v")
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED
)
if not ns_parser:
return
if not ns_parser.values:
console.print("[red]Please enter valid dataset.\n[/red]")
return
values = [x.strip() for x in ns_parser.values.split(",")]
target_df = values[0].split(".")[0]
if target_df not in self.datasets:
console.print("[red]Please enter valid dataset.\n[/red]")
return
for value in values:
if value.split(".")[0] != target_df:
console.print("[red]Please enter values from the same dataset.\n[/red]")
return
forecast_view.display_plot(
self.datasets[target_df],
[x.split(".")[1] for x in values],
ns_parser.export,
)
@log_start_end(log=logger)
def call_season(self, other_args: List[str]):
"""Process season command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="season",
description="The seasonality for a given column",
)
parser.add_argument(
"-v",
"--values",
help="Dataset.column values to be displayed in a plot",
dest="values",
choices=self.get_dataset_columns(),
type=str,
)
parser.add_argument(
"-m",
help="A time lag to highlight on the plot",
dest="m",
type=int,
default=None,
)
parser.add_argument(
"--max_lag",
help="The maximal lag order to consider",
dest="max_lag",
type=int,
default=24,
)
parser.add_argument(
"-a",
"--alpha",
help="The confidence interval to display",
dest="alpha",
type=float,
default=0.05,
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-v")
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED
)
if not ns_parser:
return
if not ns_parser.values:
console.print("[red]Please enter valid dataset.\n[/red]")
return
try:
dataset, col = ns_parser.values.split(".")
data = self.datasets[dataset]
except ValueError:
console.print("[red]Please enter 'dataset'.'column'.[/red]\n")
return
forecast_view.display_seasonality(
data=data,
column=col,
export=ns_parser.export,
m=ns_parser.m,
max_lag=ns_parser.max_lag,
alpha=ns_parser.alpha,
)
@log_start_end(log=logger)
def call_corr(self, other_args: List[str]):
"""Process correlation command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="corr",
description="Plot correlation coefficients.",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(parser, target_dataset=True)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
# check proper file name is provided
if not ns_parser.target_dataset:
console.print("[red]Please enter valid dataset.\n[/red]")
return
data = self.datasets[ns_parser.target_dataset]
forecast_view.display_corr(
data,
ns_parser.export,
)
@log_start_end(log=logger)
def call_combine(self, other_args: List[str]):
"""Process combine"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="combine",
description="Combine two entire datasets, or add specific columns. Add specific"
"columns with the syntax: <datasetX.column2>",
)
parser.add_argument(
"--dataset",
help="Dataset to add columns to",
dest="dataset",
choices=list(self.datasets.keys()),
)
# pylint: disable=consider-using-dict-items
column_choices = [
f"{x}.{y}" for x in self.datasets for y in self.datasets[x].columns
]
column_choices.extend(list(self.datasets))
parser.add_argument(
"-c",
"--columns",
help="The columns we want to add <dataset.column>",
dest="columns",
choices=column_choices,
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT)
if ns_parser:
if ns_parser.dataset not in self.datasets:
console.print(
f"Not able to find the dataset {ns_parser.dataset}. Please choose one of "
f"the following: {', '.join(self.datasets)}"
)
return
data = self.datasets[ns_parser.dataset]
for option in ns_parser.columns.split(","):
if "." in option:
dataset, col = option.split(".")
columns = [col]
else:
dataset = option
columns = [x for x in self.datasets[dataset].columns if x != "date"]
if dataset not in self.datasets:
console.print(
f"Not able to find the dataset {dataset}. Please choose one of "
f"the following: {', '.join(self.datasets)}"
)
continue
for column in columns:
data = forecast_model.combine_dfs(
data, self.datasets[dataset], column, dataset
)
self.datasets[ns_parser.dataset] = data
self.update_runtime_choices()
self.refresh_datasets_on_menu()
console.print(
f"[green]Successfully added {ns_parser.columns} into {ns_parser.dataset}[/green]"
)
@log_start_end(log=logger)
def call_clean(self, other_args: List[str]):
"""Process clean"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="clean",
description="Clean a dataset by filling and dropping NaN values.",
)
parser.add_argument(
"-f",
"--fill",
help="The method of filling NaNs. This has options to fill rows (rfill, rbfill, rffill) or fill "
"columns (cfill, cbfill, cffill). Furthermore, it has the option to forward fill and backward fill "
"(up to --limit) which refer to how many rows/columns can be set equal to the last non-NaN value",
dest="fill",
choices=["rfill", "cfill", "rbfill", "cbfill", "rffill", "bffill"],
default="",
)
parser.add_argument(
"--drop",
help="The method of dropping NaNs. This either has the option rdrop (drop rows that contain NaNs) "
"or cdrop (drop columns that contain NaNs)",
dest="drop",
choices=["rdrop", "cdrop"],
default="",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(parser, target_dataset=True)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
NO_EXPORT,
limit=5,
)
if ns_parser:
# check proper file name is provided
if not ns_parser.target_dataset:
console.print("[red]Please enter valid dataset.\n[/red]")
return
(
self.datasets[ns_parser.target_dataset],
clean_status,
) = forecast_model.clean(
self.datasets[ns_parser.target_dataset],
ns_parser.fill,
ns_parser.drop,
ns_parser.limit,
)
if not clean_status:
console.print(
f"[green]Successfully cleaned '{ns_parser.target_dataset}' dataset[/green]"
)
else:
console.print(f"[red]{ns_parser.name} still contains NaNs.[/red]")
@log_start_end(log=logger)
def call_ema(self, other_args: List[str]):
"""Process EMA"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="ema",
description="Add exponential moving average to dataset based on specific column.",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser, target_dataset=True, period=10, target_column=True
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
NO_EXPORT,
)
if ns_parser:
# check proper file name is provided
if not helpers.check_parser_input(ns_parser, self.datasets):
return
self.datasets[ns_parser.target_dataset] = forecast_model.add_ema(
self.datasets[ns_parser.target_dataset],
ns_parser.target_column,
ns_parser.period,
)
console.print(
f"Successfully added 'EMA_{ns_parser.period}' to '{ns_parser.target_dataset}' dataset"
)
# update forecast menu with new column on modified dataset
self.refresh_datasets_on_menu()
self.update_runtime_choices()
@log_start_end(log=logger)
def call_sto(self, other_args: List[str]):
"""Process Stochastic Oscillator"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="sto",
description="Add in Stochastic Oscillator %K and %D",
)
parser.add_argument(
"--close-col",
help="Close column name to use for Stochastic Oscillator",
dest="close_col",
type=str,
default="close",
)
parser.add_argument(
"--high-col",
help="High column name to use for Stochastic Oscillator",
dest="high_col",
type=str,
default="high",
)
parser.add_argument(
"--low-col",
help="Low column name to use for Stochastic Oscillator",
dest="low_col",
type=str,
default="low",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(parser, target_dataset=True, period=10)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, NO_EXPORT, limit=5
)
if ns_parser:
# check proper file name is provided
if not ns_parser.target_dataset:
console.print("[red]Please enter valid dataset.\n[/red]")
return
df = forecast_model.add_sto(
self.datasets[ns_parser.target_dataset],
close_column=ns_parser.close_col,
high_column=ns_parser.high_col,
low_column=ns_parser.low_col,
period=ns_parser.period,
)
if not df.empty:
self.datasets[ns_parser.target_dataset] = df
console.print(
f"Successfully added 'STOCH&D_{ns_parser.period}' to '{ns_parser.target_dataset}' dataset"
)
# update forecast menu with new column on modified dataset
self.refresh_datasets_on_menu()
self.update_runtime_choices()
def handle_delete(self, dataset, column):
if dataset not in self.datasets:
console.print(
f"Not able to find the dataset {dataset}. Please choose one of "
f"the following: {', '.join(self.datasets)}"
)
else:
forecast_model.delete_column(self.datasets[dataset], column)
@log_start_end(log=logger)
def call_delete(self, other_args: List[str]):
"""Process delete"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="delete",
description="The column you want to delete from a dataset.",
)
parser.add_argument(
"--delete",
help="The columns you want to delete from a dataset. Use format: <dataset.column> or"
" multiple with <dataset.column>,<datasetb.column2>",
dest="delete",
choices=self.get_dataset_columns(),
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--delete")
ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT)
if ns_parser:
if "," in ns_parser.delete:
for option in ns_parser.delete:
print(option)
dataset, column = option.split(".")
self.handle_delete(dataset, column)
else:
dataset, column = ns_parser.delete.split(".")
self.handle_delete(dataset, column)
self.update_runtime_choices()
self.refresh_datasets_on_menu()
console.print(f"[green]Successfully deleted {ns_parser.delete}[/green]")
@log_start_end(log=logger)
def call_rsi(self, other_args: List[str]):
"""Process RSI"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="rsi",
description="Add rsi to dataset based on specific column.",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser, target_dataset=True, target_column=True, period=10
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
NO_EXPORT,
)
if ns_parser:
# check proper file name is provided
if not ns_parser.target_dataset:
console.print("[red]Please enter valid dataset.\n[/red]")
return
self.datasets[ns_parser.target_dataset] = forecast_model.add_rsi(
self.datasets[ns_parser.target_dataset],
ns_parser.target_column,
ns_parser.period,
)
console.print(
f"Successfully added 'RSI_{ns_parser.period}_{ns_parser.target_column}' "
f"to '{ns_parser.target_dataset}' dataset"
)
# update forecast menu with new column on modified dataset
self.refresh_datasets_on_menu()
self.update_runtime_choices()
@log_start_end(log=logger)
def call_roc(self, other_args: List[str]):
"""Process ROC"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="roc",
description="Add rate of change to dataset based on specific column.",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser, target_dataset=True, target_column=True, period=10
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
NO_EXPORT,
)
if ns_parser:
# check proper file name is provided
if not helpers.check_parser_input(ns_parser, self.datasets):
return
self.datasets[ns_parser.target_dataset] = forecast_model.add_roc(
self.datasets[ns_parser.target_dataset],
ns_parser.target_column,
ns_parser.period,
)
console.print(
f"Successfully added 'ROC_{ns_parser.period}' to '{ns_parser.target_dataset}' dataset"
)
# update forecast menu with new column on modified dataset
self.refresh_datasets_on_menu()
self.update_runtime_choices()
@log_start_end(log=logger)
def call_mom(self, other_args: List[str]):
"""Process Momentum"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="mom",
description="Add momentum to dataset based on specific column.",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser, target_dataset=True, target_column=True, period=10
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
NO_EXPORT,
)
if ns_parser:
# check proper file name is provided
if not helpers.check_parser_input(ns_parser, self.datasets):
return
self.datasets[ns_parser.target_dataset] = forecast_model.add_momentum(
self.datasets[ns_parser.target_dataset],
ns_parser.target_column,
ns_parser.period,
)
console.print(
f"Successfully added 'Momentum_{ns_parser.period}' to '{ns_parser.target_dataset}' dataset"
)
# update forecast menu with new column on modified dataset
self.refresh_datasets_on_menu()
self.update_runtime_choices()
@log_start_end(log=logger)
def call_delta(self, other_args: List[str]):
"""Process %Change (Delta)"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="delta",
description="Add %Change (Delta) to dataset based on specific column.",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(parser, target_dataset=True, target_column=True)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
NO_EXPORT,
)
if ns_parser:
# check proper file name is provided
if not helpers.check_parser_input(ns_parser, self.datasets):
return
self.datasets[ns_parser.target_dataset] = forecast_model.add_delta(
self.datasets[ns_parser.target_dataset], ns_parser.target_column
)
console.print(
f"Successfully added 'Delta_{ns_parser.target_column}' to '{ns_parser.target_dataset}' dataset"
)
# update forecast menu with new column on modified dataset
self.refresh_datasets_on_menu()
self.update_runtime_choices()
@log_start_end(log=logger)
def call_atr(self, other_args: List[str]):
"""Process Average True Range"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="atr",
description="Add Average True Range to dataset of specific stock ticker.",
)
parser.add_argument(
"--close-col",
help="Close column name to use for Average True Range.",
dest="close_col",
type=str,
default="close",
)
parser.add_argument(
"--high-col",
help="High column name to use for Average True Range.",
dest="high_col",
type=str,
default="high",
)
parser.add_argument(
"--low-col",
help="Low column name to use for Average True Range.",
dest="low_col",
type=str,
default="low",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(parser, target_dataset=True, target_column=True)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
NO_EXPORT,
)
if ns_parser:
# check proper file name is provided
if not helpers.check_parser_input(ns_parser, self.datasets):
return
self.datasets[ns_parser.target_dataset] = forecast_model.add_atr(
self.datasets[ns_parser.target_dataset],
close_column=ns_parser.close_col,
high_column=ns_parser.high_col,
low_column=ns_parser.low_col,
)
# check if true range was added
if "true_range" in self.datasets[ns_parser.target_dataset].columns:
console.print(
f"Successfully added 'Average True Range' to '{ns_parser.target_dataset}' dataset"
)
else:
console.print(
"Could not add 'Average True Range' as it does not have one/all specific columns (low/close/high)"
)
# update forecast menu with new column on modified dataset
self.refresh_datasets_on_menu()
self.update_runtime_choices()
@log_start_end(log=logger)
def call_signal(self, other_args: List[str]):
"""Process Price Signal"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="signal",
description="""Add price signal to dataset based on closing price.
1 if the signal is that short term price will go up as compared to the long term.
0 if the signal is that short term price will go down as compared to the long term.
""",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(parser, target_dataset=True, target_column=True)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
NO_EXPORT,
)
if ns_parser:
# check proper file name is provided
if not helpers.check_parser_input(ns_parser, self.datasets):
return
self.datasets[ns_parser.target_dataset] = forecast_model.add_signal(
self.datasets[ns_parser.target_dataset],
target_column=ns_parser.target_column,
)
console.print(
f"Successfully added 'Price Signal' to '{ns_parser.target_dataset}' dataset"
)
# update forecast menu with new column on modified dataset
self.refresh_datasets_on_menu()
self.update_runtime_choices()
@log_start_end(log=logger)
def call_export(self, other_args: List[str]):
"""Process export command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="export",
description="Export dataset to Excel",
)
parser.add_argument(
"-t",
"--type",
help="The file type you wish to export to",
dest="type",
choices=common_model.file_types,
type=str,
default="xlsx",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(parser, target_dataset=True)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=NO_EXPORT,
)
if not helpers.check_parser_input(ns_parser, self.datasets, "ignore_column"):
return
forecast_view.export_df(
self.datasets[ns_parser.target_dataset],
ns_parser.type,
ns_parser.target_dataset,
ns_parser.sheet_name,
)
# Best Statistical Model
@log_start_end(log=logger)
def call_autoselect(self, other_args: List[str]):
"""Process autoselect command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="autoselect",
description="""
Perform Automatic Statistical Forecast
(select best statistical model from AutoARIMA, AutoETS, AutoCES, MSTL, ...)
""",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
target_column=True,
n_days=True,
seasonal="A",
periods=True,
window=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
# TODO Convert this to multi series
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
autoselect_view.display_autoselect_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
seasonal_periods=ns_parser.seasonal_periods,
start_window=ns_parser.start_window,
forecast_horizon=ns_parser.n_days,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
)
# AutoARIMA Model
@log_start_end(log=logger)
def call_autoarima(self, other_args: List[str]):
"""Process autoarima command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="autoarima",
description="""
Perform Automatic ARIMA forecast:
https://nixtla.github.io/statsforecast/examples/getting_started_with_auto_arima_and_ets.html
""",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
target_column=True,
n_days=True,
seasonal="A",
periods=True,
window=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
# TODO Convert this to multi series
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
autoarima_view.display_autoarima_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
seasonal_periods=ns_parser.seasonal_periods,
start_window=ns_parser.start_window,
forecast_horizon=ns_parser.n_days,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
)
# AutoCES Model
@log_start_end(log=logger)
def call_autoces(self, other_args: List[str]):
"""Process autoces command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="autoces",
description="""
Perform Automatic Complex Exponential Smoothing forecast:
https://nixtla.github.io/statsforecast/models.html#autoces
""",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
target_column=True,
n_days=True,
seasonal="A",
periods=True,
window=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
# TODO Convert this to multi series
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
autoces_view.display_autoces_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
seasonal_periods=ns_parser.seasonal_periods,
start_window=ns_parser.start_window,
forecast_horizon=ns_parser.n_days,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
)
# AutoETS Model
@log_start_end(log=logger)
def call_autoets(self, other_args: List[str]):
"""Process autoets command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="autoets",
description="""
Perform Automatic ETS (Error, Trend, Seasonality) forecast:
https://nixtla.github.io/statsforecast/examples/getting_started_with_auto_arima_and_ets.html
""",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
target_column=True,
n_days=True,
seasonal="A",
periods=True,
window=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
# TODO Convert this to multi series
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
autoets_view.display_autoets_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
seasonal_periods=ns_parser.seasonal_periods,
start_window=ns_parser.start_window,
forecast_horizon=ns_parser.n_days,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
)
# MSTL Model
@log_start_end(log=logger)
def call_mstl(self, other_args: List[str]):
"""Process mstl command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="mstl",
description="""
Perform Multiple Seasonalities and Trend using Loess (MSTL) forecast:
https://nixtla.github.io/statsforecast/examples/multipleseasonalities.html
""",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
target_column=True,
n_days=True,
seasonal="A",
periods=True,
window=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
# TODO Convert this to multi series
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
mstl_view.display_mstl_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
seasonal_periods=ns_parser.seasonal_periods,
start_window=ns_parser.start_window,
forecast_horizon=ns_parser.n_days,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
)
# RWD Model
@log_start_end(log=logger)
def call_rwd(self, other_args: List[str]):
"""Process rwd command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="rwd",
description="""
Perform Random Walk with Drift forecast:
https://nixtla.github.io/statsforecast/models.html#randomwalkwithdrift
""",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
target_column=True,
n_days=True,
seasonal="A",
periods=False,
window=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
# TODO Convert this to multi series
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
rwd_view.display_rwd_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
start_window=ns_parser.start_window,
forecast_horizon=ns_parser.n_days,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
)
# SeasonalNaive Model
@log_start_end(log=logger)
def call_seasonalnaive(self, other_args: List[str]):
"""Process seasonalnaive command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="seasonalnaive",
description="""
Perform SeasonalNaive forecasting:
https://nixtla.github.io/statsforecast/models.html#seasonalnaive
""",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
target_column=True,
n_days=True,
seasonal="A",
periods=True,
window=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
# TODO Convert this to multi series
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
seasonalnaive_view.display_seasonalnaive_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
seasonal_periods=ns_parser.seasonal_periods,
start_window=ns_parser.start_window,
forecast_horizon=ns_parser.n_days,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
)
# EXPO Model
@log_start_end(log=logger)
def call_expo(self, other_args: List[str]):
"""Process expo command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="expo",
description="""
Perform Probabilistic Exponential Smoothing forecast:
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.exponential_smoothing.html
""",
)
parser.add_argument(
"--trend",
action="store",
dest="trend",
choices=expo_model.TRENDS,
default="A",
help="Trend: N: None, A: Additive, M: Multiplicative.",
)
parser.add_argument(
"--dampen",
action="store",
dest="dampen",
default="F",
help="Dampening",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
target_column=True,
n_days=True,
seasonal="A",
periods=True,
window=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
# TODO Convert this to multi series
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
expo_view.display_expo_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
trend=ns_parser.trend,
seasonal=ns_parser.seasonal,
seasonal_periods=ns_parser.seasonal_periods,
dampen=ns_parser.dampen,
start_window=ns_parser.start_window,
forecast_horizon=ns_parser.n_days,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_theta(self, other_args: List[str]):
"""Process theta command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="theta",
description="""
Perform Theta forecast:
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.theta.html
""",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
n_days=True,
target_column=True,
seasonal="M",
periods=True,
window=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
theta_view.display_theta_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
seasonal=ns_parser.seasonal,
seasonal_periods=ns_parser.seasonal_periods,
start_window=ns_parser.start_window,
forecast_horizon=ns_parser.n_days,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_rnn(self, other_args: List[str]):
"""Process RNN command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="rnn",
description="""
Perform RNN forecast (Vanilla RNN, LSTM, GRU):
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.rnn_model.html
""",
)
# RNN Hyperparameters
parser.add_argument(
"--hidden-dim",
action="store",
dest="hidden_dim",
default=20,
type=check_positive,
help="Size for feature maps for each hidden RNN layer (h_n)",
)
parser.add_argument(
"--training_length",
action="store",
dest="training_length",
default=20,
type=check_positive,
help="""The length of both input (target and covariates) and output (target) time series used during training.
Generally speaking, training_length should have a higher value than input_chunk_length because otherwise
during training the RNN is never run for as many iterations as it will during training.""",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
n_days=True,
target_column=True,
train_split=True,
input_chunk_length=True,
force_reset=True,
save_checkpoints=True,
model_save_name="rnn_model",
n_epochs=True,
model_type=True,
dropout=0,
batch_size=32,
learning_rate=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
rnn_view.display_rnn_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
train_split=ns_parser.train_split,
forecast_horizon=ns_parser.n_days,
model_type=ns_parser.model_type,
hidden_dim=ns_parser.hidden_dim,
dropout=ns_parser.dropout,
batch_size=ns_parser.batch_size,
n_epochs=ns_parser.n_epochs,
learning_rate=ns_parser.learning_rate,
model_save_name=ns_parser.model_save_name,
training_length=ns_parser.training_length,
input_chunk_size=ns_parser.input_chunk_length,
force_reset=ns_parser.force_reset,
save_checkpoints=ns_parser.save_checkpoints,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_nbeats(self, other_args: List[str]):
"""Process NBEATS command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="nbeats",
description="""
Perform NBEATS forecast (Neural Bayesian Estimation of Time Series):
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.nbeats.html
""",
)
# NBEATS Hyperparameters
parser.add_argument(
"--num_stacks",
action="store",
dest="num_stacks",
default=10,
type=check_positive_float,
help="The number of stacks that make up the whole model.",
)
parser.add_argument(
"--num_blocks",
action="store",
dest="num_blocks",
default=3,
type=check_positive,
help="The number of blocks making up every stack.",
)
parser.add_argument(
"--num_layers",
action="store",
dest="num_layers",
default=4,
type=check_positive,
help="""The number of fully connected layers preceding the final forking layers
in each block of every stack.""",
)
parser.add_argument(
"--layer_widths",
action="store",
dest="layer_widths",
default=512,
type=check_positive,
help="""Determines the number of neurons that make up each fully connected layer
in each block of every stack""",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
n_days=True,
target_column=True,
train_split=True,
input_chunk_length=True,
output_chunk_length=True,
save_checkpoints=True,
force_reset=True,
model_save_name="nbeats_model",
learning_rate=True,
n_epochs=True,
batch_size=800,
past_covariates=True,
all_past_covariates=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
covariates = helpers.clean_covariates(
ns_parser, self.datasets[ns_parser.target_dataset]
)
nbeats_view.display_nbeats_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
past_covariates=covariates,
train_split=ns_parser.train_split,
forecast_horizon=ns_parser.n_days,
input_chunk_length=ns_parser.input_chunk_length,
output_chunk_length=ns_parser.output_chunk_length,
num_stacks=ns_parser.num_stacks,
num_blocks=ns_parser.num_blocks,
num_layers=ns_parser.num_layers,
layer_widths=ns_parser.layer_widths,
batch_size=ns_parser.batch_size,
n_epochs=ns_parser.n_epochs,
learning_rate=ns_parser.learning_rate,
model_save_name=ns_parser.model_save_name,
force_reset=ns_parser.force_reset,
save_checkpoints=ns_parser.save_checkpoints,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_tcn(self, other_args: List[str]):
"""Process TCN command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="tcn",
description="""
Perform TCN forecast:
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.tcn_model.html
""",
)
# TCN Hyperparameters
parser.add_argument(
"--num-filters",
action="store",
dest="num_filters",
default=3,
type=check_positive,
help="The number of filters in a convolutional layer of the TCN",
)
parser.add_argument(
"--weight-norm",
action="store",
dest="weight_norm",
default=True,
type=bool,
help="Boolean value indicating whether to use weight normalization.",
)
parser.add_argument(
"--dilation-base",
action="store",
dest="dilation_base",
default=2,
type=check_positive,
help="The base of the exponent that will determine the dilation on every level.",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
target_dataset=True,
n_days=True,
target_column=True,
train_split=True,
past_covariates=True,
all_past_covariates=True,
save_checkpoints=True,
force_reset=True,
model_save_name="tcn_model",
learning_rate=True,
n_epochs=True,
dropout=0.1,
batch_size=32,
input_chunk_length=True,
output_chunk_length=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
# check proper file name is provided
if not helpers.check_parser_input(ns_parser, self.datasets):
return
covariates = helpers.clean_covariates(
ns_parser, self.datasets[ns_parser.target_dataset]
)
tcn_view.display_tcn_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
past_covariates=covariates,
train_split=ns_parser.train_split,
forecast_horizon=ns_parser.n_days,
input_chunk_length=ns_parser.input_chunk_length,
output_chunk_length=ns_parser.output_chunk_length,
dropout=ns_parser.dropout,
num_filters=ns_parser.num_filters,
weight_norm=ns_parser.weight_norm,
dilation_base=ns_parser.dilation_base,
batch_size=ns_parser.batch_size,
n_epochs=ns_parser.n_epochs,
learning_rate=ns_parser.learning_rate,
model_save_name=ns_parser.model_save_name,
force_reset=ns_parser.force_reset,
save_checkpoints=ns_parser.save_checkpoints,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_regr(self, other_args: List[str]):
"""Process REGR command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="regr",
description="""
Perform a regression forecast:
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.regression_model.html
""",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
output_chunk_length=True,
train_split=True,
past_covariates=True,
all_past_covariates=True,
n_days=True,
target_dataset=True,
target_column=True,
lags=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
explainability_raw=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
# check proper file name is provided
if not helpers.check_parser_input(ns_parser, self.datasets):
return
covariates = helpers.clean_covariates(
ns_parser, self.datasets[ns_parser.target_dataset]
)
regr_view.display_regression(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
past_covariates=covariates,
train_split=ns_parser.train_split,
forecast_horizon=ns_parser.n_days,
output_chunk_length=ns_parser.output_chunk_length,
lags=ns_parser.lags,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
explainability_raw=ns_parser.explainability_raw,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_linregr(self, other_args: List[str]):
"""Process LINREGR command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="linregr",
description="""
Perform a linear regression forecast:
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.linear_regression_model.html
""",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
lags=True,
output_chunk_length=True,
train_split=True,
past_covariates=True,
all_past_covariates=True,
target_column=True,
n_days=True,
target_dataset=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
explainability_raw=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
covariates = helpers.clean_covariates(
ns_parser, self.datasets[ns_parser.target_dataset]
)
linregr_view.display_linear_regression(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
past_covariates=covariates,
train_split=ns_parser.train_split,
forecast_horizon=ns_parser.n_days,
output_chunk_length=ns_parser.output_chunk_length,
lags=ns_parser.lags,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
explainability_raw=ns_parser.explainability_raw,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_brnn(self, other_args: List[str]):
"""Process BRNN command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="brnn",
description="""
Perform BRNN forecast (Vanilla RNN, LSTM, GRU):
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.block_rnn_model.html
""",
)
# BRNN Hyperparameters
parser.add_argument(
"--n-rnn-layers",
action="store",
dest="n_rnn_layers",
default=1,
type=check_positive,
help="Number of layers in the RNN module.",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
save_checkpoints=True,
force_reset=True,
model_save_name="brnn_model",
learning_rate=True,
n_epochs=True,
batch_size=32,
dropout=0,
input_chunk_length=True,
output_chunk_length=True,
model_type=True,
train_split=True,
past_covariates=True,
all_past_covariates=True,
target_dataset=True,
n_days=True,
target_column=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
covariates = helpers.clean_covariates(
ns_parser, self.datasets[ns_parser.target_dataset]
)
brnn_view.display_brnn_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
past_covariates=covariates,
train_split=ns_parser.train_split,
forecast_horizon=ns_parser.n_days,
input_chunk_length=ns_parser.input_chunk_length,
output_chunk_length=ns_parser.output_chunk_length,
model_type=ns_parser.model_type,
n_rnn_layers=ns_parser.n_rnn_layers,
dropout=ns_parser.dropout,
batch_size=ns_parser.batch_size,
n_epochs=ns_parser.n_epochs,
learning_rate=ns_parser.learning_rate,
model_save_name=ns_parser.model_save_name,
force_reset=ns_parser.force_reset,
save_checkpoints=ns_parser.save_checkpoints,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_trans(self, other_args: List[str]):
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="trans",
description="""
Perform Transformer Forecast:
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.transformer_model.html
""",
)
parser.add_argument(
"--d-model",
action="store",
dest="d_model",
default=64,
type=check_positive,
help="Number of expected features in inputs.",
)
parser.add_argument(
"--nhead",
action="store",
dest="nhead",
default=4,
type=check_positive,
help="Number of head in the attention mechanism.",
)
parser.add_argument(
"--num_encoder_layers",
action="store",
dest="num_encoder_layers",
default=3,
type=check_positive,
help="The number of encoder layers in the encoder.",
)
parser.add_argument(
"--num_decoder_layers",
action="store",
dest="num_decoder_layers",
default=3,
type=check_positive,
help="The number of decoder layers in the encoder.",
)
parser.add_argument(
"--dim_feedforward",
action="store",
dest="dim_feedforward",
default=512,
type=check_positive,
help="The dimension of the feedforward model.",
)
parser.add_argument(
"--activation",
action="store",
dest="activation",
default="relu",
choices=["relu", "gelu"],
type=str,
help="Number of LSTM layers.",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
n_days=True,
target_column=True,
target_dataset=True,
past_covariates=True,
all_past_covariates=True,
train_split=True,
input_chunk_length=True,
output_chunk_length=True,
dropout=0,
batch_size=32,
n_epochs=True,
model_save_name="trans_model",
force_reset=True,
save_checkpoints=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
covariates = helpers.clean_covariates(
ns_parser, self.datasets[ns_parser.target_dataset]
)
trans_view.display_trans_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
past_covariates=covariates,
train_split=ns_parser.train_split,
forecast_horizon=ns_parser.n_days,
input_chunk_length=ns_parser.input_chunk_length,
output_chunk_length=ns_parser.output_chunk_length,
d_model=ns_parser.d_model,
nhead=ns_parser.nhead,
num_encoder_layers=ns_parser.num_encoder_layers,
num_decoder_layers=ns_parser.num_decoder_layers,
dim_feedforward=ns_parser.dim_feedforward,
activation=ns_parser.activation,
dropout=ns_parser.dropout,
n_epochs=ns_parser.n_epochs,
batch_size=ns_parser.batch_size,
model_save_name=ns_parser.model_save_name,
force_reset=ns_parser.force_reset,
save_checkpoints=ns_parser.save_checkpoints,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_tft(self, other_args: List[str]):
"""Process TFT command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="tft",
description="""
Perform TFT forecast (Temporal Fusion Transformer):
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.tft_model.html
""",
)
parser.add_argument(
"--lstm-layers",
action="store",
dest="lstm_layers",
default=1,
type=check_positive,
help="Number of LSTM layers.",
)
parser.add_argument(
"--num-attention-heads",
action="store",
dest="num_attention_heads",
default=4,
type=check_positive,
help="Number of attention heads.",
)
parser.add_argument(
"--full-attention",
action="store_true",
dest="full_attention",
default=False,
help="Whether to apply a multi-head attention query.",
)
parser.add_argument(
"--hidden-continuous-size",
action="store",
dest="hidden_continuous_size",
default=8,
type=check_positive,
help="Default hidden size for processing continuous variables.",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
save_checkpoints=True,
target_dataset=True,
n_days=True,
force_reset=True,
model_save_name="tft_model",
train_split=True,
hidden_size=16,
batch_size=32,
n_epochs=True,
dropout=0.1,
input_chunk_length=True,
output_chunk_length=True,
past_covariates=True,
all_past_covariates=True,
target_column=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
covariates = helpers.clean_covariates(
ns_parser, self.datasets[ns_parser.target_dataset]
)
tft_view.display_tft_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
past_covariates=covariates,
train_split=ns_parser.train_split,
forecast_horizon=ns_parser.n_days,
input_chunk_length=ns_parser.input_chunk_length,
output_chunk_length=ns_parser.output_chunk_length,
hidden_size=ns_parser.hidden_size,
lstm_layers=ns_parser.lstm_layers,
num_attention_heads=ns_parser.num_attention_heads,
full_attention=ns_parser.full_attention,
dropout=ns_parser.dropout,
hidden_continuous_size=ns_parser.hidden_continuous_size,
n_epochs=ns_parser.n_epochs,
batch_size=ns_parser.batch_size,
model_save_name=ns_parser.model_save_name,
force_reset=ns_parser.force_reset,
save_checkpoints=ns_parser.save_checkpoints,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_nhits(self, other_args: List[str]):
"""Process nhits command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="nhits",
description="""
Perform nhits forecast:
https://unit8co.github.io/darts/generated_api/darts.models.forecasting.tft_model.html
""",
)
parser.add_argument(
"--num-stacks",
dest="num_stacks",
type=check_positive,
default=3,
help="The number of stacks that make up the model",
)
parser.add_argument(
"--num-blocks",
dest="num_blocks",
type=check_positive,
default=1,
help="The number of blocks making up every stack",
)
parser.add_argument(
"--num-layers",
dest="num_layers",
type=check_positive,
default=2,
help="The number of fully connected layers",
)
parser.add_argument(
"--layer_widths",
dest="layer_widths",
type=check_positive,
default=512,
help="The number of neurons in each layer",
)
parser.add_argument(
"--activation",
dest="activation",
type=str,
default="ReLU",
choices=[
"ReLU",
"RReLU",
"PReLU",
"Softplus",
"Tanh",
"SELU",
"LeakyReLU",
"Sigmoid",
],
help="The desired activation",
)
parser.add_argument(
"--max_pool_1d",
action="store_true",
dest="maxpool1d",
default=True,
help="Whether to use max_pool_1d or AvgPool1d",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
save_checkpoints=True,
target_dataset=True,
n_days=True,
force_reset=True,
model_save_name="nhits_model",
train_split=True,
dropout=0.1,
input_chunk_length=True,
output_chunk_length=True,
batch_size=32,
n_epochs=True,
past_covariates=True,
all_past_covariates=True,
target_column=True,
residuals=True,
forecast_only=True,
start=True,
end=True,
naive=True,
export_pred_raw=True,
metric=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
covariates = helpers.clean_covariates(
ns_parser, self.datasets[ns_parser.target_dataset]
)
nhits_view.display_nhits_forecast(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
n_predict=ns_parser.n_days,
target_column=ns_parser.target_column,
past_covariates=covariates,
train_split=ns_parser.train_split,
forecast_horizon=ns_parser.n_days,
input_chunk_length=ns_parser.input_chunk_length,
output_chunk_length=ns_parser.output_chunk_length,
num_stacks=ns_parser.num_stacks,
num_blocks=ns_parser.num_blocks,
num_layers=ns_parser.num_layers,
layer_widths=ns_parser.layer_widths,
activation=ns_parser.activation,
max_pool_1d=ns_parser.maxpool1d,
batch_size=ns_parser.batch_size,
n_epochs=ns_parser.n_epochs,
dropout=ns_parser.dropout,
model_save_name=ns_parser.model_save_name,
force_reset=ns_parser.force_reset,
save_checkpoints=ns_parser.save_checkpoints,
export=ns_parser.export,
residuals=ns_parser.residuals,
forecast_only=ns_parser.forecast_only,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
naive=ns_parser.naive,
export_pred_raw=ns_parser.export_pred_raw,
metric=ns_parser.metric,
)
@log_start_end(log=logger)
def call_anom(self, other_args: List[str]):
"""Process ANOM command"""
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False,
prog="anom",
description="""
Perform a Quantile Anomaly detection on a given dataset:
https://unit8co.github.io/darts/generated_api/darts.ad.detectors.quantile_detector.html
""",
)
# if user does not put in --dataset
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "--dataset")
parser = self.add_standard_args(
parser,
train_split=True,
target_column=True,
target_dataset=True,
forecast_only=True,
start=True,
end=True,
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
if not helpers.check_parser_input(ns_parser, self.datasets):
return
anom_view.display_anomaly_detection(
data=self.datasets[ns_parser.target_dataset],
dataset_name=ns_parser.target_dataset,
target_column=ns_parser.target_column,
train_split=ns_parser.train_split,
start_date=ns_parser.s_start_date,
end_date=ns_parser.s_end_date,
)