text: """Custom Quantitative Analysis Controller Module"""
  __docformat__ = "numpy"
  
  import argparse
  import logging
  from pathlib import Path
  from typing import List
  
  import pandas as pd
  from prompt_toolkit.completion import NestedCompleter
  
  from gamestonk_terminal import feature_flags as gtff
  from gamestonk_terminal.common.quantitative_analysis import qa_view, rolling_view
  from gamestonk_terminal.custom import custom_model
  from gamestonk_terminal.decorators import log_start_end
  from gamestonk_terminal.helper_funcs import (
      EXPORT_ONLY_FIGURES_ALLOWED,
      EXPORT_ONLY_RAW_DATA_ALLOWED,
      check_positive,
      check_proportion_range,
      parse_known_args_and_warn,
      check_list_dates,
  )
  from gamestonk_terminal.menu import session
  from gamestonk_terminal.parent_classes import BaseController
  from gamestonk_terminal.rich_config import console
  
  logger = logging.getLogger(__name__)
  
  
  class QaController(BaseController):
      """Quantitative Analysis Controller class"""
  
      CHOICES_COMMANDS = [
          "load",
          "pick",
          "raw",
          "summary",
          "line",
          "hist",
          "cdf",
          "bw",
          "rolling",
          "decompose",
          "cusum",
          "acf",
          "spread",
          "quantile",
          "skew",
          "kurtosis",
          "normality",
          "qqplot",
          "unitroot",
          "goodness",
          "unitroot",
          "capm",
      ]
  
      PATH = "/custom/qa/"
  
      def __init__(
          self,
          custom_df: pd.DataFrame,
          file: str,
          queue: List[str] = None,
      ):
          """Constructor"""
          super().__init__(queue)
          self.DATA_FILES = [file.name for file in Path("custom_imports").iterdir()]
  
          self.df = custom_df
          self.ticker = file
          self.target = custom_df.columns[1]
          self.file = file
  
          if session and gtff.USE_PROMPT_TOOLKIT:
              choices: dict = {c: {} for c in self.controller_choices}
              choices["pick"] = {c: None for c in list(custom_df.columns)}
              choices["load"] = {c: None for c in self.DATA_FILES}
              self.choices = choices
              self.completer = NestedCompleter.from_nested_dict(choices)
  
      def custom_reset(self):
          """Class specific component of reset command"""
          if self.ticker:
              if self.target:
                  return ["custom", f"load {self.file}", "qa", f"pick {self.target}"]
              return ["custom", f"load {self.file}", "qa"]
          return []
  
      def update_runtime_choices(self):
          if session and gtff.USE_PROMPT_TOOLKIT:
              self.choices["pick"] = {c: None for c in list(self.df.columns)}
          self.completer = NestedCompleter.from_nested_dict(self.choices)
  
      def print_help(self):
          """Print help"""
          help_text = f"""[cmds]
     load        load new data file
     pick        pick target column for analysis[/cmds]
  
  [param]File: [/param]{self.file}
  [param]Target Column: [/param]{self.target}
  [cmds]
  [info]Statistics:[/info]
      summary     brief summary statistics of loaded stock.
      normality   normality statistics and tests
      unitroot    unit root test for stationarity (ADF, KPSS)
  [info]Plots:[/info]
      line        line plot of selected target
      hist        histogram with density plot
      cdf         cumulative distribution function
      bw          box and whisker plot
      acf         (partial) auto-correlation function differentials of prices
      qqplot      residuals against standard normal curve
  [info]Rolling Metrics:[/info]
      rolling     rolling mean and std deviation of prices
      spread      rolling variance and std deviation of prices
      quantile    rolling median and quantile of prices
      skew        rolling skewness of distribution of prices
      kurtosis    rolling kurtosis of distribution of prices
  [info]Other:[/info]
      raw         print raw data
      decompose   decomposition in cyclic-trend, season, and residuals of prices
      cusum       detects abrupt changes using cumulative sum algorithm of prices[/cmds]
          """
          console.print(text=help_text, menu="Custom - Quantitative Analysis")
  
      @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 data set into a dataframe",
          )
          parser.add_argument(
              "-f",
              "--file",
              choices=self.DATA_FILES,
              help="File to load in.",
              default="test.csv",
              type=str,
          )
  
          if other_args and "-" not in other_args[0][0]:
              other_args.insert(0, "-f")
          ns_parser = parse_known_args_and_warn(parser, other_args)
          if ns_parser:
              file = Path("custom_imports") / ns_parser.file
              self.df = custom_model.load(file)
              self.df.columns = self.df.columns.map(lambda x: x.lower())
              for col in self.df.columns:
                  if col in ["date", "time", "timestamp"]:  # Could be others?
                      self.df[col] = pd.to_datetime(self.df[col])
                      self.df = self.df.set_index(col)
              self.file = ns_parser.file
              self.update_runtime_choices()
          console.print("")
  
      @log_start_end(log=logger)
      def call_pick(self, other_args: List[str]):
          """Process pick command"""
          parser = argparse.ArgumentParser(
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              add_help=False,
              prog="pick",
              description="""
                  Change target variable
              """,
          )
          parser.add_argument(
              "-t",
              "--target",
              dest="target",
              type=lambda x: x.lower(),
              choices=list(self.df.columns),
              help="Select variable to analyze",
          )
          if other_args and "-t" not in other_args and "-h" not in other_args:
              other_args.insert(0, "-t")
  
          ns_parser = parse_known_args_and_warn(parser, other_args)
          if ns_parser:
              self.target = ns_parser.target
          console.print("")
  
      @log_start_end(log=logger)
      def call_raw(self, other_args: List[str]):
          parser = argparse.ArgumentParser(
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              add_help=False,
              prog="raw",
              description="""
                  Print raw data to console
              """,
          )
          parser.add_argument(
              "-w",
              "--window",
              help="Number to show",
              type=check_positive,
              default=20,
              dest="limit",
          )
          parser.add_argument(
              "-d",
              "--descend",
              action="store_true",
              default=False,
              dest="descend",
              help="Sort in descending order",
          )
  
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              qa_view.display_raw(
                  self.df[self.target],
                  num=ns_parser.limit,
                  sort="",
                  des=ns_parser.descend,
                  export=ns_parser.export,
              )
  
      @log_start_end(log=logger)
      def call_summary(self, other_args: List[str]):
          """Process summary command"""
          parser = argparse.ArgumentParser(
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              add_help=False,
              prog="summary",
              description="""
                  Summary statistics
              """,
          )
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              qa_view.display_summary(df=self.df, export=ns_parser.export)
  
      @log_start_end(log=logger)
      def call_line(self, other_args: List[str]):
          """Process line command"""
          parser = argparse.ArgumentParser(
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              add_help=False,
              prog="line",
              description="Show line plot of selected data and allow to draw lines or highlight specific datetimes.",
          )
          parser.add_argument(
              "--log",
              help="Plot with y on log scale",
              dest="log",
              action="store_true",
              default=False,
          )
          parser.add_argument(
              "-d",
              "--draw",
              help="Draw lines and annotate on the plot",
              dest="draw",
              action="store_true",
              default=False,
          )
          parser.add_argument(
              "--ml",
              help="Draw vertical line markers to highlight certain events",
              dest="ml",
              type=check_list_dates,
              default="",
          )
          parser.add_argument(
              "--ms",
              help="Draw scatter markers to highlight certain events",
              dest="ms",
              type=check_list_dates,
              default="",
          )
  
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED
          )
          if ns_parser:
              qa_view.display_line(
                  self.df[self.target],
                  title=f"{self.ticker} {self.target}",
                  log_y=ns_parser.log,
                  draw=ns_parser.draw,
                  markers_lines=ns_parser.ml,
                  markers_scatter=ns_parser.ms,
              )
  
      @log_start_end(log=logger)
      def call_hist(self, other_args: List[str]):
          """Process hist command"""
          parser = argparse.ArgumentParser(
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              add_help=False,
              prog="hist",
              description="""
                  Histogram with density and rug
              """,
          )
          parser.add_argument(
              "-b", "--bins", type=check_positive, default=15, dest="n_bins"
          )
          ns_parser = parse_known_args_and_warn(parser, other_args)
          if ns_parser:
              qa_view.display_hist(
                  name=self.ticker,
                  df=self.df,
                  target=self.target,
                  bins=ns_parser.n_bins,
              )
  
      @log_start_end(log=logger)
      def call_cdf(self, other_args: List[str]):
          """Process cdf command"""
          parser = argparse.ArgumentParser(
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              add_help=False,
              prog="cdf",
              description="""
                  Cumulative distribution function
              """,
          )
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              qa_view.display_cdf(
                  name=self.ticker,
                  df=self.df,
                  target=self.target,
                  export=ns_parser.export,
              )
  
      @log_start_end(log=logger)
      def call_bw(self, other_args: List[str]):
          """Process bwy command"""
          parser = argparse.ArgumentParser(
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              add_help=False,
              prog="bw",
              description="""
                  Box and Whisker plot
              """,
          )
          parser.add_argument(
              "-y",
              "--yearly",
              action="store_true",
              default=False,
              dest="year",
              help="Flag to show yearly bw plot",
          )
          ns_parser = parse_known_args_and_warn(parser, other_args)
          if ns_parser:
              qa_view.display_bw(
                  name=self.ticker,
                  df=self.df,
                  target=self.target,
                  yearly=ns_parser.year,
              )
  
      @log_start_end(log=logger)
      def call_decompose(self, other_args: List[str]):
          """Process decompose command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="decompose",
              description="""
                  Decompose time series as:
                  - Additive Time Series = Level + CyclicTrend + Residual + Seasonality
                  - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality
              """,
          )
          parser.add_argument(
              "-m",
              "--multiplicative",
              action="store_true",
              default=False,
              dest="multiplicative",
              help="decompose using multiplicative model instead of additive",
          )
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              qa_view.display_seasonal(
                  name=self.ticker,
                  df=self.df,
                  target=self.target,
                  multiplicative=ns_parser.multiplicative,
                  export=ns_parser.export,
              )
  
      @log_start_end(log=logger)
      def call_cusum(self, other_args: List[str]):
          """Process cusum command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="cusum",
              description="""
                  Cumulative sum algorithm (CUSUM) to detect abrupt changes in data
              """,
          )
          parser.add_argument(
              "-t",
              "--threshold",
              dest="threshold",
              type=float,
              default=(
                  max(self.df[self.target].values) - min(self.df[self.target].values)
              )
              / 40,
              help="threshold",
          )
          parser.add_argument(
              "-d",
              "--drift",
              dest="drift",
              type=float,
              default=(
                  max(self.df[self.target].values) - min(self.df[self.target].values)
              )
              / 80,
              help="drift",
          )
          ns_parser = parse_known_args_and_warn(parser, other_args)
          if ns_parser:
              qa_view.display_cusum(
                  df=self.df,
                  target=self.target,
                  threshold=ns_parser.threshold,
                  drift=ns_parser.drift,
              )
  
      @log_start_end(log=logger)
      def call_acf(self, other_args: List[str]):
          """Process acf command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="acf",
              description="""
                  Auto-Correlation and Partial Auto-Correlation Functions for diff and diff diff stock data
              """,
          )
          parser.add_argument(
              "-w",
              "--window",
              dest="lags",
              type=check_positive,
              default=15,
              help="maximum lags to display in plots",
          )
          ns_parser = parse_known_args_and_warn(parser, other_args)
          if ns_parser:
              qa_view.display_acf(
                  name=self.ticker,
                  df=self.df,
                  target=self.target,
                  lags=ns_parser.lags,
              )
  
      @log_start_end(log=logger)
      def call_rolling(self, other_args: List[str]):
          """Process rolling command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="rolling",
              description="""
                  Rolling mean and std deviation
              """,
          )
          parser.add_argument(
              "-w",
              "--window",
              action="store",
              dest="n_window",
              type=check_positive,
              default=14,
              help="Window length",
          )
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              rolling_view.display_mean_std(
                  name=self.ticker,
                  df=self.df,
                  target=self.target,
                  window=ns_parser.n_window,
                  export=ns_parser.export,
              )
  
      @log_start_end(log=logger)
      def call_spread(self, other_args: List[str]):
          """Process spread command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="spread",
              description="""Shows rolling spread measurement
              """,
          )
          parser.add_argument(
              "-w",
              "--window",
              action="store",
              dest="n_window",
              type=check_positive,
              default=14,
              help="Window length",
          )
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              rolling_view.display_spread(
                  name=self.ticker,
                  df=self.df,
                  target=self.target,
                  window=ns_parser.n_window,
                  export=ns_parser.export,
              )
  
      @log_start_end(log=logger)
      def call_quantile(self, other_args: List[str]):
          """Process quantile command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="quantile",
              description="""
                  The quantiles are values which divide the distribution such that
                  there is a given proportion of observations below the quantile.
                  For example, the median is a quantile. The median is the central
                  value of the distribution, such that half the points are less than
                  or equal to it and half are greater than or equal to it.
  
                  By default, q is set at 0.5, which effectively is median. Change q to
                  get the desired quantile (0<q<1).
              """,
          )
          parser.add_argument(
              "-w",
              "--window",
              action="store",
              dest="n_length",
              type=check_positive,
              default=14,
              help="length",
          )
          parser.add_argument(
              "-q",
              "--quantile",
              action="store",
              dest="f_quantile",
              type=check_proportion_range,
              default=0.5,
              help="quantile",
          )
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              rolling_view.display_quantile(
                  name=self.ticker,
                  df=self.df,
                  target=self.target,
                  window=ns_parser.n_window,
                  quantile=ns_parser.f_quantile,
                  export=ns_parser.export,
              )
  
      @log_start_end(log=logger)
      def call_skew(self, other_args: List[str]):
          """Process skew command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="skew",
              description="""
                  Skewness is a measure of asymmetry or distortion of symmetric
                  distribution. It measures the deviation of the given distribution
                  of a random variable from a symmetric distribution, such as normal
                  distribution. A normal distribution is without any skewness, as it is
                  symmetrical on both sides. Hence, a curve is regarded as skewed if
                  it is shifted towards the right or the left.
              """,
          )
          parser.add_argument(
              "-w",
              "--window",
              action="store",
              dest="n_length",
              type=check_positive,
              default=14,
              help="length",
          )
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              rolling_view.display_skew(
                  name=self.ticker,
                  df=self.df,
                  target=self.target,
                  window=ns_parser.n_length,
                  export=ns_parser.export,
              )
  
      @log_start_end(log=logger)
      def call_kurtosis(self, other_args: List[str]):
          """Process kurtosis command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="kurtosis",
              description="""
                  Kurtosis is a measure of the "tailedness" of the probability distribution
                  of a real-valued random variable. Like skewness, kurtosis describes the shape
                  of a probability distribution and there are different ways of quantifying it
                  for a theoretical distribution and corresponding ways of estimating it from
                  a sample from a population. Different measures of kurtosis may have different
                  interpretations.
              """,
          )
          parser.add_argument(
              "-w",
              "--window",
              action="store",
              dest="n_length",
              type=check_positive,
              default=14,
              help="length",
          )
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              rolling_view.display_kurtosis(
                  name=self.ticker,
                  df=self.df,
                  target=self.target,
                  window=ns_parser.n_length,
                  export=ns_parser.export,
              )
  
      @log_start_end(log=logger)
      def call_normality(self, other_args: List[str]):
          """Process normality command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="normality",
              description="""
                  Normality tests
              """,
          )
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              qa_view.display_normality(
                  df=self.df, target=self.target, export=ns_parser.export
              )
  
      @log_start_end(log=logger)
      def call_qqplot(self, other_args: List[str]):
          """Process qqplot command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="qqplot",
              description="""
                  Display QQ plot vs normal quantiles
              """,
          )
          ns_parser = parse_known_args_and_warn(parser, other_args)
          if ns_parser:
              qa_view.display_qqplot(name=self.ticker, df=self.df, target=self.target)
  
      @log_start_end(log=logger)
      def call_unitroot(self, other_args: List[str]):
          """Process unitroot command"""
          parser = argparse.ArgumentParser(
              add_help=False,
              formatter_class=argparse.ArgumentDefaultsHelpFormatter,
              prog="unitroot",
              description="""
                  Unit root test / stationarity (ADF, KPSS)
              """,
          )
          parser.add_argument(
              "-r",
              "--fuller_reg",
              help="Type of regression.  Can be ‘c’,’ct’,’ctt’,’nc’ 'c' - Constant and t - trend order",
              choices=["c", "ct", "ctt", "nc"],
              default="c",
              type=str,
              dest="fuller_reg",
          )
          parser.add_argument(
              "-k",
              "--kps_reg",
              help="Type of regression.  Can be ‘c’,’ct'",
              choices=["c", "ct"],
              type=str,
              dest="kpss_reg",
              default="c",
          )
          ns_parser = parse_known_args_and_warn(
              parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
          )
          if ns_parser:
              qa_view.display_unitroot(
                  df=self.df,
                  target=self.target,
                  fuller_reg=ns_parser.fuller_reg,
                  kpss_reg=ns_parser.kpss_reg,
                  export=ns_parser.export,
              )
  