mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 04:31:13 +00:00
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:
2
README.md
vendored
2
README.md
vendored
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,-
|
||||||
|
|||||||
|
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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 -
|
||||||
|
|||||||
Reference in New Issue
Block a user