def test_insert_row(): df = pd.DataFrame( data={ "col1": [1, 2], "col2": [3, 4] }, index=["one", "two"], ) result_df = dcf_model.insert_row( name="three", index="two", df=df, row_v=[5, 6], ) expected_df = pd.DataFrame( data={ "col1": [1, 2, 5], "col2": [3, 4, 6] }, index=["one", "two", "three"], ) assert not result_df.empty pd.testing.assert_frame_equal(result_df, expected_df)
def get_data(self, statement: str, row: int, header: bool): URL = f"https://stockanalysis.com/stocks/{self.ticker}/financials/" if statement == "BS": URL += "balance-sheet/" title = "Balance Sheet" ignores = dcf_model.non_gaap_bs if statement == "CF": URL += "cash-flow-statement/" title = "Cash Flows" ignores = dcf_model.non_gaap_cf if statement == "IS": title = "Income Statement" ignores = dcf_model.non_gaap_is r = requests.get(URL, headers=dcf_model.headers) if "404 - Page Not Found" in r.text: raise ValueError( "The ticker given is not in the stock analysis website.") soup = BeautifulSoup(r.content, "html.parser") table = soup.find( "table", attrs={"class": "FinancialTable_table_financial__1RhYq"}) head = table.find("thead") columns = head.find_all("th") if self.years == []: self.years = [x.get_text().strip() for x in columns] self.len_data = len(self.years) - 1 if self.rounding == 0: phrase = soup.find("div", attrs={ "class": "text-sm pb-1 text-gray-600" }).get_text() if "thousand" in phrase: self.rounding = 1_000 elif "millions" in phrase: self.rounding = 1_000_000 elif "billions" in phrase: self.rounding = 1_000_000_000 else: raise ValueError( "Stock Analysis did not specify a proper rounding amount") body = table.find("tbody") rows = body.find_all("tr") all_data = [[x.get_text().strip() for x in y.find_all("td")] for y in rows] df = pd.DataFrame(data=all_data) df = df.set_index(0) n = df.shape[1] - self.len_data if n > 0: df = df.iloc[:, :-n] df.columns = self.years[1:] for ignore in ignores: if ignore in df.index: df = df.drop([ignore]) df = df[df.columns[::-1]] self.ws1[f"A{row}"] = title self.ws1[f"A{row}"].font = dcf_model.bold_font # Refactor in the future if statement == "IS": if "Revenue" in df.index: blank_list = ["0" for x in df.loc["Revenue"].to_list()] else: raise ValueError("Dataframe does not have key information.") for i, value in enumerate(dcf_model.gaap_is[1:]): df = dcf_model.insert_row(dcf_model.gaap_is[i + 1], dcf_model.gaap_is[i], df, blank_list) if statement == "BS": if "Cash & Equivalents" in df.index: blank_list = [ "0" for x in df.loc["Cash & Equivalents"].to_list() ] else: raise ValueError("Dataframe does not have key information.") for i, value in enumerate(dcf_model.gaap_bs[1:]): df = dcf_model.insert_row(dcf_model.gaap_bs[i + 1], dcf_model.gaap_bs[i], df, blank_list) if statement == "CF": if "Net Income" in df.index: blank_list = ["0" for x in df.loc["Net Income"].to_list()] else: raise ValueError("Dataframe does not have key information.") for i, value in enumerate(dcf_model.gaap_cf[1:]): df = dcf_model.insert_row(dcf_model.gaap_cf[i + 1], dcf_model.gaap_cf[i], df, blank_list) rowI = row + 1 names = df.index.values.tolist() for name in names: self.ws1[f"A{rowI}"] = name if name in dcf_model.sum_rows: length = self.len_data + (self.len_pred if statement != "CF" else 0) for i in range(length): if statement == "CF" and name == "Net Income": pass else: self.ws1[ f"{dcf_model.letters[i+1]}{rowI}"].font = dcf_model.bold_font self.ws1[ f"{dcf_model.letters[i+1]}{rowI}"].border = dcf_model.thin_border_top rowI += 1 column = 1 for key, value in df.iteritems(): rowI = row if header: dcf_model.set_cell( self.ws1, f"{dcf_model.letters[column]}{rowI}", float(key), font=dcf_model.bold_font, ) for item in value: rowI += 1 m = 0 if item is None else float(item.replace(",", "")) dcf_model.set_cell( self.ws1, f"{dcf_model.letters[column]}{rowI}", m, num_form=dcf_model.fmt_acct, ) column += 1 return df
def get_sister_data(self, statement: str, ticker: str) -> pd.DataFrame: URL = f"https://stockanalysis.com/stocks/{ticker}/financials/" if statement == "BS": URL += "balance-sheet/" ignores = dcf_model.non_gaap_bs if statement == "CF": URL += "cash-flow-statement/" ignores = dcf_model.non_gaap_cf if statement == "IS": ignores = dcf_model.non_gaap_is r = requests.get(URL, headers=dcf_model.headers) if "404 - Page Not Found" in r.text: # TODO: add better handling print("Unable to find requested sister ticker for ration analysis") raise ValueError("The ticker given is not in the stock analysis website.") soup = BeautifulSoup(r.content, "html.parser") table = soup.find( "table", attrs={"class": re.compile("^FinancialTable_table_financial__.*")} ) head = table.find("thead") if head is None: raise ValueError("Incorrect website format") columns = head.find_all("th") if self.years == []: self.years = [x.get_text().strip() for x in columns] self.len_data = len(self.years) - 1 if self.rounding == 0: phrase = soup.find( "div", attrs={"class": "text-sm pb-1 text-gray-600"} ).get_text() if "thousand" in phrase: self.rounding = 1_000 elif "millions" in phrase: self.rounding = 1_000_000 elif "billions" in phrase: self.rounding = 1_000_000_000 else: raise ValueError( "Stock Analysis did not specify a proper rounding amount" ) body = table.find("tbody") rows = body.find_all("tr") all_data = [[x.get_text().strip() for x in y.find_all("td")] for y in rows] df = pd.DataFrame(data=all_data) df = df.set_index(0) n = df.shape[1] - self.len_data if n > 0: df = df.iloc[:, :-n] df.columns = self.years[1:] for ignore in ignores: if ignore in df.index: df = df.drop([ignore]) df = df[df.columns[::-1]] if statement == "IS": vals = ["Revenue", dcf_model.gaap_is] elif statement == "BS": vals = ["Cash & Equivalents", dcf_model.gaap_bs] elif statement == "CF": vals = ["Net Income", dcf_model.gaap_cf] if vals[0] in df.index: blank_list = ["0" for _ in df.loc[vals[0]].to_list()] else: raise ValueError("Dataframe does not have key information.") for i, _ in enumerate(vals[1][1:]): df = dcf_model.insert_row(vals[1][i + 1], vals[1][i], df, blank_list) return df