Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed incorrect data display using stocks/fa/est command #4940

Merged
merged 7 commits into from
May 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ If you wish to install the OpenBB Terminal or the OpenBB SDK, please use one of
|:-|:-|
|[PyPI](https://docs.openbb.co/terminal/installation/pypi)|If you wish to use the OpenBB SDK in Python or Jupyter Notebooks|
|[Source](https://docs.openbb.co/terminal/installation/source)|If you wish to contribute to the development of the OpenBB Terminal|
nbsp;|
<!-- nbsp;| -->

## 2. Contributing

Expand Down
168 changes: 54 additions & 114 deletions openbb_terminal/stocks/fundamental_analysis/business_insider_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import json
import logging
import re
from typing import Tuple

import pandas as pd
Expand Down Expand Up @@ -193,120 +192,61 @@ def get_estimates(symbol: str) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame
"lxml",
)

l_estimates_year_header = list()
l_estimates_quarter_header = list()
for estimates_header in text_soup_market_business_insider.findAll(
"th", {"class": "table__th text-right"}
):
s_estimates_header = estimates_header.text.strip()
if s_estimates_header.isdigit():
l_estimates_year_header.append(s_estimates_header)
elif ("in %" not in s_estimates_header) and ("Job" not in s_estimates_header):
l_estimates_quarter_header.append(s_estimates_header)

l_estimates_year_metric = list()
for estimates_year_metric in text_soup_market_business_insider.findAll(
"td", {"class": "table__td black"}
):
l_estimates_year_metric.append(estimates_year_metric.text)

l_estimates_quarter_metric = list()
for estimates_quarter_metric in text_soup_market_business_insider.findAll(
"td", {"class": "table__td font-color-dim-gray"}
):
l_estimates_quarter_metric.append(estimates_quarter_metric.text)

d_metric_year = dict()
d_metric_quarter_earnings = dict()
d_metric_quarter_revenues = dict()
l_metrics = list()
n_metrics = 0
b_year = True
for idx, metric_value in enumerate(
text_soup_market_business_insider.findAll(
"td", {"class": "table__td text-right"}
)
):
if b_year:
# YEAR metrics
l_metrics.append(metric_value.text.strip())

# Check if we have processed all year metrics
if n_metrics > len(l_estimates_year_metric) - 1:
b_year = False
n_metrics = 0
l_metrics = list()
idx_y = idx

# Add value to dictionary
if (idx + 1) % len(l_estimates_year_header) == 0:
d_metric_year[l_estimates_year_metric[n_metrics]] = l_metrics
l_metrics = list()
n_metrics += 1

if not b_year:
# QUARTER metrics
l_metrics.append(metric_value.text.strip())

# Check if we have processed all quarter metrics
if n_metrics > len(l_estimates_quarter_metric) - 1:
break

# Add value to dictionary
if (idx - idx_y + 1) % len(l_estimates_quarter_header) == 0:
if n_metrics < 4:
d_metric_quarter_earnings[
l_estimates_quarter_metric[n_metrics]
] = l_metrics
else:
d_metric_quarter_revenues[
l_estimates_quarter_metric[n_metrics - 4]
] = l_metrics
l_metrics = list()
n_metrics += 1

df_year_estimates = pd.DataFrame.from_dict(
d_metric_year, orient="index", columns=l_estimates_year_header
)
df_year_estimates.index.name = "YEARLY ESTIMATES"
df_quarter_earnings = pd.DataFrame.from_dict(
d_metric_quarter_earnings,
orient="index",
columns=l_estimates_quarter_header,
# Get all tables and convert them to list of pandas dataframes
tables = text_soup_market_business_insider.find_all("table")
list_df = pd.read_html(str(tables))

# Get year estimates
df_year_estimates = list_df[3]
l_year_estimates_columns = df_year_estimates.columns.tolist()
l_year_estimates_columns[0] = "YEARLY ESTIMATES"
df_year_estimates.columns = l_year_estimates_columns
df_year_estimates.set_index("YEARLY ESTIMATES", inplace=True)

df_quarter = list_df[4]
date_row = dict()

# Get quarter earnings estimates
df_quarter_earnings = df_quarter.iloc[0:5, :].reset_index(drop=True).copy()
df_quarter_earnings.drop(index=0, inplace=True)
l_quarter_earnings_columns = df_quarter_earnings.columns.tolist()
l_quarter_earnings_columns[0] = "QUARTER EARNINGS ESTIMATES"
date_row["QUARTER EARNINGS ESTIMATES"] = "Date"

# Adding Date info to add to dataframe
for col in l_quarter_earnings_columns[1:]:
key = col.split("ending")[0].strip()
value = col[col.find("ending") :].strip()
date_row[key] = value

df_quarter_earnings.columns = date_row.keys()
date_row = pd.DataFrame(date_row, index=[0])
df_quarter_earnings = pd.concat([date_row, df_quarter_earnings]).reset_index(
drop=True
)
# df_quarter_earnings.index.name = 'Earnings'
df_quarter_revenues = pd.DataFrame.from_dict(
d_metric_quarter_revenues,
orient="index",
columns=l_estimates_quarter_header,
df_quarter_earnings.set_index("QUARTER EARNINGS ESTIMATES", inplace=True)

# Setting date_row to empty dict object
date_row = dict()

# Get quarter revenues estimates
df_quarter_revenues = df_quarter.iloc[5:, :].reset_index(drop=True).copy()
df_quarter_revenues.drop(index=0, inplace=True)
l_quarter_revenues_columns = df_quarter_revenues.columns.tolist()
l_quarter_revenues_columns[0] = "QUARTER REVENUES ESTIMATES"
date_row["QUARTER REVENUES ESTIMATES"] = "Date"

# Adding Date info to add to dataframe
for col in l_quarter_revenues_columns[1:]:
key = col.split("ending")[0].strip()
value = col[col.find("ending") :].strip()
date_row[key] = value

df_quarter_revenues.columns = date_row.keys()
date_row = pd.DataFrame(date_row, index=[0])
df_quarter_revenues = pd.concat([date_row, df_quarter_revenues]).reset_index(
drop=True
)
# df_quarter_revenues.index.name = 'Revenues'

if not df_quarter_earnings.empty:
l_quarter = list()
l_date = list()
for quarter_title in df_quarter_earnings.columns:
l_quarter.append(re.split(" ending", quarter_title)[0])
if len(re.split(" ending", quarter_title)) == 2:
l_date.append(
"ending " + re.split(" ending", quarter_title)[1].strip()
)
else:
l_date.append("-")

df_quarter_earnings.index.name = "QUARTER EARNINGS ESTIMATES"
df_quarter_earnings.columns = l_quarter
df_quarter_earnings.loc["Date"] = l_date
df_quarter_earnings = df_quarter_earnings.reindex(
["Date", "No. of Analysts", "Average Estimate", "Year Ago", "Publish Date"]
)

if not df_quarter_revenues.empty:
df_quarter_revenues.index.name = "QUARTER REVENUES ESTIMATES"
df_quarter_revenues.columns = l_quarter
df_quarter_revenues.loc["Date"] = l_date
df_quarter_revenues = df_quarter_revenues.reindex(
["Date", "No. of Analysts", "Average Estimate", "Year Ago", "Publish Date"]
)
df_quarter_revenues.set_index("QUARTER REVENUES ESTIMATES", inplace=True)

return df_year_estimates, df_quarter_earnings, df_quarter_revenues
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def display_estimates(
df_quarter_revenues,
) = business_insider_model.get_estimates(symbol)

if estimate == "annualearnings":
if estimate == "annual_earnings":
print_rich_table(
df_year_estimates,
headers=list(df_year_estimates.columns),
Expand All @@ -231,7 +231,7 @@ def display_estimates(
sheet_name,
)

elif estimate == "quarterearnings":
elif estimate == "quarter_earnings":
print_rich_table(
df_quarter_earnings,
headers=list(df_quarter_earnings.columns),
Expand All @@ -247,7 +247,7 @@ def display_estimates(
sheet_name,
)

elif estimate == "annualrevenue":
elif estimate == "quarter_revenues":
print_rich_table(
df_quarter_revenues,
headers=list(df_quarter_revenues.columns),
Expand Down
4 changes: 2 additions & 2 deletions openbb_terminal/stocks/fundamental_analysis/fa_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class FundamentalAnalysisController(StockBaseController):
PATH = "/stocks/fa/"

SHRS_CHOICES = ["major", "institutional", "mutualfund"]
ESTIMATE_CHOICES = ["annualrevenue", "annualearnings", "quarterearnings"]
ESTIMATE_CHOICES = ["annual_earnings", "quarter_earnings", "quarter_revenues"]
CHOICES_GENERATION = True

def __init__(
Expand Down Expand Up @@ -1858,7 +1858,7 @@ def call_est(self, other_args: List[str]):
help="Estimates to get",
dest="estimate",
choices=self.ESTIMATE_CHOICES,
default="annualearnings",
default="annual_earnings",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
YEARLY ESTIMATES,2023,2024,2025,2026,2027
Revenue,"103,351","135,186","168,507",-,-
Revenue,103351,135186,168507,-,-
Dividend,0.00,0.00,0.00,-,-
Dividend Yield (in %),-,-,-,-,-
EPS,4.08,5.65,6.91,5.83,6.98
P/E Ratio,45.95,33.21,27.13,32.17,26.86
EBIT,"14,600","21,973","27,740","30,032","44,029"
EBITDA,"19,429","27,963","38,063","37,754","38,937"
Net Profit,"13,681","19,370","23,763","21,385","26,126"
Net Profit Adjusted,"13,713","19,366","23,746","21,385","26,126"
Pre-Tax Profit,"14,437","22,015","28,932","29,404","31,787"
Net Profit (Adjusted),"14,363","22,392","29,878","29,404","31,787"
EBIT,14600,21973,27740,30032,44029
EBITDA,19429,27963,38063,37754,38937
Net Profit,13681,19370,23763,21385,26126
Net Profit Adjusted,13713,19366,23746,21385,26126
Pre-Tax Profit,14437,22015,28932,29404,31787
Net Profit (Adjusted),14363,22392,29878,29404,31787
EPS (Non-GAAP) ex. SOE,4.08,5.65,6.91,5.83,6.98
EPS (GAAP),3.49,5.25,6.18,5.73,6.65
Gross Income,"23,184","32,468","40,980","37,949","42,464"
Cash Flow from Investing,"-7,935","-9,482","-10,201","-12,681",-
Cash Flow from Operations,"19,523","24,129","27,935","29,376","35,875"
Gross Income,23184,32468,40980,37949,42464
Cash Flow from Investing,-7935,-9482,-10201,-12681,-
Cash Flow from Operations,19523,24129,27935,29376,35875
Cash Flow from Financing,-478,-810,100,-,-
Cash Flow per Share,4.92,7.39,6.94,-,-
Free Cash Flow,"10,236","14,300","15,203","18,405","22,501"
Free Cash Flow,10236,14300,15203,18405,22501
Free Cash Flow per Share,2.84,4.55,4.61,6.10,-
Book Value per Share,17.00,22.50,28.90,31.25,-
Net Debt,"-24,814","-36,525","-52,052",-,-
Research & Development Exp.,"3,565","4,327","4,852","5,192","5,250"
Capital Expenditure,"7,692","8,522","8,950","9,832",-
"Selling, General & Admin. Exp.","4,614","5,569","6,495","6,145","5,725"
Shareholder’s Equity,"57,825","76,028","97,431","99,807",-
Total Assets,"99,781","126,117","153,798","155,216",-
Net Debt,-24814,-36525,-52052,-,-
Research & Development Exp.,3565,4327,4852,5192,5250
Capital Expenditure,7692,8522,8950,9832,-
"Selling, General & Admin. Exp.",4614,5569,6495,6145,5725
Shareholder’s Equity,57825,76028,97431,99807,-
Total Assets,99781,126117,153798,155216,-
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,5 @@ def test_price_target_from_analysts_plt():
@pytest.mark.record_stdout
def test_estimates():
business_insider_view.display_estimates(
symbol="TSLA", estimate="annualearnings", export=None
symbol="TSLA", estimate="annual_earnings", export=None
)
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ def test_call_func_expect_queue(expected_queue, queue, func):
[],
{
"symbol": "TSLA",
"estimate": "annualearnings",
"estimate": "annual_earnings",
"export": "",
"sheet_name": None,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
2023 2024 2025 2026 2027
YEARLY ESTIMATES
Revenue 103,418 135,351 168,507 194,661 295,169
Dividend 0.00 0.00 0.00 - -
Dividend Yield (in %) - - - - -
EPS 4.07 5.65 6.91 5.83 6.89
P/E Ratio 50.51 36.42 29.74 35.29 29.83
EBIT 14,555 21,972 27,741 30,025 43,704
EBITDA 19,519 27,962 38,066 37,741 38,504
Net Profit 13,506 19,372 23,768 21,372 25,801
Net Profit Adjusted 13,533 19,368 23,752 21,372 25,801
Pre-Tax Profit 14,454 22,034 28,932 29,404 31,787
Net Profit (Adjusted) 14,363 22,392 29,878 29,404 31,787
EPS (Non-GAAP) ex. SOE 4.07 5.65 6.91 5.83 6.89
EPS (GAAP) 3.47 5.25 6.18 5.72 6.54
Gross Income 23,115 32,466 40,982 37,942 41,972
Cash Flow from Investing -7,935 -9,482 -10,201 -12,681 -
Cash Flow from Operations 19,523 24,129 27,935 29,376 35,875
Cash Flow from Financing -478 -810 100 - -
Cash Flow per Share 4.92 7.39 6.94 - -
Free Cash Flow 10,541 14,300 15,203 18,405 22,501
Free Cash Flow per Share 2.84 4.55 4.61 6.10 -
Book Value per Share 17.00 22.50 28.90 31.25 -
Net Debt -24,814 -36,525 -52,052 - -
Research & Development Exp. 3,555 4,325 4,846 5,179 5,162
Capital Expenditure 7,672 8,522 8,950 9,832 -
Selling, General & Admin. Exp. 4,618 5,572 6,500 6,160 5,755
Shareholder’s Equity 57,825 76,028 97,431 99,807 -
Total Assets 99,781 126,117 153,798 155,216 -
2023 2024 2025 2026 2027
YEARLY ESTIMATES
Revenue 103418 135351 168507 194661 295169
Dividend 0.00 0.00 0.00 - -
Dividend Yield (in %) - - - - -
EPS 4.07 5.65 6.91 5.83 6.89
P/E Ratio 50.51 36.42 29.74 35.29 29.83
EBIT 14555 21972 27741 30025 43704
EBITDA 19519 27962 38066 37741 38504
Net Profit 13506 19372 23768 21372 25801
Net Profit Adjusted 13533 19368 23752 21372 25801
Pre-Tax Profit 14454 22034 28932 29404 31787
Net Profit (Adjusted) 14363 22392 29878 29404 31787
EPS (Non-GAAP) ex. SOE 4.07 5.65 6.91 5.83 6.89
EPS (GAAP) 3.47 5.25 6.18 5.72 6.54
Gross Income 23115 32466 40982 37942 41972
Cash Flow from Investing -7935 -9482 -10201 -12681 -
Cash Flow from Operations 19523 24129 27935 29376 35875
Cash Flow from Financing -478 -810 100 - -
Cash Flow per Share 4.92 7.39 6.94 - -
Free Cash Flow 10541 14300 15203 18405 22501
Free Cash Flow per Share 2.84 4.55 4.61 6.10 -
Book Value per Share 17.00 22.50 28.90 31.25 -
Net Debt -24814 -36525 -52052 - -
Research & Development Exp. 3555 4325 4846 5179 5162
Capital Expenditure 7672 8522 8950 9832 -
Selling, General & Admin. Exp. 4618 5572 6500 6160 5755
Shareholder’s Equity 57825 76028 97431 99807 -
Total Assets 99781 126117 153798 155216 -