From 0436dbf14aa759204f1a94e6e8d017b8a76fb23c Mon Sep 17 00:00:00 2001 From: didier Date: Thu, 24 Dec 2020 14:55:07 +0000 Subject: [PATCH] Split code across different files --- stock_market_bot.py | 430 ----------------------------- stock_market_cli.py | 172 ++++++++++++ stock_market_helper_funcs.py | 76 +++++ stock_market_menu.py | 122 ++++++++ stock_market_technical_analysis.py | 117 ++++++++ 5 files changed, 487 insertions(+), 430 deletions(-) delete mode 100644 stock_market_bot.py create mode 100644 stock_market_cli.py create mode 100644 stock_market_helper_funcs.py create mode 100644 stock_market_menu.py create mode 100644 stock_market_technical_analysis.py diff --git a/stock_market_bot.py b/stock_market_bot.py deleted file mode 100644 index 4b407c722ba..00000000000 --- a/stock_market_bot.py +++ /dev/null @@ -1,430 +0,0 @@ -""" Example or something - - Is this not informative enough? - - Load a specific stock - Then apply a specific TA on it - - Do this because AlphaVantage only allows 5 API calls per minutes. - This way we only need 1 call, and can apply TA to the result without - using an API request. - - Finviz for data - -""" - -from alpha_vantage.timeseries import TimeSeries -import argparse -import config_bot as cfg -import pandas as pd -import pandas_ta as ta -from datetime import datetime -import matplotlib -import matplotlib.pyplot as plt -from pandas.plotting import register_matplotlib_converters -from pytz import timezone -from holidays import US as holidaysUS -from datetime import datetime, time as Time -register_matplotlib_converters() - - -# ----------------------------------------------------------------------------------------------------------------------- -def print_help(s_ta_ticker, s_ta_start, b_is_market_open): - """ Print help - """ - print("What do you want to do?") - - print("\n--- Screeners:") - print("view view and load a specific stock ticker for technical analysis") - print("gainers show latest top gainers") - print("load load a specific stock ticker for technical analysis") - print("clear clear a specific stock ticker from technical analysis") - - if s_ta_ticker and s_ta_start: - print(f"\n--- Technical Analysis on {s_ta_ticker} starting at {s_ta_start.strftime('%Y-%m-%d')}:") - elif s_ta_ticker: - print(f"\n--- Technical Analysis on {s_ta_ticker}:") - else: - print("\n--- Technical Analysis:") - print("sma simple moving average") - print("ema exponential moving average") - - print("\n--- Prediction:") - print("RNN") - print("ARIMA") - print("LSTM") - print("Facebook prophet") - - print(f"\n--- Market {('Closed', 'Open')[b_is_market_open]}. Play stocks:") - print("Stock name, and how much money to bet?") - - print("\n--- Others:") - print("help help to see this menu again") - print("quit to abandon the program") - - -# ----------------------------------------------------------------------------------------------------------------------- -def check_positive(value): - ivalue = int(value) - if ivalue <= 0: - raise argparse.ArgumentTypeError(f"{value} is an invalid positive int value") - return ivalue - - -# ----------------------------------------------------------------------------------------------------------------------- -def valid_date(s): - try: - return datetime.strptime(s, "%Y-%m-%d") - except ValueError: - raise argparse.ArgumentTypeError("Not a valid date: {s}") - - -# ----------------------------------------------------------------------------------------------------------------------- -def plot_view_stock(df, symbol): - pfig, axVolume = plt.subplots() - plt.bar(df.index, df['5. volume'].values, color='k', alpha=0.8) - plt.ylabel('Volume') - axPrice = axVolume.twinx() - plt.plot(df.index, df.iloc[:, :-1]) - plt.title(symbol + ' (Time Series)') - plt.xlim(df.index[0], df.index[-1]) - plt.xlabel('Time') - plt.ylabel('Share Price ($)') - plt.legend(df.columns) - plt.grid(b=True, which='major', color='#666666', linestyle='-') - plt.minorticks_on() - plt.grid(b=True, which='minor', color='#999999', linestyle='-', alpha=0.2) - plt.show() - - -# ----------------------------------------------------------------------------------------------------------------------- -def plot_stock_ta(df_stock, s_stock, df_ta, s_ta): - plt.plot(df_stock.index, df_stock['4. close'].values) - plt.plot(df_ta.index, df_ta.values) - plt.title(f"{s_ta} on {s_stock}") - plt.xlim(df_stock.index[0], df_stock.index[-1]) - plt.xlabel('Time') - plt.ylabel('Share Price ($)') - plt.legend([s_stock, s_ta]) - plt.grid(b=True, which='major', color='#666666', linestyle='-') - plt.minorticks_on() - plt.grid(b=True, which='minor', color='#999999', linestyle='-', alpha=0.2) - plt.show() - - -# ----------------------------------------------------------------------------------------------------------------------- -def b_is_stock_market_open(): - ''' checks if the stock market is open ''' - # Get current US time - now = datetime.now(timezone('US/Eastern')) - # Check if it is a weekend - if now.date().weekday() > 4: - return False - # Check if it is a holiday - if now.strftime('%Y-%m-%d') in holidaysUS(): - return False - # Check if it hasn't open already - if now.time() < Time(hour=9, minute=30, second=0): - return False - # Check if it has already closed - if now.time() > Time(hour=16, minute=0, second=0): - return False - # Otherwise, Stock Market is open! - return True - - -# ----------------------------------------------------------------------------------------------------------------------- -def main(): - """ Main function of the program - """ - - # temporary - #parser.add_argument('-a', action="store_true", default=False, help='example of flag') - #parser.add_argument('-b', action="store", dest="b", help='example of string') - #parser.add_argument('-c', action="store", dest="c", type=int, help='example of int') - - s_ta_ticker = "" - s_ta_start = "" - main_parser = argparse.ArgumentParser(prog='stock_market_bot', add_help=False) - - # Add list of arguments that the main parser accepts - main_parser.add_argument('cmd', choices=['quit', 'help', 'gainers' ,'view', 'load', 'clear', 'sma', 'ema']) - - # Print first welcome message and help - print("\nWelcome to Didier's Stock Market Bot\n") - print_help(s_ta_ticker, s_ta_start, b_is_stock_market_open()) - print("\n") - - # Loop forever and ever - while True: - # Get input command from user - as_input = input('> ') - - # Parse main command of the list of possible commands - try: - (ns_known_args, l_args) = main_parser.parse_known_args(as_input.split()) - except SystemExit: - print("The command selected doesn't exist\n") - continue - - # ---------------------------------------------------- GAINERS ---------------------------------------------------- - if ns_known_args.cmd == 'gainers': - parser = argparse.ArgumentParser(prog='gainers', - description='Show top ticker gainers from Yahoo Finance') - parser.add_argument('-n', "--num", action="store", dest="n_gainers", type=int, default=5, choices=range(1, 25), - help='Number of the top gainers stocks to retrieve (default 5)') - - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - except SystemExit: - print("") - continue - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - - df_gainers = pd.read_html('https://finance.yahoo.com/screener/predefined/day_gainers')[0] - print(df_gainers.head(ns_parser.n_gainers).to_string(index=False)) - - - # ---------------------------------------------------- VIEW ---------------------------------------------------- - elif ns_known_args.cmd == 'view': - parser = argparse.ArgumentParser(prog='view', - description='Given a stock market ticker, allows to see historical data of \ - the corresponding stock. Note that the alpha_vantage key is necessary \ - for this to work!') - parser.add_argument('-t', "--ticker" ,action="store", dest="s_ticker", required=True, help='Stock ticker') - parser.add_argument("--type", action="store", dest="n_type", type=check_positive, default=4, - help='1234 corresponds to types: 1. open; 2. high; 3.low; 4. close \ - while 14 corresponds to types: 1.open; 4. close') - parser.add_argument('-s', "--start", type=valid_date, dest="s_start_date", - help="The starting date (format YYYY-MM-DD) of the stock") - - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - except SystemExit: - print("") - continue - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - - # Currently only supports daily stocks - # TODO: Intra-daily perhaps - ts = TimeSeries(key=cfg.API_KEY, output_format='pandas') - try: - df_stock, d_stock_metadata = ts.get_daily(symbol=ns_parser.s_ticker, outputsize='full') - except: - print("Either the ticker or the API_KEY are invalids. Try again!") - continue - - s_ta_ticker = ns_parser.s_ticker - - # Add function to plot data - ln_col_idx = [int(x)-1 for x in list(str(ns_parser.n_type))] - # Check that the types given are not bigger than 3, as there are only 4 types (0-3) - if len([i for i in ln_col_idx if i > 3]) > 0: - print("An index bigger than 3 was given, which is wrong. Try again") - continue - - # Slice dataframe from the starting date YYYY-MM-DD selected - if ns_parser.s_start_date: - df_stock = df_stock[ns_parser.s_start_date:] - s_ta_start = ns_parser.s_start_date - - ln_col_idx.append(4) # Append last column of df to be filtered which corresponds to: 5. Volume - - plot_view_stock(df_stock.iloc[:, ln_col_idx], ns_parser.s_ticker) - - - # ---------------------------------------------------- LOAD ---------------------------------------------------- - elif ns_known_args.cmd == 'load': - parser = argparse.ArgumentParser(prog='load', - description=""" Given a stock market ticker, and an optional starting time, - load a stock in order to perform technical analysis""") - parser.add_argument('-t', "--ticker", action="store", dest="s_ticker", required=True, help='Stock ticker') - parser.add_argument("--type", action="store", dest="n_type", type=check_positive, default=4, - help='1234 corresponds to types: 1. open; 2. high; 3.low; 4. close \ - while 14 corresponds to types: 1.open; 4. close') - parser.add_argument('-s', "--start", type=valid_date, dest="s_start_date", - help="The starting date (format YYYY-MM-DD) of the stock") - - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - except SystemExit: - print("") - continue - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - - # Currently only supports daily stocks - # TODO: Intra-daily perhaps - ts = TimeSeries(key=cfg.API_KEY, output_format='pandas') - try: - df_stock, d_stock_metadata = ts.get_daily(symbol=ns_parser.s_ticker, outputsize='full') - except: - print("Either the ticker or the API_KEY are invalids. Try again!") - continue - - s_ta_ticker = ns_parser.s_ticker - - # Add function to plot data - ln_col_idx = [int(x)-1 for x in list(str(ns_parser.n_type))] - # Check that the types given are not bigger than 3, as there are only 4 types (0-3) - if len([i for i in ln_col_idx if i > 3]) > 0: - print("An index bigger than 3 was given, which is wrong. Try again") - continue - - # Slice dataframe from the starting date YYYY-MM-DD selected - if ns_parser.s_start_date: - df_stock = df_stock[ns_parser.s_start_date:] - s_ta_start = ns_parser.s_start_date - else: - s_ta_start = "" - - if s_ta_start: - print(f"Loading {s_ta_ticker} stock with starting period {s_ta_start.strftime('%Y-%m-%d')} for technical analysis.") - else: - print(f"Loading {s_ta_ticker} stock for technical analysis.") - - - # ---------------------------------------------------- CLEAR ---------------------------------------------------- - elif ns_known_args.cmd == 'clear': - print("Clearing stock ticker to be used for technical analysis") - s_ta_ticker = "" - s_ta_start = "" - - # -------------------------------------------------------------------------------------------------------------- - # -------------------------------------------- TECHNICAL ANALYSIS ---------------------------------------------- - # -------------------------------------------------------------------------------------------------------------- - - # ---------------------------------------------------- SMA ---------------------------------------------------- - elif ns_known_args.cmd == 'sma': - parser = argparse.ArgumentParser(prog='sma', - description=""" Moving Averages are used to smooth the data in an array to - help eliminate noise and identify trends. The Simple Moving Average is literally - the simplest form of a moving average. Each output value is the average of the - previous n values. In a Simple Moving Average, each value in the time period carries - equal weight, and values outside of the time period are not included in the average. - This makes it less responsive to recent changes in the data, which can be useful for - filtering out those changes. """) - parser.add_argument('-p', "--period", action="store", dest="n_time_period", type=check_positive, default=20, - help='Number of data points used to calculate each moving average value.') - parser.add_argument("-s", "--start", type=valid_date, dest="s_start_date", - help="The starting date (format YYYY-MM-DD) of the stock") - if s_ta_ticker: - parser.add_argument('-t', "--ticker", action="store", dest="s_ticker", default=s_ta_ticker, help='Stock ticker') - else: - parser.add_argument('-t', "--ticker", action="store", dest="s_ticker", required=True, help='Stock ticker') - - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - except SystemExit: - print("") - continue - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - - try: # Get stock data - ts = TimeSeries(key=cfg.API_KEY, output_format='pandas') - df_stock, d_stock_metadata = ts.get_daily(symbol=ns_parser.s_ticker, outputsize='full') - except: - print("ERROR! Try again!") - print("Check that: 1. The ticker symbol exists and is valid") - print(" 2. Less than 5 AlphaVantage requests have been made in 1 minute") - print(" 3. The API_KEY is (still) valid") - continue - - # Update TA ticker - s_ta_ticker = ns_parser.s_ticker - - # Update TA starting point if needed - if ns_parser.s_start_date: - s_ta_start = ns_parser.s_start_date - - # Slice dataframe from the starting date YYYY-MM-DD selected - if s_ta_start: - df_stock = df_stock[ns_parser.s_start_date:] - - df_ta = ta.sma(df_stock['4. close'], timeperiod=ns_parser.n_time_period).dropna() - - plot_stock_ta(df_stock, d_stock_metadata['2. Symbol'], df_ta, f"{ns_parser.n_time_period} SMA") - - continue - - - # ---------------------------------------------------- EMA ---------------------------------------------------- - elif ns_known_args.cmd == 'ema': - parser = argparse.ArgumentParser(prog='ema', - description=""" The Exponential Moving Average is a staple of technical - analysis and is used in countless technical indicators. In a Simple Moving - Average, each value in the time period carries equal weight, and values outside - of the time period are not included in the average. However, the Exponential - Moving Average is a cumulative calculation, including all data. Past values have - a diminishing contribution to the average, while more recent values have a greater - contribution. This method allows the moving average to be more responsive to changes - in the data. """) - parser.add_argument('-p', "--period", action="store", dest="n_time_period", type=check_positive, default=20, - help='Number of data points used to calculate each moving average value.') - parser.add_argument("-s", "--start", type=valid_date, dest="s_start_date", - help="The starting date (format YYYY-MM-DD) of the stock") - if s_ta_ticker: - parser.add_argument('-t', "--ticker", action="store", dest="s_ticker", default=s_ta_ticker, help='Stock ticker') - else: - parser.add_argument('-t', "--ticker", action="store", dest="s_ticker", required=True, help='Stock ticker') - - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - except SystemExit: - print("") - continue - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - - try: # Get stock data - ts = TimeSeries(key=cfg.API_KEY, output_format='pandas') - df_stock, d_stock_metadata = ts.get_daily(symbol=ns_parser.s_ticker, outputsize='full') - except: - print("ERROR! Try again!") - print("Check that: 1. The ticker symbol exists and is valid") - print(" 2. Less than 5 AlphaVantage requests have been made in 1 minute") - print(" 3. The API_KEY is (still) valid") - continue - - # Update TA ticker - s_ta_ticker = ns_parser.s_ticker - - # Update TA starting point if needed - if ns_parser.s_start_date: - s_ta_start = ns_parser.s_start_date - - # Slice dataframe from the starting date YYYY-MM-DD selected - if s_ta_start: - df_stock = df_stock[ns_parser.s_start_date:] - - df_ta = ta.ema(df_stock['4. close'], timeperiod=ns_parser.n_time_period).dropna() - - plot_stock_ta(df_stock, d_stock_metadata['2. Symbol'], df_ta, f"{ns_parser.n_time_period} EMA") - - continue - - - # ---------------------------------------------------- HELP ---------------------------------------------------- - elif ns_known_args.cmd == 'help': - print_help(s_ta_ticker, s_ta_start, b_is_stock_market_open()) - - # ---------------------------------------------------- QUIT ---------------------------------------------------- - elif ns_known_args.cmd == 'quit': - print("Hope you made money today. Good bye my lover, good bye my friend.\n") - break - - else: - print('Shouldnt see this command!') - - print("\n") - -if __name__ == "__main__": - main() diff --git a/stock_market_cli.py b/stock_market_cli.py new file mode 100644 index 00000000000..f8e9e3b7f90 --- /dev/null +++ b/stock_market_cli.py @@ -0,0 +1,172 @@ +""" Example or something + + Is this not informative enough? + + Load a specific stock + Then apply a specific TA on it + + Do this because AlphaVantage only allows 5 API calls per minutes. + This way we only need 1 call, and can apply TA to the result without + using an API request. + + Finviz for data + +""" + +from alpha_vantage.timeseries import TimeSeries +import argparse +import config_bot as cfg +import pandas as pd +import pandas_ta as ta +from pandas.plotting import register_matplotlib_converters +register_matplotlib_converters() + +from stock_market_helper_funcs import * +import stock_market_menu as smm +import stock_market_technical_analysis as smta + + + +# ----------------------------------------------------------------------------------------------------------------------- +def print_help(s_ticker, s_start, b_is_market_open): + """ Print help + """ + print("What do you want to do?") + + print("\nMenu:") + print(" gainers show latest top gainers") + print(" view view and load a specific stock ticker for technical analysis") + print(" clear clear a specific stock ticker from technical analysis") + print(" load load a specific stock ticker for technical analysis") + print(" help help to see this menu again") + print(" quit to abandon the program") + + if s_ticker and s_start: + print(f"\n-----> {s_ticker} starting at {s_start.strftime('%Y-%m-%d')} <-----") + elif s_ticker: + print(f"\n-----> {s_ticker} <-----") + else: + print("\n-----> ? <-----") + + print("\nFundamental Analysis:") + print(" example not yet covered") + + print("\nTechnical Analysis:") + print(" sma simple moving average") + print(" ema exponential moving average") + + print("\nPrediction:") + print(" ma") + print(" ema") + print(" lr") + print(" knn") + print(" arima") + print(" rnn") + print(" lstm") + print(" prophet") + + print(f"\nMarket {('Closed', 'Open')[b_is_market_open]}. Play stocks:") + print("Stock name, and how much money to bet?") + + +# ----------------------------------------------------------------------------------------------------------------------- +def main(): + """ Main function of the program + """ + + # temporary + #parser.add_argument('-a', action="store_true", default=False, help='example of flag') + #parser.add_argument('-b', action="store", dest="b", help='example of string') + #parser.add_argument('-c', action="store", dest="c", type=int, help='example of int') + + s_ticker = "" + s_start = "" + df_stock = pd.DataFrame() + + main_parser = argparse.ArgumentParser(prog='stock_market_bot', add_help=False) + + # Add list of arguments that the main parser accepts + main_parser.add_argument('cmd', choices=['quit', 'help', 'gainers' ,'view', 'load', 'clear', 'sma', 'ema']) + + # Print first welcome message and help + print("\nWelcome to Didier's Stock Market Bot\n") + print_help(s_ticker, s_start, b_is_stock_market_open()) + print("\n") + + # Loop forever and ever + while True: + # Get input command from user + as_input = input('> ') + + # Parse main command of the list of possible commands + try: + (ns_known_args, l_args) = main_parser.parse_known_args(as_input.split()) + except SystemExit: + print("The command selected doesn't exist\n") + continue + + # -------------------------------------------------------------------------------------------------------------- + # --------------------------------------------------- MENU ----------------------------------------------------- + # -------------------------------------------------------------------------------------------------------------- + + # --------------------------------------------------- GAINERS -------------------------------------------------- + if ns_known_args.cmd == 'gainers': + smm.gainers(l_args) + continue + + # --------------------------------------------------- CLEAR ---------------------------------------------------- + elif ns_known_args.cmd == 'clear': + print("Clearing stock ticker to be used for analysis") + s_ticker = "" + s_start = "" + + # ---------------------------------------------------- LOAD ---------------------------------------------------- + elif ns_known_args.cmd == 'load': + [s_ticker, s_start, df_stock] = smm.load(l_args, s_ticker, s_start, df_stock) + continue + + # ---------------------------------------------------- VIEW ---------------------------------------------------- + elif ns_known_args.cmd == 'view': + smm.view(l_args, s_ticker, s_start) + continue + + # ---------------------------------------------------- HELP ---------------------------------------------------- + elif ns_known_args.cmd == 'help': + print_help(s_ticker, s_start, b_is_stock_market_open()) + + # ----------------------------------------------------- QUIT ---------------------------------------------------- + elif ns_known_args.cmd == 'quit': + print("Hope you made money today. Good bye my lover, good bye my friend.\n") + break + + # -------------------------------------------------------------------------------------------------------------- + # ------------------------------------------- FUNDAMENTAL ANALYSIS --------------------------------------------- + # -------------------------------------------------------------------------------------------------------------- + + # -------------------------------------------------------------------------------------------------------------- + # -------------------------------------------- TECHNICAL ANALYSIS ---------------------------------------------- + # -------------------------------------------------------------------------------------------------------------- + + # -------------------------------------------------------------------------------------------------------------- + # ------------------------------------------------ PREDICTION -------------------------------------------------- + # -------------------------------------------------------------------------------------------------------------- + + # ---------------------------------------------------- SMA ---------------------------------------------------- + elif ns_known_args.cmd == 'sma': + [s_ticker, s_start] = smta.sma(l_args, s_ticker, s_start) + continue + + # ---------------------------------------------------- EMA ---------------------------------------------------- + elif ns_known_args.cmd == 'ema': + [s_ticker, s_start] = smta.ema(l_args, s_ticker, s_start) + continue + + + + else: + print('Shouldnt see this command!') + + print("\n") + +if __name__ == "__main__": + main() diff --git a/stock_market_helper_funcs.py b/stock_market_helper_funcs.py new file mode 100644 index 00000000000..9af941f9f4d --- /dev/null +++ b/stock_market_helper_funcs.py @@ -0,0 +1,76 @@ + +from pytz import timezone +from holidays import US as holidaysUS +from datetime import datetime, time as Time +import matplotlib +import matplotlib.pyplot as plt + +# ----------------------------------------------------------------------------------------------------------------------- +def check_positive(value): + ivalue = int(value) + if ivalue <= 0: + raise argparse.ArgumentTypeError(f"{value} is an invalid positive int value") + return ivalue + + +# ----------------------------------------------------------------------------------------------------------------------- +def valid_date(s): + try: + return datetime.strptime(s, "%Y-%m-%d") + except ValueError: + raise argparse.ArgumentTypeError("Not a valid date: {s}") + + +# ----------------------------------------------------------------------------------------------------------------------- +def plot_view_stock(df, symbol): + pfig, axVolume = plt.subplots() + plt.bar(df.index, df['5. volume'].values, color='k', alpha=0.8) + plt.ylabel('Volume') + axPrice = axVolume.twinx() + plt.plot(df.index, df.iloc[:, :-1]) + plt.title(symbol + ' (Time Series)') + plt.xlim(df.index[0], df.index[-1]) + plt.xlabel('Time') + plt.ylabel('Share Price ($)') + plt.legend(df.columns) + plt.grid(b=True, which='major', color='#666666', linestyle='-') + plt.minorticks_on() + plt.grid(b=True, which='minor', color='#999999', linestyle='-', alpha=0.2) + plt.show() + + +# ----------------------------------------------------------------------------------------------------------------------- +def plot_stock_ta(df_stock, s_stock, df_ta, s_ta): + plt.plot(df_stock.index, df_stock['4. close'].values) + plt.plot(df_ta.index, df_ta.values) + plt.title(f"{s_ta} on {s_stock}") + plt.xlim(df_stock.index[0], df_stock.index[-1]) + plt.xlabel('Time') + plt.ylabel('Share Price ($)') + plt.legend([s_stock, s_ta]) + plt.grid(b=True, which='major', color='#666666', linestyle='-') + plt.minorticks_on() + plt.grid(b=True, which='minor', color='#999999', linestyle='-', alpha=0.2) + plt.show() + + +# ----------------------------------------------------------------------------------------------------------------------- +def b_is_stock_market_open(): + ''' checks if the stock market is open ''' + # Get current US time + now = datetime.now(timezone('US/Eastern')) + # Check if it is a weekend + if now.date().weekday() > 4: + return False + # Check if it is a holiday + if now.strftime('%Y-%m-%d') in holidaysUS(): + return False + # Check if it hasn't open already + if now.time() < Time(hour=9, minute=30, second=0): + return False + # Check if it has already closed + if now.time() > Time(hour=16, minute=0, second=0): + return False + # Otherwise, Stock Market is open! + return True + diff --git a/stock_market_menu.py b/stock_market_menu.py new file mode 100644 index 00000000000..4af24d16a28 --- /dev/null +++ b/stock_market_menu.py @@ -0,0 +1,122 @@ +import argparse +from alpha_vantage.timeseries import TimeSeries +import config_bot as cfg +import pandas as pd +from stock_market_helper_funcs import * + + +# ---------------------------------------------------- GAINERS ---------------------------------------------------- +def gainers(l_args): + parser = argparse.ArgumentParser(prog='gainers', + description='Show top ticker gainers from Yahoo Finance') + parser.add_argument('-n', "--num", action="store", dest="n_gainers", type=int, default=5, choices=range(1, 25), + help='Number of the top gainers stocks to retrieve (default 5)') + + try: + (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + except SystemExit: + print("") + return + + if l_unknown_args: + print(f"The following args couldn't be interpreted: {l_unknown_args}") + + df_gainers = pd.read_html('https://finance.yahoo.com/screener/predefined/day_gainers')[0] + print(df_gainers.head(ns_parser.n_gainers).to_string(index=False)) + print("") + +# ----------------------------------------------------- LOAD ----------------------------------------------------- +def load(l_args, s_ticker, s_start, df_stock): + parser = argparse.ArgumentParser(prog='load', + description=""" Given a stock market ticker, and an optional starting time, + load a stock in order to perform analysis""") + parser.add_argument('-t', "--ticker", action="store", dest="s_ticker", required=True, help='Stock ticker') + parser.add_argument('-s', "--start", type=valid_date, dest="s_start_date", + help="The starting date (format YYYY-MM-DD) of the stock") + + try: + (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + except SystemExit: + print("") + return [s_ticker, s_start, df_stock] + + if l_unknown_args: + print(f"The following args couldn't be interpreted: {l_unknown_args}") + + # Currently only supports daily stocks + ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format='pandas') + try: + df_stock, d_stock_metadata = ts.get_daily(symbol=ns_parser.s_ticker, outputsize='full') + except: + print("Either the ticker or the API_KEY are invalids. Try again!") + return [s_ticker, s_start, df_stock] + + s_ticker = ns_parser.s_ticker + + if ns_parser.s_start_date: + s_start = ns_parser.s_start_date + + # Slice dataframe from the starting date YYYY-MM-DD selected + if ns_parser.s_start_date: + df_stock = df_stock[ns_parser.s_start_date:] + + # Get stock close price + df_stock = df_stock.iloc[:, 3] + + if s_start: + print(f"Loading {s_ticker} stock with starting period {s_start.strftime('%Y-%m-%d')} for analysis.\n") + else: + print(f"Loading {s_ticker} stock for analysis.\n") + + return [s_ticker, s_start, df_stock] + + +# ----------------------------------------------------- VIEW ----------------------------------------------------- +def view(l_args, s_ticker, s_start): + parser = argparse.ArgumentParser(prog='view', + description='Given a stock market ticker, allows to see historical data of \ + the corresponding stock. Note that the alpha_vantage key is necessary \ + for this to work!') + if s_ticker: + parser.add_argument('-t', "--ticker" ,action="store", dest="s_ticker", default=s_ticker, help='Stock ticker') + else: + parser.add_argument('-t', "--ticker" ,action="store", dest="s_ticker", required=True, help='Stock ticker') + + parser.add_argument('-s', "--start", type=valid_date, dest="s_start_date", + help="The starting date (format YYYY-MM-DD) of the stock") + parser.add_argument("--type", action="store", dest="n_type", type=check_positive, default=4, + help='1234 corresponds to types: 1. open; 2. high; 3.low; 4. close \ + while 14 corresponds to types: 1.open; 4. close') + + try: + (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + except SystemExit: + print("") + return + + if l_unknown_args: + print(f"The following args couldn't be interpreted: {l_unknown_args}") + + # Currently only supports daily stocks + ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format='pandas') + try: + df_stock, d_stock_metadata = ts.get_daily(symbol=ns_parser.s_ticker, outputsize='full') + except: + print("Either the ticker or the API_KEY are invalids. Try again!") + return + + # Slice dataframe from the starting date YYYY-MM-DD selected + if ns_parser.s_start_date: + df_stock = df_stock[ns_parser.s_start_date:] + + # Add function to plot data + ln_col_idx = [int(x)-1 for x in list(str(ns_parser.n_type))] + # Check that the types given are not bigger than 3, as there are only 4 types (0-3) + if len([i for i in ln_col_idx if i > 3]) > 0: + print("An index bigger than 3 was given, which is wrong. Try again") + return + + ln_col_idx.append(4) # Append last column of df to be filtered which corresponds to: 5. Volume + + plot_view_stock(df_stock.iloc[:, ln_col_idx], ns_parser.s_ticker) + diff --git a/stock_market_technical_analysis.py b/stock_market_technical_analysis.py new file mode 100644 index 00000000000..37a6079d5f9 --- /dev/null +++ b/stock_market_technical_analysis.py @@ -0,0 +1,117 @@ +import argparse +from alpha_vantage.timeseries import TimeSeries +import pandas_ta as ta +import config_bot as cfg +from stock_market_helper_funcs import * + +# ----------------------------------------------------- SMA ----------------------------------------------------- +def sma(l_args, s_ticker, s_start): + parser = argparse.ArgumentParser(prog='sma', + description=""" Moving Averages are used to smooth the data in an array to + help eliminate noise and identify trends. The Simple Moving Average is literally + the simplest form of a moving average. Each output value is the average of the + previous n values. In a Simple Moving Average, each value in the time period carries + equal weight, and values outside of the time period are not included in the average. + This makes it less responsive to recent changes in the data, which can be useful for + filtering out those changes. """) + parser.add_argument('-p', "--period", action="store", dest="n_time_period", type=check_positive, default=20, + help='Number of data points used to calculate each moving average value.') + parser.add_argument("-s", "--start", type=valid_date, dest="s_start_date", + help="The starting date (format YYYY-MM-DD) of the stock") + if s_ticker: + parser.add_argument('-t', "--ticker", action="store", dest="s_ticker", default=s_ticker, help='Stock ticker') + else: + parser.add_argument('-t', "--ticker", action="store", dest="s_ticker", required=True, help='Stock ticker') + + try: + (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + except SystemExit: + print("") + return [s_ticker, s_start] + + if l_unknown_args: + print(f"The following args couldn't be interpreted: {l_unknown_args}") + + try: # Get stock data + ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format='pandas') + df_stock, d_stock_metadata = ts.get_daily(symbol=ns_parser.s_ticker, outputsize='full') + except: + print("ERROR! Try again!") + print("Check that: 1. The ticker symbol exists and is valid") + print(" 2. Less than 5 AlphaVantage requests have been made in 1 minute") + print(" 3. The API_KEY is (still) valid") + return [s_ticker, s_start] + + # Update TA ticker + s_ticker = ns_parser.s_ticker + + # Update TA starting point if needed + if ns_parser.s_start_date: + s_start = ns_parser.s_start_date + + # Slice dataframe from the starting date YYYY-MM-DD selected + if s_start: + df_stock = df_stock[ns_parser.s_start_date:] + + df_ta = ta.sma(df_stock['4. close'], timeperiod=ns_parser.n_time_period).dropna() + + plot_stock_ta(df_stock, d_stock_metadata['2. Symbol'], df_ta, f"{ns_parser.n_time_period} SMA") + + return [s_ticker, s_start] + + +# ----------------------------------------------------- EMA ----------------------------------------------------- +def ema(l_args, s_ticker, s_start): + parser = argparse.ArgumentParser(prog='ema', + description=""" The Exponential Moving Average is a staple of technical + analysis and is used in countless technical indicators. In a Simple Moving + Average, each value in the time period carries equal weight, and values outside + of the time period are not included in the average. However, the Exponential + Moving Average is a cumulative calculation, including all data. Past values have + a diminishing contribution to the average, while more recent values have a greater + contribution. This method allows the moving average to be more responsive to changes + in the data. """) + parser.add_argument('-p', "--period", action="store", dest="n_time_period", type=check_positive, default=20, + help='Number of data points used to calculate each moving average value.') + parser.add_argument("-s", "--start", type=valid_date, dest="s_start_date", + help="The starting date (format YYYY-MM-DD) of the stock") + if s_ticker: + parser.add_argument('-t', "--ticker", action="store", dest="s_ticker", default=s_ticker, help='Stock ticker') + else: + parser.add_argument('-t', "--ticker", action="store", dest="s_ticker", required=True, help='Stock ticker') + + try: + (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + except SystemExit: + print("") + return [s_ticker, s_start] + + if l_unknown_args: + print(f"The following args couldn't be interpreted: {l_unknown_args}") + + try: # Get stock data + ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format='pandas') + df_stock, d_stock_metadata = ts.get_daily(symbol=ns_parser.s_ticker, outputsize='full') + except: + print("ERROR! Try again!") + print("Check that: 1. The ticker symbol exists and is valid") + print(" 2. Less than 5 AlphaVantage requests have been made in 1 minute") + print(" 3. The API_KEY is (still) valid") + return [s_ticker, s_start] + + # Update TA ticker + s_ticker = ns_parser.s_ticker + + # Update TA starting point if needed + if ns_parser.s_start_date: + s_start = ns_parser.s_start_date + + # Slice dataframe from the starting date YYYY-MM-DD selected + if s_start: + df_stock = df_stock[ns_parser.s_start_date:] + + df_ta = ta.ema(df_stock['4. close'], timeperiod=ns_parser.n_time_period).dropna() + + plot_stock_ta(df_stock, d_stock_metadata['2. Symbol'], df_ta, f"{ns_parser.n_time_period} EMA") + + return [s_ticker, s_start] \ No newline at end of file