diff --git a/gamestonk_terminal/common/technical_analysis/momentum_view.py b/gamestonk_terminal/common/technical_analysis/momentum_view.py index 99fa96d519f..9d1347a642f 100644 --- a/gamestonk_terminal/common/technical_analysis/momentum_view.py +++ b/gamestonk_terminal/common/technical_analysis/momentum_view.py @@ -333,20 +333,20 @@ def view_fisher( else: ax.plot(df_stock.index, df_stock["Close"].values, "k", lw=1) ax.set_xlim(df_stock.index[0], df_stock.index[-1]) - ax.set_ylabel("Share Price ($)") + ax.set_ylabel("Price") ax.grid(b=True, which="major", color="#666666", linestyle="-") ax2 = axes[1] ax2.plot( df_ta.index, - df_ta["FISHERT_14_1"].values, + df_ta.iloc[:, 0].values, "b", lw=2, label="Fisher", ) ax2.plot( df_ta.index, - df_ta["FISHERTs_14_1"].values, + df_ta.iloc[:, 1].values, "fuchsia", lw=2, label="Signal", diff --git a/gamestonk_terminal/common/technical_analysis/volatility_model.py b/gamestonk_terminal/common/technical_analysis/volatility_model.py index c6829998656..cabdb997efc 100644 --- a/gamestonk_terminal/common/technical_analysis/volatility_model.py +++ b/gamestonk_terminal/common/technical_analysis/volatility_model.py @@ -81,3 +81,58 @@ def donchian( ).dropna() return df_ta + + +def kc( + s_interval: str, + df_stock: pd.DataFrame, + length: int, + scalar: float, + mamode: str, + offset: int, +) -> pd.DataFrame: + """Keltner Channels + + Parameters + ---------- + s_interval : str + Interval of stock data + df_stock : pd.DataFrame + Dataframe of prices + length : int + Length of window + mamode: str + Type of filter + offset : int + Offset value + + Returns + ------- + df_en : pd.DataFrame + Dataframe of rolling kc + """ + # Daily + if s_interval == "1440min": + df_kc = ta.kc( + high=df_stock["High"], + low=df_stock["Low"], + close=df_stock["Adj Close"], + length=length, + scalar=scalar, + mamode=mamode, + offset=offset, + ).dropna() + + # Intraday + else: + df_kc = ta.kc( + high=df_stock["High"], + low=df_stock["Low"], + close=df_stock["Close"], + length=length, + scalar=scalar, + mamode=mamode, + offset=offset, + ).dropna() + + return df_kc diff --git a/gamestonk_terminal/common/technical_analysis/volatility_view.py b/gamestonk_terminal/common/technical_analysis/volatility_view.py index 868285ff6dd..6cd2841931a 100644 --- a/gamestonk_terminal/common/technical_analysis/volatility_view.py +++ b/gamestonk_terminal/common/technical_analysis/volatility_view.py @@ -152,3 +152,74 @@ def view_donchian( "donchian", df_ta, ) + + +def view_kc( + s_ticker: str, + s_interval: str, + df_stock: pd.DataFrame, + length: int, + scalar: float, + mamode: str, + offset: int, + export: str, +): + """View Keltner Channels Indicator + + Parameters + ---------- + s_ticker : str + Ticker + s_interval : str + Interval of data + df_stock : pd.DataFrame + Dataframe of stock prices + length : int + Length of window + mamode: str + Type of filter + offset : int + Offset value + export : str + Format to export data + """ + df_ta = volatility_model.kc(s_interval, df_stock, length, scalar, mamode, offset) + fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + if s_interval == "1440min": + ax.plot(df_stock.index, df_stock["Adj Close"].values, color="fuchsia") + else: + ax.plot(df_stock.index, df_stock["Close"].values, color="fuchsia") + ax.plot(df_ta.index, df_ta.iloc[:, 0].values, "b", lw=1.5, label="upper") + ax.plot(df_ta.index, df_ta.iloc[:, 1].values, "b", lw=1.5, ls="--") + ax.plot(df_ta.index, df_ta.iloc[:, 2].values, "b", lw=1.5, label="lower") + ax.set_title(f"{s_ticker} Keltner Channels") + ax.set_xlim(df_stock.index[0], df_stock.index[-1]) + ax.set_xlabel("Time") + ax.set_ylabel("Price") + + ax.legend([s_ticker, df_ta.columns[0], df_ta.columns[1], df_ta.columns[2]]) + ax.fill_between( + df_ta.index, + df_ta.iloc[:, 0].values, + df_ta.iloc[:, 2].values, + alpha=0.1, + color="b", + ) + ax.grid(b=True, which="major", color="#666666", linestyle="-") + + if gtff.USE_ION: + plt.ion() + + plt.gcf().autofmt_xdate() + fig.tight_layout(pad=1) + + plt.legend() + plt.show() + + print("") + export_data( + export, + os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), + "kc", + df_ta, + ) diff --git a/gamestonk_terminal/stocks/technical_analysis/ta_controller.py b/gamestonk_terminal/stocks/technical_analysis/ta_controller.py index ebcc4f6d697..e544f3477d8 100644 --- a/gamestonk_terminal/stocks/technical_analysis/ta_controller.py +++ b/gamestonk_terminal/stocks/technical_analysis/ta_controller.py @@ -62,6 +62,7 @@ class TechnicalAnalysisController: "aroon", "bbands", "donchian", + "kc", "ad", "adosc", "obv", @@ -131,6 +132,7 @@ Trend: Volatility: bbands bollinger bands donchian donchian channels + kc keltner channels Volume: ad accumulation/distribution line adosc chaikin oscillator @@ -281,6 +283,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -325,6 +331,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -434,6 +444,7 @@ Custom: if other_args: if "-l" not in other_args and "-h" not in other_args: other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -485,6 +496,7 @@ Custom: if other_args: if "-l" not in other_args and "-h" not in other_args: other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -536,6 +548,7 @@ Custom: if other_args: if "-l" not in other_args and "-h" not in other_args: other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -627,6 +640,10 @@ Custom: help="Export dataframe data to csv,json,xlsx file", ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -690,6 +707,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -761,6 +782,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -829,6 +854,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -898,6 +927,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -947,6 +980,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -995,9 +1032,14 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return + momentum_view.view_cg( s_ticker=self.ticker, s_interval=self.interval, @@ -1058,6 +1100,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -1113,15 +1159,6 @@ Custom: default=100, help="scalar", ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=check_positive, - default=0, - help="offset", - ) parser.add_argument( "--export", choices=["csv", "json", "xlsx"], @@ -1132,6 +1169,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -1204,9 +1245,14 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return + volatility_view.view_bbands( ticker=self.ticker, s_interval=self.interval, @@ -1263,9 +1309,14 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return + volatility_view.view_donchian( ticker=self.ticker, s_interval=self.interval, @@ -1278,6 +1329,87 @@ Custom: except Exception as e: print(e, "\n") + def call_kc(self, other_args: List[str]): + """Process kc command""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="kc", + description=""" + Keltner Channels are volatility-based bands that are placed + on either side of an asset's price and can aid in determining + the direction of a trend.The Keltner channel uses the average + true range (ATR) or volatility, with breaks above or below the top + and bottom barriers signaling a continuation. + """, + ) + parser.add_argument( + "-l", + "--length", + action="store", + dest="n_length", + type=check_positive, + default=20, + help="Window length", + ) + parser.add_argument( + "-s", + "--scalar", + action="store", + dest="n_scalar", + type=check_positive, + default=2, + help="scalar", + ) + parser.add_argument( + "-m", + "--mamode", + action="store", + dest="s_mamode", + default="ema", + choices=["ema", "sma", "wma", "hma", "zlma"], + help="mamode", + ) + parser.add_argument( + "-o", + "--offset", + action="store", + dest="n_offset", + type=int, + default=0, + help="offset", + ) + parser.add_argument( + "--export", + choices=["csv", "json", "xlsx"], + default="", + type=str, + dest="export", + help="Export dataframe data to csv,json,xlsx file", + ) + + try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + volatility_view.view_kc( + s_ticker=self.ticker, + s_interval=self.interval, + df_stock=self.stock, + length=ns_parser.n_length, + scalar=ns_parser.n_scalar, + mamode=ns_parser.s_mamode, + offset=ns_parser.n_offset, + export=ns_parser.export, + ) + except Exception as e: + print(e, "\n") + def call_ad(self, other_args: List[str]): """Process ad command""" parser = argparse.ArgumentParser( @@ -1314,6 +1446,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -1379,6 +1515,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -1421,6 +1561,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -1475,6 +1619,10 @@ Custom: ) try: + if other_args: + if "-l" not in other_args and "-h" not in other_args: + other_args.insert(0, "-l") + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return