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

* Removed empty row in OpenBB SDK table under Installation

* Revamped `get_estimates` function

* Linting

* Fixed bugs, tests and modified args

* Fixed test
This commit is contained in:
Pratyush Shukla
2023-05-02 01:53:59 +05:30
committed by GitHub
parent c603a360ec
commit 3d7b934d16
8 changed files with 103 additions and 163 deletions

2
README.md vendored
View File

@@ -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| |[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| |[Source](https://docs.openbb.co/terminal/installation/source)|If you wish to contribute to the development of the OpenBB Terminal|
nbsp;| <!-- nbsp;| -->
## 2. Contributing ## 2. Contributing

View File

@@ -3,7 +3,6 @@ __docformat__ = "numpy"
import json import json
import logging import logging
import re
from typing import Tuple from typing import Tuple
import pandas as pd import pandas as pd
@@ -193,120 +192,61 @@ def get_estimates(symbol: str) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame
"lxml", "lxml",
) )
l_estimates_year_header = list() # Get all tables and convert them to list of pandas dataframes
l_estimates_quarter_header = list() tables = text_soup_market_business_insider.find_all("table")
for estimates_header in text_soup_market_business_insider.findAll( list_df = pd.read_html(str(tables))
"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() # Get year estimates
for estimates_year_metric in text_soup_market_business_insider.findAll( df_year_estimates = list_df[3]
"td", {"class": "table__td black"} l_year_estimates_columns = df_year_estimates.columns.tolist()
): l_year_estimates_columns[0] = "YEARLY ESTIMATES"
l_estimates_year_metric.append(estimates_year_metric.text) df_year_estimates.columns = l_year_estimates_columns
df_year_estimates.set_index("YEARLY ESTIMATES", inplace=True)
l_estimates_quarter_metric = list() df_quarter = list_df[4]
for estimates_quarter_metric in text_soup_market_business_insider.findAll( date_row = dict()
"td", {"class": "table__td font-color-dim-gray"}
):
l_estimates_quarter_metric.append(estimates_quarter_metric.text)
d_metric_year = dict() # Get quarter earnings estimates
d_metric_quarter_earnings = dict() df_quarter_earnings = df_quarter.iloc[0:5, :].reset_index(drop=True).copy()
d_metric_quarter_revenues = dict() df_quarter_earnings.drop(index=0, inplace=True)
l_metrics = list() l_quarter_earnings_columns = df_quarter_earnings.columns.tolist()
n_metrics = 0 l_quarter_earnings_columns[0] = "QUARTER EARNINGS ESTIMATES"
b_year = True date_row["QUARTER EARNINGS ESTIMATES"] = "Date"
for idx, metric_value in enumerate(
text_soup_market_business_insider.findAll( # Adding Date info to add to dataframe
"td", {"class": "table__td text-right"} 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.set_index("QUARTER EARNINGS ESTIMATES", inplace=True)
if b_year:
# YEAR metrics
l_metrics.append(metric_value.text.strip())
# Check if we have processed all year metrics # Setting date_row to empty dict object
if n_metrics > len(l_estimates_year_metric) - 1: date_row = dict()
b_year = False
n_metrics = 0
l_metrics = list()
idx_y = idx
# Add value to dictionary # Get quarter revenues estimates
if (idx + 1) % len(l_estimates_year_header) == 0: df_quarter_revenues = df_quarter.iloc[5:, :].reset_index(drop=True).copy()
d_metric_year[l_estimates_year_metric[n_metrics]] = l_metrics df_quarter_revenues.drop(index=0, inplace=True)
l_metrics = list() l_quarter_revenues_columns = df_quarter_revenues.columns.tolist()
n_metrics += 1 l_quarter_revenues_columns[0] = "QUARTER REVENUES ESTIMATES"
date_row["QUARTER REVENUES ESTIMATES"] = "Date"
if not b_year: # Adding Date info to add to dataframe
# QUARTER metrics for col in l_quarter_revenues_columns[1:]:
l_metrics.append(metric_value.text.strip()) key = col.split("ending")[0].strip()
value = col[col.find("ending") :].strip()
date_row[key] = value
# Check if we have processed all quarter metrics df_quarter_revenues.columns = date_row.keys()
if n_metrics > len(l_estimates_quarter_metric) - 1: date_row = pd.DataFrame(date_row, index=[0])
break df_quarter_revenues = pd.concat([date_row, df_quarter_revenues]).reset_index(
drop=True
# 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,
)
# 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_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 return df_year_estimates, df_quarter_earnings, df_quarter_revenues

View File

@@ -215,7 +215,7 @@ def display_estimates(
df_quarter_revenues, df_quarter_revenues,
) = business_insider_model.get_estimates(symbol) ) = business_insider_model.get_estimates(symbol)
if estimate == "annualearnings": if estimate == "annual_earnings":
print_rich_table( print_rich_table(
df_year_estimates, df_year_estimates,
headers=list(df_year_estimates.columns), headers=list(df_year_estimates.columns),
@@ -231,7 +231,7 @@ def display_estimates(
sheet_name, sheet_name,
) )
elif estimate == "quarterearnings": elif estimate == "quarter_earnings":
print_rich_table( print_rich_table(
df_quarter_earnings, df_quarter_earnings,
headers=list(df_quarter_earnings.columns), headers=list(df_quarter_earnings.columns),
@@ -247,7 +247,7 @@ def display_estimates(
sheet_name, sheet_name,
) )
elif estimate == "annualrevenue": elif estimate == "quarter_revenues":
print_rich_table( print_rich_table(
df_quarter_revenues, df_quarter_revenues,
headers=list(df_quarter_revenues.columns), headers=list(df_quarter_revenues.columns),

View File

@@ -92,7 +92,7 @@ class FundamentalAnalysisController(StockBaseController):
PATH = "/stocks/fa/" PATH = "/stocks/fa/"
SHRS_CHOICES = ["major", "institutional", "mutualfund"] SHRS_CHOICES = ["major", "institutional", "mutualfund"]
ESTIMATE_CHOICES = ["annualrevenue", "annualearnings", "quarterearnings"] ESTIMATE_CHOICES = ["annual_earnings", "quarter_earnings", "quarter_revenues"]
CHOICES_GENERATION = True CHOICES_GENERATION = True
def __init__( def __init__(
@@ -1858,7 +1858,7 @@ class FundamentalAnalysisController(StockBaseController):
help="Estimates to get", help="Estimates to get",
dest="estimate", dest="estimate",
choices=self.ESTIMATE_CHOICES, choices=self.ESTIMATE_CHOICES,
default="annualearnings", default="annual_earnings",
) )
ns_parser = self.parse_known_args_and_warn( ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED

View File

@@ -1,28 +1,28 @@
YEARLY ESTIMATES,2023,2024,2025,2026,2027 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,0.00,0.00,0.00,-,-
Dividend Yield (in %),-,-,-,-,- Dividend Yield (in %),-,-,-,-,-
EPS,4.08,5.65,6.91,5.83,6.98 EPS,4.08,5.65,6.91,5.83,6.98
P/E Ratio,45.95,33.21,27.13,32.17,26.86 P/E Ratio,45.95,33.21,27.13,32.17,26.86
EBIT,"14,600","21,973","27,740","30,032","44,029" EBIT,14600,21973,27740,30032,44029
EBITDA,"19,429","27,963","38,063","37,754","38,937" EBITDA,19429,27963,38063,37754,38937
Net Profit,"13,681","19,370","23,763","21,385","26,126" Net Profit,13681,19370,23763,21385,26126
Net Profit Adjusted,"13,713","19,366","23,746","21,385","26,126" Net Profit Adjusted,13713,19366,23746,21385,26126
Pre-Tax Profit,"14,437","22,015","28,932","29,404","31,787" Pre-Tax Profit,14437,22015,28932,29404,31787
Net Profit (Adjusted),"14,363","22,392","29,878","29,404","31,787" Net Profit (Adjusted),14363,22392,29878,29404,31787
EPS (Non-GAAP) ex. SOE,4.08,5.65,6.91,5.83,6.98 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 EPS (GAAP),3.49,5.25,6.18,5.73,6.65
Gross Income,"23,184","32,468","40,980","37,949","42,464" Gross Income,23184,32468,40980,37949,42464
Cash Flow from Investing,"-7,935","-9,482","-10,201","-12,681",- Cash Flow from Investing,-7935,-9482,-10201,-12681,-
Cash Flow from Operations,"19,523","24,129","27,935","29,376","35,875" Cash Flow from Operations,19523,24129,27935,29376,35875
Cash Flow from Financing,-478,-810,100,-,- Cash Flow from Financing,-478,-810,100,-,-
Cash Flow per Share,4.92,7.39,6.94,-,- 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,- 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,- Book Value per Share,17.00,22.50,28.90,31.25,-
Net Debt,"-24,814","-36,525","-52,052",-,- Net Debt,-24814,-36525,-52052,-,-
Research & Development Exp.,"3,565","4,327","4,852","5,192","5,250" Research & Development Exp.,3565,4327,4852,5192,5250
Capital Expenditure,"7,692","8,522","8,950","9,832",- Capital Expenditure,7692,8522,8950,9832,-
"Selling, General & Admin. Exp.","4,614","5,569","6,495","6,145","5,725" "Selling, General & Admin. Exp.",4614,5569,6495,6145,5725
Shareholder’s Equity,"57,825","76,028","97,431","99,807",- Shareholder’s Equity,57825,76028,97431,99807,-
Total Assets,"99,781","126,117","153,798","155,216",- Total Assets,99781,126117,153798,155216,-
1 YEARLY ESTIMATES 2023 2024 2025 2026 2027
2 Revenue 103,351 103351 135,186 135186 168,507 168507 - -
3 Dividend 0.00 0.00 0.00 - -
4 Dividend Yield (in %) - - - - -
5 EPS 4.08 5.65 6.91 5.83 6.98
6 P/E Ratio 45.95 33.21 27.13 32.17 26.86
7 EBIT 14,600 14600 21,973 21973 27,740 27740 30,032 30032 44,029 44029
8 EBITDA 19,429 19429 27,963 27963 38,063 38063 37,754 37754 38,937 38937
9 Net Profit 13,681 13681 19,370 19370 23,763 23763 21,385 21385 26,126 26126
10 Net Profit Adjusted 13,713 13713 19,366 19366 23,746 23746 21,385 21385 26,126 26126
11 Pre-Tax Profit 14,437 14437 22,015 22015 28,932 28932 29,404 29404 31,787 31787
12 Net Profit (Adjusted) 14,363 14363 22,392 22392 29,878 29878 29,404 29404 31,787 31787
13 EPS (Non-GAAP) ex. SOE 4.08 5.65 6.91 5.83 6.98
14 EPS (GAAP) 3.49 5.25 6.18 5.73 6.65
15 Gross Income 23,184 23184 32,468 32468 40,980 40980 37,949 37949 42,464 42464
16 Cash Flow from Investing -7,935 -7935 -9,482 -9482 -10,201 -10201 -12,681 -12681 -
17 Cash Flow from Operations 19,523 19523 24,129 24129 27,935 27935 29,376 29376 35,875 35875
18 Cash Flow from Financing -478 -810 100 - -
19 Cash Flow per Share 4.92 7.39 6.94 - -
20 Free Cash Flow 10,236 10236 14,300 14300 15,203 15203 18,405 18405 22,501 22501
21 Free Cash Flow per Share 2.84 4.55 4.61 6.10 -
22 Book Value per Share 17.00 22.50 28.90 31.25 -
23 Net Debt -24,814 -24814 -36,525 -36525 -52,052 -52052 - -
24 Research & Development Exp. 3,565 3565 4,327 4327 4,852 4852 5,192 5192 5,250 5250
25 Capital Expenditure 7,692 7692 8,522 8522 8,950 8950 9,832 9832 -
26 Selling, General & Admin. Exp. 4,614 4614 5,569 5569 6,495 6495 6,145 6145 5,725 5725
27 Shareholder’s Equity 57,825 57825 76,028 76028 97,431 97431 99,807 99807 -
28 Total Assets 99,781 99781 126,117 126117 153,798 153798 155,216 155216 -

View File

@@ -83,5 +83,5 @@ def test_price_target_from_analysts_plt():
@pytest.mark.record_stdout @pytest.mark.record_stdout
def test_estimates(): def test_estimates():
business_insider_view.display_estimates( business_insider_view.display_estimates(
symbol="TSLA", estimate="annualearnings", export=None symbol="TSLA", estimate="annual_earnings", export=None
) )

View File

@@ -575,7 +575,7 @@ def test_call_func_expect_queue(expected_queue, queue, func):
[], [],
{ {
"symbol": "TSLA", "symbol": "TSLA",
"estimate": "annualearnings", "estimate": "annual_earnings",
"export": "", "export": "",
"sheet_name": None, "sheet_name": None,
}, },

View File

@@ -1,29 +1,29 @@
2023 2024 2025 2026 2027 2023 2024 2025 2026 2027
YEARLY ESTIMATES YEARLY ESTIMATES
Revenue 103,418 135,351 168,507 194,661 295,169 Revenue 103418 135351 168507 194661 295169
Dividend 0.00 0.00 0.00 - - Dividend 0.00 0.00 0.00 - -
Dividend Yield (in %) - - - - - Dividend Yield (in %) - - - - -
EPS 4.07 5.65 6.91 5.83 6.89 EPS 4.07 5.65 6.91 5.83 6.89
P/E Ratio 50.51 36.42 29.74 35.29 29.83 P/E Ratio 50.51 36.42 29.74 35.29 29.83
EBIT 14,555 21,972 27,741 30,025 43,704 EBIT 14555 21972 27741 30025 43704
EBITDA 19,519 27,962 38,066 37,741 38,504 EBITDA 19519 27962 38066 37741 38504
Net Profit 13,506 19,372 23,768 21,372 25,801 Net Profit 13506 19372 23768 21372 25801
Net Profit Adjusted 13,533 19,368 23,752 21,372 25,801 Net Profit Adjusted 13533 19368 23752 21372 25801
Pre-Tax Profit 14,454 22,034 28,932 29,404 31,787 Pre-Tax Profit 14454 22034 28932 29404 31787
Net Profit (Adjusted) 14,363 22,392 29,878 29,404 31,787 Net Profit (Adjusted) 14363 22392 29878 29404 31787
EPS (Non-GAAP) ex. SOE 4.07 5.65 6.91 5.83 6.89 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 EPS (GAAP) 3.47 5.25 6.18 5.72 6.54
Gross Income 23,115 32,466 40,982 37,942 41,972 Gross Income 23115 32466 40982 37942 41972
Cash Flow from Investing -7,935 -9,482 -10,201 -12,681 - Cash Flow from Investing -7935 -9482 -10201 -12681 -
Cash Flow from Operations 19,523 24,129 27,935 29,376 35,875 Cash Flow from Operations 19523 24129 27935 29376 35875
Cash Flow from Financing -478 -810 100 - - Cash Flow from Financing -478 -810 100 - -
Cash Flow per Share 4.92 7.39 6.94 - - 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 10541 14300 15203 18405 22501
Free Cash Flow per Share 2.84 4.55 4.61 6.10 - 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 - Book Value per Share 17.00 22.50 28.90 31.25 -
Net Debt -24,814 -36,525 -52,052 - - Net Debt -24814 -36525 -52052 - -
Research & Development Exp. 3,555 4,325 4,846 5,179 5,162 Research & Development Exp. 3555 4325 4846 5179 5162
Capital Expenditure 7,672 8,522 8,950 9,832 - Capital Expenditure 7672 8522 8950 9832 -
Selling, General & Admin. Exp. 4,618 5,572 6,500 6,160 5,755 Selling, General & Admin. Exp. 4618 5572 6500 6160 5755
Shareholder’s Equity 57,825 76,028 97,431 99,807 - Shareholder’s Equity 57825 76028 97431 99807 -
Total Assets 99,781 126,117 153,798 155,216 - Total Assets 99781 126117 153798 155216 -