From 3d5cbcb2aeb97e4c6a88305320dabb8a9ccfb228 Mon Sep 17 00:00:00 2001 From: Colin Delahunty <72827203+colin99d@users.noreply.github.com> Date: Sat, 12 Mar 2022 14:28:25 -0500 Subject: [PATCH] Extended Dupont (#1475) * Added dupont, fixed fraud * Added dupont to scripts * Added better handling * Added test fixes Co-authored-by: jmaslek --- .../stocks/fundamental_analysis/av_model.py | 84 ++++++- .../stocks/fundamental_analysis/av_view.py | 19 ++ .../fundamental_analysis/fa_controller.py | 19 +- gamestonk_terminal/stocks/stocks_helper.py | 21 ++ scripts/test_stocks_fa.gst | 3 +- .../test_av_model/test_get_dupont.yaml | 218 ++++++++++++++++++ .../csv/test_av_model/test_get_dupont.csv | 7 + .../fundamental_analysis/test_av_model.py | 11 +- .../fundamental_analysis/test_av_view.py | 16 ++ .../test_fa_controller.py | 6 + .../txt/test_av_view/test_dupont[df0].txt | 2 + .../test_fa_controller/test_print_help.txt | 1 + .../fundamental_analysis/dupont/_index.md | 10 + .../fundamental_analysis/fraud/_index.md | 2 +- 14 files changed, 403 insertions(+), 16 deletions(-) create mode 100644 tests/gamestonk_terminal/stocks/fundamental_analysis/cassettes/test_av_model/test_get_dupont.yaml create mode 100644 tests/gamestonk_terminal/stocks/fundamental_analysis/csv/test_av_model/test_get_dupont.csv create mode 100644 tests/gamestonk_terminal/stocks/fundamental_analysis/txt/test_av_view/test_dupont[df0].txt create mode 100644 website/content/terminal/stocks/fundamental_analysis/dupont/_index.md diff --git a/gamestonk_terminal/stocks/fundamental_analysis/av_model.py b/gamestonk_terminal/stocks/fundamental_analysis/av_model.py index 9cf700b8f2d..f3ba3bbd062 100644 --- a/gamestonk_terminal/stocks/fundamental_analysis/av_model.py +++ b/gamestonk_terminal/stocks/fundamental_analysis/av_model.py @@ -2,7 +2,7 @@ __docformat__ = "numpy" import logging -from typing import Dict, List, Optional, Tuple +from typing import Dict, List import numpy as np import pandas as pd @@ -12,6 +12,7 @@ from gamestonk_terminal import config_terminal as cfg from gamestonk_terminal.decorators import log_start_end from gamestonk_terminal.helper_funcs import lambda_long_number_format from gamestonk_terminal.rich_config import console +from gamestonk_terminal.stocks.stocks_helper import clean_fraction from gamestonk_terminal.stocks.fundamental_analysis.fa_helper import clean_df_index logger = logging.getLogger(__name__) @@ -368,7 +369,9 @@ def get_earnings(ticker: str, quarterly: bool = False) -> pd.DataFrame: @log_start_end(log=logger) -def df_values(df: pd.DataFrame, item: str, index: Optional[int] = None) -> List[int]: +def df_values( + df: pd.DataFrame, item: str, index: int = 0, length: int = 2 +) -> List[int]: """Clean the values from the df Parameters @@ -377,6 +380,10 @@ def df_values(df: pd.DataFrame, item: str, index: Optional[int] = None) -> List[ The Dataframe to use item : str The item to select + index : int + The number of row to display + length : int + The number of rows to return Returns ------- @@ -384,16 +391,16 @@ def df_values(df: pd.DataFrame, item: str, index: Optional[int] = None) -> List[ The values for the dataframe """ if index: - df = df.iloc[index : index + 2] + df = df.iloc[index : index + length] selection = df[item] - values = selection.apply(lambda x: int(x) if x else 0).values + values = selection.apply( + lambda x: "N/A" if (not x or x == "None") else int(x) + ).values return values.tolist() @log_start_end(log=logger) -def get_fraud_ratios( - ticker: str, -) -> Tuple[Optional[Dict[str, float]], Optional[float], Optional[float]]: +def get_fraud_ratios(ticker: str) -> pd.DataFrame: """Get fraud ratios based on fundamentals Parameters @@ -403,10 +410,8 @@ def get_fraud_ratios( Returns ------- - Dict[float]: - Dictionary of fraud metrics - float: - Z score for fraud metrics + metrics : pd.DataFrame + The fraud ratios """ try: @@ -486,4 +491,61 @@ def get_fraud_ratios( if fraud_years.empty: fraud_years.index = ratios.keys() fraud_years[df_cf.index[i]] = ratios.values() + fraud_years = fraud_years[sorted(fraud_years)] return fraud_years + + +@log_start_end(log=logger) +def get_dupont(ticker: str) -> pd.DataFrame: + """Get dupont ratios + + Parameters + ---------- + ticker : str + Stock ticker + + Returns + ------- + dupont : pd.DataFrame + The dupont ratio breakdown + """ + + try: + fd = FundamentalData(key=cfg.API_KEY_ALPHAVANTAGE, output_format="pandas") + # pylint: disable=unbalanced-tuple-unpacking + df_bs, _ = fd.get_balance_sheet_annual(symbol=ticker) + df_is, _ = fd.get_income_statement_annual(symbol=ticker) + + except Exception as e: + console.print(e) + return pd.DataFrame() + + # pylint: disable=no-member + df_bs = df_bs.set_index("fiscalDateEnding") + df_is = df_is.set_index("fiscalDateEnding") + dupont_years = pd.DataFrame() + + for i in range(len(df_bs)): + ni = df_values(df_is, "netIncome", i, 1) + pretax = df_values(df_is, "incomeBeforeTax", i, 1) + ebit = df_values(df_is, "ebit", i, 1) + sales = df_values(df_is, "totalRevenue", i, 1) + assets = df_values(df_bs, "totalAssets", i, 1) + equity = df_values(df_bs, "totalShareholderEquity", i, 1) + + ratios: Dict = {} + try: + ratios["Tax Burden"] = clean_fraction(ni[0], pretax[0]) + ratios["Interest Burden"] = clean_fraction(pretax[0], ebit[0]) + ratios["EBIT Margin"] = clean_fraction(ebit[0], sales[0]) + ratios["Asset Turnover"] = clean_fraction(sales[0], assets[0]) + ratios["Finance Leverage"] = clean_fraction(assets[0], equity[0]) + ratios["ROI"] = clean_fraction(ni[0], equity[0]) + except IndexError: + pass + + if dupont_years.empty: + dupont_years.index = ratios.keys() + dupont_years[df_bs.index[i]] = ratios.values() + dupont_years = dupont_years[sorted(dupont_years)] + return dupont_years diff --git a/gamestonk_terminal/stocks/fundamental_analysis/av_view.py b/gamestonk_terminal/stocks/fundamental_analysis/av_view.py index 725edcaf0c9..28fc6b91f9e 100644 --- a/gamestonk_terminal/stocks/fundamental_analysis/av_view.py +++ b/gamestonk_terminal/stocks/fundamental_analysis/av_view.py @@ -231,3 +231,22 @@ A mckee less than 0.5 indicates a high risk of fraud. if help_text: console.print(help_message) + + +@log_start_end(log=logger) +@check_api_key(["API_KEY_ALPHAVANTAGE"]) +def display_dupont(ticker: str): + """Shows the extended dupont ratio + + Parameters + ---------- + ticker : str + Fundamental analysis ticker symbol + """ + df = av_model.get_dupont(ticker) + if df.empty: + console.print("[red]Invalid response from AlphaVantage[/red]\n") + else: + print_rich_table( + df, headers=list(df.columns), show_index=True, title="Extended Dupont" + ) diff --git a/gamestonk_terminal/stocks/fundamental_analysis/fa_controller.py b/gamestonk_terminal/stocks/fundamental_analysis/fa_controller.py index e59c9234dd4..77d764c5e75 100644 --- a/gamestonk_terminal/stocks/fundamental_analysis/fa_controller.py +++ b/gamestonk_terminal/stocks/fundamental_analysis/fa_controller.py @@ -71,6 +71,7 @@ class FundamentalAnalysisController(StockBaseController): "fraud", "dcf", "mktcap", + "dupont", ] CHOICES_MENUS = [ @@ -130,7 +131,8 @@ Ticker: [/param] {self.ticker} [cmds] balance balance sheet of the company cash cash flow of the company earnings earnings dates and reported EPS - fraud key fraud ratios[/cmds] + fraud key fraud ratios + dupont detailed breakdown for return on equity[/cmds] [info]Other Sources:[/info][menu] > fmp profile,quote,enterprise,dcf,income,ratios,growth from FMP[/menu] """ @@ -741,6 +743,21 @@ Ticker: [/param] {self.ticker} [cmds] if ns_parser: av_view.display_fraud(self.ticker, ns_parser.exp) + @log_start_end(log=logger) + def call_dupont(self, other_args: List[str]): + """Process dupont command.""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.RawTextHelpFormatter, + prog="dupont", + description="The extended dupont deconstructs return on equity to allow investors to understand it better", + ) + ns_parser = parse_known_args_and_warn( + parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED + ) + if ns_parser: + av_view.display_dupont(self.ticker) + @log_start_end(log=logger) def call_dcf(self, other_args: List[str]): """Process dcf command.""" diff --git a/gamestonk_terminal/stocks/stocks_helper.py b/gamestonk_terminal/stocks/stocks_helper.py index 51f67913ef3..b7c0f9026e1 100644 --- a/gamestonk_terminal/stocks/stocks_helper.py +++ b/gamestonk_terminal/stocks/stocks_helper.py @@ -878,3 +878,24 @@ def additional_info_about_ticker(ticker: str) -> str: extra_info += "\n[param]Currency: [/param]" return extra_info + "\n" + + +def clean_fraction(num, denom): + """Returns the decimal value or NA if the operation cannot be performed + + Parameters + ---------- + num : Any + The numerator for the fraction + denom : Any + The denominator for the fraction + + Returns + ---------- + val : Any + The value of the fraction + """ + try: + return num / denom + except TypeError: + return "N/A" diff --git a/scripts/test_stocks_fa.gst b/scripts/test_stocks_fa.gst index c1062d835ad..ab9389d0a2e 100644 --- a/scripts/test_stocks_fa.gst +++ b/scripts/test_stocks_fa.gst @@ -72,4 +72,5 @@ cash dcf splits mktcap -exit \ No newline at end of file +dupont +exit diff --git a/tests/gamestonk_terminal/stocks/fundamental_analysis/cassettes/test_av_model/test_get_dupont.yaml b/tests/gamestonk_terminal/stocks/fundamental_analysis/cassettes/test_av_model/test_get_dupont.yaml new file mode 100644 index 00000000000..2d48a922c7e --- /dev/null +++ b/tests/gamestonk_terminal/stocks/fundamental_analysis/cassettes/test_av_model/test_get_dupont.yaml @@ -0,0 +1,218 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + method: GET + uri: https://www.alphavantage.co/query?apikey=MOCK_API_KEY&function=BALANCE_SHEET&symbol=TSLA + response: + body: + string: !!binary | + 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 + urYAAA== + headers: + Allow: + - GET, HEAD, OPTIONS + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 6ea918b5edc28140-ORD + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Sat, 12 Mar 2022 02:28:27 GMT + Expect-CT: + - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" + Server: + - cloudflare + Transfer-Encoding: + - chunked + Vary: + - Cookie + Via: + - 1.1 vegur + X-Frame-Options: + - SAMEORIGIN + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + method: GET + uri: https://www.alphavantage.co/query?apikey=MOCK_API_KEY&function=INCOME_STATEMENT&symbol=TSLA + response: + body: + string: !!binary | + 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 + headers: + Allow: + - GET, HEAD, OPTIONS + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 6ea918b7adf22a15-ORD + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Sat, 12 Mar 2022 02:28:27 GMT + Expect-CT: + - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" + Server: + - cloudflare + Transfer-Encoding: + - chunked + Vary: + - Cookie + Via: + - 1.1 vegur + X-Frame-Options: + - SAMEORIGIN + status: + code: 200 + message: OK +version: 1 diff --git a/tests/gamestonk_terminal/stocks/fundamental_analysis/csv/test_av_model/test_get_dupont.csv b/tests/gamestonk_terminal/stocks/fundamental_analysis/csv/test_av_model/test_get_dupont.csv new file mode 100644 index 00000000000..ff5c9b95171 --- /dev/null +++ b/tests/gamestonk_terminal/stocks/fundamental_analysis/csv/test_av_model/test_get_dupont.csv @@ -0,0 +1,7 @@ +,2017-12-31,2018-12-31,2019-12-31,2020-12-31,2021-12-31 +Tax Burden,1.0165803108808291,1.0631808278867103,1.1462765957446808,0.7117472852912142,0.8875844322933419 +Interest Burden,1.3228238519533928,3.6,11.223880597014926,0.5752413401476434,0.943694035513735 +EBIT Margin,-0.12407517646058339,-0.011882018545268162,-0.002726015135487021,0.05584094368340944,0.12241978336398937 +Asset Turnover,0.41035935600487056,0.7216207128446537,0.7163717974875398,0.6047403543760067,0.8662825320693374 +Finance Leverage,6.762741424728633,6.041031891123299,5.184194620731339,2.3463667041619796,2.0580675080327273 +ROI,-0.46303704154730835,-0.19825309770465163,-0.13025083106678756,0.03244094488188976,0.1828149325913412 diff --git a/tests/gamestonk_terminal/stocks/fundamental_analysis/test_av_model.py b/tests/gamestonk_terminal/stocks/fundamental_analysis/test_av_model.py index 5a78f1bcfb7..9702bdcbd7d 100644 --- a/tests/gamestonk_terminal/stocks/fundamental_analysis/test_av_model.py +++ b/tests/gamestonk_terminal/stocks/fundamental_analysis/test_av_model.py @@ -65,6 +65,13 @@ def test_check_output(func, kwargs_dict, recorder): @pytest.mark.vcr def test_get_fraud_ratios(recorder): - result_tuple = av_model.get_fraud_ratios(ticker="TSLA") + result_df = av_model.get_fraud_ratios(ticker="TSLA") - recorder.capture(result_tuple) + recorder.capture(result_df) + + +@pytest.mark.vcr +def test_get_dupont(recorder): + result_df = av_model.get_dupont(ticker="TSLA") + + recorder.capture(result_df) diff --git a/tests/gamestonk_terminal/stocks/fundamental_analysis/test_av_view.py b/tests/gamestonk_terminal/stocks/fundamental_analysis/test_av_view.py index 82492a3ed9b..41e3613f1e7 100644 --- a/tests/gamestonk_terminal/stocks/fundamental_analysis/test_av_view.py +++ b/tests/gamestonk_terminal/stocks/fundamental_analysis/test_av_view.py @@ -120,3 +120,19 @@ def test_display_fraud(mocker, df): return_value=(df), ) av_view.display_fraud(ticker="TSLA") + + +@pytest.mark.vcr(record_mode="none") +@pytest.mark.record_stdout +@pytest.mark.parametrize( + "df", + [ + (pd.DataFrame()), + ], +) +def test_dupont(mocker, df): + mocker.patch( + "gamestonk_terminal.stocks.fundamental_analysis.av_view.av_model.get_dupont", + return_value=(df), + ) + av_view.display_dupont(ticker="TSLA") diff --git a/tests/gamestonk_terminal/stocks/fundamental_analysis/test_fa_controller.py b/tests/gamestonk_terminal/stocks/fundamental_analysis/test_fa_controller.py index 83398713477..e7c9752020e 100644 --- a/tests/gamestonk_terminal/stocks/fundamental_analysis/test_fa_controller.py +++ b/tests/gamestonk_terminal/stocks/fundamental_analysis/test_fa_controller.py @@ -264,6 +264,12 @@ def test_call_func_expect_queue(expected_queue, queue, func): [], {"TSLA"}, ), + ( + "call_dupont", + "av_view.display_dupont", + [], + {"TSLA"}, + ), ( "call_cal", "yahoo_finance_view.display_calendar_earnings", diff --git a/tests/gamestonk_terminal/stocks/fundamental_analysis/txt/test_av_view/test_dupont[df0].txt b/tests/gamestonk_terminal/stocks/fundamental_analysis/txt/test_av_view/test_dupont[df0].txt new file mode 100644 index 00000000000..6166bbb294b --- /dev/null +++ b/tests/gamestonk_terminal/stocks/fundamental_analysis/txt/test_av_view/test_dupont[df0].txt @@ -0,0 +1,2 @@ +[red]Invalid response from AlphaVantage[/red] + diff --git a/tests/gamestonk_terminal/stocks/fundamental_analysis/txt/test_fa_controller/test_print_help.txt b/tests/gamestonk_terminal/stocks/fundamental_analysis/txt/test_fa_controller/test_print_help.txt index 8bef97f5804..3ae4a8c0fe2 100644 --- a/tests/gamestonk_terminal/stocks/fundamental_analysis/txt/test_fa_controller/test_print_help.txt +++ b/tests/gamestonk_terminal/stocks/fundamental_analysis/txt/test_fa_controller/test_print_help.txt @@ -25,6 +25,7 @@ Ticker: TSLA cash cash flow of the company earnings earnings dates and reported EPS fraud key fraud ratios + dupont detailed breakdown for return on equity Other Sources: > fmp profile,quote,enterprise,dcf,income,ratios,growth from FMP diff --git a/website/content/terminal/stocks/fundamental_analysis/dupont/_index.md b/website/content/terminal/stocks/fundamental_analysis/dupont/_index.md new file mode 100644 index 00000000000..dc72cb1b273 --- /dev/null +++ b/website/content/terminal/stocks/fundamental_analysis/dupont/_index.md @@ -0,0 +1,10 @@ +``` +usage: dupont [-h] [--export {csv,json,xlsx}] +``` +The dupont ratio is a breakdown of the various components of return on equity. This breakdown gives investors a more complete picutre of how a company generates profit from its assets. + +``` +optional arguments: + -h, --help show this help message + --export {csv,json,xlsx} + Export raw data into csv, json, xlsx diff --git a/website/content/terminal/stocks/fundamental_analysis/fraud/_index.md b/website/content/terminal/stocks/fundamental_analysis/fraud/_index.md index a2846057252..95266d716dc 100755 --- a/website/content/terminal/stocks/fundamental_analysis/fraud/_index.md +++ b/website/content/terminal/stocks/fundamental_analysis/fraud/_index.md @@ -1,5 +1,5 @@ ``` -usage: fraud [-h] [--export {csv,json,xlsx}] +usage: fraud [-h] [-e] [--export {csv,json,xlsx}] ``` ```