''' @author Pablo Aurbano @license GNU-free with author mention @group BearBullTraders.com @version v0.5 @description Activate verbose mode only for debug Enter daily money target for share size calc ATR projections will be calculated with the 14 period ATR of each ticker symbol and adjusted for 3 levels/scenarios a) PDC up (PDC >> max) b) PDC down (PDC >> min) c) Daily inside candle with PDC in the middle (R >> S) Volume analysis will get 9 last days for consideration: rvol = relative volume tvol = today's volume avg_vol = last 9 days average volume @pkgs pandas, math, datetime @copyright @2020 ''' # Package imports import pandas as pd import math from datetime import datetime, timedelta import yfinance as yf import warnings # avoiding silly warnings from datetime conversion package warnings.filterwarnings("ignore") # define the number of days back # that you should retrieve the volume VOLUME_ANALYSIS_PERIOD = 9 # set if you want extra verbose # information about the process ............................... verbose = input("Verbose ? [y/N] ") if verbose != "" or verbose == "y" or verbose == "Y": verbose = True else: verbose = False # daily $$ target ............................................. daily_target = input("What is your daily target today ($) ? [$200] ") if daily_target == "": daily_target = float(200) else: daily_target = float(daily_target) print("Share size will be calculated for a profit of $%d using 80%% of daily ATR" % daily_target ) print(" ") # buying_power ................................................. buying_power = input("What is your Buying Power ($) [$4500] ? ") if buying_power == "": buying_power = float(4500) else: buying_power = float(buying_power) print("BP_check will check if \"PDC price x sh/target\" is smaller than your BP") # set a max column value pd.set_option('display.max_columns', 12) def get_volume(ticker,data): # filter data to actual time or defaults to openning price actual_data = data.between_time("04:00", "09:30") # get days index days = actual_data.index.to_period('D') # aggregate volume into days between the PM open and 9.30am final_data = actual_data.groupby(days).agg(['sum']) # output extra data if the header option is selected if verbose: print(final_data['Volume']) # show volume data from market open to current timestamp for X days volume = final_data['Volume'].agg('mean') return volume def get_avg_volume(ticker): # whole dataset from yahoo finance data = yf.download(tickers=ticker, interval='5m', progress=False, prepost=True, start=datetime.today() - timedelta(days=VOLUME_ANALYSIS_PERIOD), end=datetime.today() - timedelta(days=1)) if verbose: print(data) volume = get_volume(ticker, data) return volume def get_daily_volume(ticker): # get today's data for the ticker data = yf.download(tickers=ticker, interval='1m', progress=False, prepost=True, period="1d") if verbose: print(data) todays_vol = get_volume(ticker,data) return todays_vol def rvol(tickers): data = [] for ticker in tickers: avg_vol = get_avg_volume(ticker) todays_vol = get_daily_volume(ticker) relative_vol = todays_vol / avg_vol data.append({ 'ticker': ticker, 'rvol': "{:,.2f}".format(relative_vol[0]), 'tvol': str("{:,.1f}".format(todays_vol[0]/1000000))+" M", 'avg_vol': str("{:,.1f}".format(avg_vol[0]/1000000*2))+" M" }) #print("$%s rvol(%s) = %f" % (t, VOLUME_ANALYSIS_PERIOD, relative_vol)) return data def get_true_range(high, low, prv_close): return max(high, prv_close) - min(low, prv_close) def get_atr(ticker): data = yf.download(tickers=ticker, interval='1d', progress=False, prepost=True, start=datetime.today() - timedelta(days=30), end=datetime.today()) # add calculated column for True range data["pcl"] = data['Adj Close'].shift(1) data['TR'] = data.apply(lambda row: get_true_range(row['High'], row['Low'], row['pcl']), axis=1) data['TRp'] = data.TR / data.pcl * 100 data = data.dropna().tail(14) # output extra data if the header option is selected if verbose: print(data[['High','Low','Adj Close','pcl','TR', "TRp"]]) # get average true range from the period atr = data['TR'].agg(['mean']) last_close = data['Close'].tail(1) atrp = atr[0] / last_close[0] * 100 atrpx = last_close[0] + atr[0] atrpn = last_close[0] - atr[0] shares = math.ceil( float(daily_target) / round(atr[0]*0.8, 2) ) prev_day_close = round(last_close[0], 2) data = { 'ticker' : ticker, 'ATR(14)p' : str("{:,.2f}".format(atrp))+" %", 'ATR(14)' : '$'+str(round(atr[0],2)), '1/3 ATR SL' : '$' + str(round(atr[0]/3,2)), '1/4 ATR SL' : '$' + str(round(atr[0]/4,2)), 'max price' : round(atrpx,2), 'Resistance' : round(last_close[0] + (atr[0] / 2), 2), 'PDC' : prev_day_close, 'Support' : round(last_close[0] - (atr[0] / 2), 2), 'min price' : round(atrpn,2), 'max sh 4 BP' : int( buying_power / prev_day_close), 'sh/target' : shares } if (shares*prev_day_close > buying_power): data['BP_check'] = "X" else: data['BP_check'] = "ok" # print("$%s pClose = %.2f" % (ticker, last_close)) # print("$%s pATR(14) = %.2f%% -> max:%.2f -> min:%.2f" % (ticker, atrp, atrpx, atrpn)) return data def atr(tickers): data = [] for ticker in tickers: atr = get_atr(ticker) data.append(atr) # print("$%s ATR(14) = %f" % (ticker, avg_true_range)) return data tickers = "" last_tickers = "" while (True): print(" ") print("="*60) print(" ") if tickers != "": tickers = input("Tickers [%s]: " % last_tickers).upper() else: tickers = input("Tickers: ").upper() if (tickers.lower() == "q") or (tickers=="" and last_tickers==""): exit() if (tickers == "" and last_tickers != ""): tickers = last_tickers last_tickers = tickers rvols = rvol(tickers.split(" ")) atrs = atr(tickers.split(" ")) df1 = pd.DataFrame(data=atrs, columns=['ticker', 'ATR(14)p', 'ATR(14)', '1/3 ATR SL', '1/4 ATR SL', 'max price', 'Resistance', 'PDC', 'Support', 'min price', 'max sh 4 BP', 'sh/target', 'BP_check']) df1.set_index(['ticker'], inplace=True) df2 = pd.DataFrame(data=rvols, columns=['ticker', 'rvol', 'tvol', 'avg_vol']) df2.set_index(['ticker'], inplace=True) df = pd.merge(df2, df1, on="ticker") print(datetime.now()) print("") print(df.sort_values(['rvol', 'ATR(14)p'], ascending=[False, False])\ .transpose())