def __init__(self, ticker): self.ticker = ticker.upper() os.environ["FMP_API_KEY"] = "606d643d87241cde956b5cd85a3c56d1" self.fmp = FMP() self.today = datetime.today() self.this_year = self.today.year self.dividends_raw = self.get_dividends_raw()
def test_get_quote_index(self, mock_request): fmp = FMP() file_path = self.get_file_from_name('mock_quote_index') with open(file_path) as f: mock_request.get(BASE_URL + "/quote/%5EGSPC?apikey=demo", text=f.read()) quote = fmp.get_index_quote('GSPC') self.assertIsInstance(quote, list)
def test_get_quote(self, mock_request): fmp = FMP() file_path = self.get_file_from_name('mock_quote') with open(file_path) as f: mock_request.get(BASE_URL + "/quote/JMCRX,JSMTX,JUESX?apikey=demo", text=f.read()) quote = fmp.get_quote('JMCRX,JSMTX,JUESX') self.assertIsInstance(quote, list)
def test_short_quote(self, mock_request): fmp = FMP() file_path = self.get_file_from_name('mock_quote_short') with open(file_path) as f: mock_request.get(BASE_URL + "/quote-short/AAPL?apikey=demo", text=f.read()) quote_short = fmp.get_quote_short('AAPL') self.assertIsInstance(quote_short, list)
def test_get_historical_price(self, mock_request): fmp = FMP() file_path = self.get_file_from_name('mock_historical_price') with open(file_path) as f: mock_request.get(BASE_URL + "/historical-price-full/JMCRX?apikey=demo", text=f.read()) quote = fmp.get_historical_price('JMCRX') self.assertIsInstance(quote, list)
def test_get_historical_chart_index(self, mock_request): fmp = FMP() file_path = self.get_file_from_name('mock_historical_chart_index') with open(file_path) as f: mock_request.get(BASE_URL + "/historical-chart/4hour/%5EGSPC?apikey=demo", text=f.read()) quote = fmp.get_historical_chart_index('4hour', 'GSPC') self.assertIsInstance(quote, list)
class DividendProcessor(object): def __init__(self, ticker): self.ticker = ticker.upper() os.environ["FMP_API_KEY"] = "606d643d87241cde956b5cd85a3c56d1" self.fmp = FMP() self.today = datetime.today() self.this_year = self.today.year self.dividends_raw = self.get_dividends_raw() def get_dividends_raw(self): # dividend, date - historical dividend values and dates self.dividends_raw = self.fmp.get_dividends(self.ticker, 'historical-price-full/stock_dividend') # set proper DateTime object as the index of the dataframe self.dividends_raw['Datetime'] = pd.to_datetime(self.dividends_raw['date']) try: # find days difference between rows. Needed to see data gap (for instane AAPL dividends) self.dividends_raw['Datetime_DaysDiffRows']=self.dividends_raw['Datetime'].diff().dt.days # find if there is more than 400 days passed between rows. If that happens, delete all rows below index_of_first_gap = np.where(self.dividends_raw['Datetime_DaysDiffRows'].lt(-400))[0][0] self.dividends_raw = self.dividends_raw.iloc[:index_of_first_gap] except: # no gaps found in datetime pass self.dividends_raw = self.dividends_raw.set_index('Datetime') self.dividends_raw["Symbol"] = self.ticker # export to csv. Use header only for the first time, then only append new rows # output_path = r'C:\Users\50000700\Python\Python_repos\dividends\excel_files\dividends_raw.csv' # self.dividends_raw.to_csv(output_path, mode='a', header=not os.path.exists(output_path)) print (self.dividends_raw) return self.dividends_raw def get_real_time_price(self): self.real_time_price = self.fmp.get_quote_short(self.ticker) print (f"REAL TIME PRICE {self.real_time_price}") return self.real_time_price def get_dividends_per_year(self): # resample annually and sum dividend values. Note that year end (Dec 31) will be shown for all groups dividends_per_year = self.dividends_raw.resample("A")["adjDividend"].sum() return dividends_per_year def get_dividend_frequency_of_prev_year(self): # calculates the last year's dividend frequency and returns how many times it was paid dividends = self.dividends_raw.resample("A")["date"].count() mask = (dividends.index > (str(self.this_year-1)+'-01-01')) & (dividends.index <= (str(self.this_year-1)+'-12-31')) dividends_filtered=dividends.loc[mask] return dividends_filtered.iloc[0] def get_dividend_dates_values_of_year(self, year): mask = (self.dividends_raw.index > (str(year)+'-01-01')) & (self.dividends_raw.index <= (str(year)+'-12-31')) dividends_filtered=self.dividends_raw.loc[mask] return dividends_filtered def get_dividend_months_of_year(self, year): mask = (self.dividends_raw.index > (str(year)+'-01-01')) & (self.dividends_raw.index <= (str(year)+'-12-31')) dividends_filtered_of_year=self.dividends_raw.loc[mask] list_of_div_months = dividends_filtered_of_year.index.tolist() list_of_div_months = [date.strftime("%m") for date in list_of_div_months] return list_of_div_months def is_div_frequency_same_for_years(self, new_year, old_year): # if no dividends paid - early in the year return same frequency as old year if len(self.get_dividend_months_of_year(new_year))==0: return True if all(elem in self.get_dividend_months_of_year(old_year) for elem in self.get_dividend_months_of_year(new_year)): return True else: raise ValueError(f"Dividend frequency of ticker {self.ticker} for years {new_year} and {old_year} don't match. Further calculation is not possible!") def get_forward_dividend(self): # take last dividend paid and multiply it with the dividend payment frequency last_dividend = self.dividends_raw.iloc[0]["adjDividend"] if self.is_div_frequency_same_for_years(self.this_year, self.this_year-1): return (last_dividend * self.get_dividend_frequency_of_prev_year()) else: raise ValueError(f"Dividend frequency of ticker {self.ticker} for years {self.this_year} and {self.this_year-1} don't match. Further calculation is not possible!") def get_dividend_yield(self): # returns dividend yield in % self.real_time_price = self.get_real_time_price() try: return (self.get_forward_dividend() / self.real_time_price.iloc[0]["price"] * 100) except: print ("No real time price data is available from API") return -1 def get_dividend_growth_per_year(self): # get dividend growth per year dividend_gr_per_yr = pd.DataFrame() # remove current year line as it cannot be complete and we are interested in historical data dividend_gr_per_yr['yearlyDividendValue'] = self.dividends_raw.resample("A")["adjDividend"].sum() dividend_gr_per_yr['dividendGrowth'] = dividend_gr_per_yr.pct_change() dividend_gr_per_yr = dividend_gr_per_yr.sort_index(ascending=False) # mask out row of current year mask = (dividend_gr_per_yr.index <= (str(self.this_year-1)+'-12-31')) dividend_gr_per_yr_filtered=dividend_gr_per_yr.loc[mask] return dividend_gr_per_yr_filtered def get_most_recent_dividend_cut_year(self): # result is the closest year from today when the dividend was cut last time. From that year he dividends are increasing again # if there is a dividend cut, then the yearly dividend growth rate will contain a negative growth for the particular cut year div_gr_per_year = self.get_dividend_growth_per_year() index = div_gr_per_year.dividendGrowth.lt(0).idxmax() # if index is the most recent date of the dataframe (first element), then set it to 1970-1-1 showing, that there was not a single cut in the dividends # print (f"first valid index {div_gr_per_year.first_valid_index()}") if index == div_gr_per_year.first_valid_index(): # if there is an unlikely event of the most recent year was a div cut (see ticker ABM), then return this most recent year print (f"first element of div grwth rate {div_gr_per_year.iloc[0][1]}") if div_gr_per_year.iloc[0][1] < 0: return index else: return datetime(1970, 1, 1) return index def get_DGR_3_5yr(self): # calculate DGR1yr, DGR3yr, DGR5yr and potentially DGR10yr # note, that CAGR calculation is used. http://www.moneychimp.com/features/cagr.htm # give it current dividend, old dividend and number of years # calculation gives uniform yearly growth value to reach final dividend # note that dripinvesting has mistakes in their input data and they will not always match with this result dividend_gr_per_yr = self.get_dividend_growth_per_year() years_to_check = [3,5,10,15] list_of_DGR = {} last_dividend_cut_year = self.get_most_recent_dividend_cut_year() print (f"first dividend cut year index from today {last_dividend_cut_year}") for year in years_to_check: mask = (dividend_gr_per_yr.index > (str(self.this_year-year)+'-01-01')) & (dividend_gr_per_yr.index <= (str(self.this_year-1)+'-12-31')) dividends_gr_x_yr=dividend_gr_per_yr.loc[mask] if len(dividends_gr_x_yr.index) == year and last_dividend_cut_year.year < (self.this_year-year) : div_current = dividends_gr_x_yr.iloc[0]["yearlyDividendValue"] div_old = dividends_gr_x_yr.iloc[-1]["yearlyDividendValue"] cagr = (((div_current / div_old) ** (1/year)) - 1) * 100 print (f"year {year} div_current {div_current} div_old {div_old} cagr {cagr}") list_of_DGR[year] = cagr return (list_of_DGR)
# read the excel file # combine rows 4-5 to be the header. By doing this the real pandas df will start from the header (and you don't need to drop rows..) # note that in case the top cell is empty, then it will use the closest top cell value to the left. # EXAMPLE: "TickerSymbol" column (col "B" in excel) name is correct. But the next column is empty top cell and "Sector" bottom cell (col "C" in excel). # Now it will be "TickerSector" df = pd.read_excel(listOfFiles[0], sheet_name='All CCC', header=[4, 5]) # flatten the multi index header: https://stackoverflow.com/questions/41931332/how-do-i-flatten-a-hierarchical-column-index-in-a-pandas-dataframe df.columns = df.columns.map(lambda x: ''.join([*map(str, x)])) # drop last rows of summary where not individual companies are shown but sector performance df = df.dropna(subset=['TickerSector']) ticker_list = df['TickerSymbol'].tolist() os.environ["FMP_API_KEY"] = "606d643d87241cde956b5cd85a3c56d1" fmp = FMP() def get_dividend_df_of(symbol): # dividend, date - historical dividend values and dates dividends_raw = fmp.get_dividends_and_stock_splits( symbol, 'historical-price-full/stock_dividend') # set proper DateTime object as the index of the dataframe dividends_raw['Datetime'] = pd.to_datetime(dividends_raw['date']) try: # find days difference between rows. Needed to see data gap (for instane AAPL dividends) dividends_raw['Datetime_DaysDiffRows'] = dividends_raw[ 'Datetime'].diff().dt.days # find if there is more than 400 days passed between rows. If that happens, delete all rows below index_of_first_gap = np.where(