def calculate_earnings_score(organized_data): increase_percentage = Utils.safe_cast(organized_data['earnings_increase_percentage'], float, 0) strict_increase_percentage = Utils.safe_cast(organized_data['earnings_strict_increase_percentage'], float, 0) positive_percentage = Utils.safe_cast(organized_data['earnings_positive_percentage'], float, 0) num_years = Utils.safe_cast(organized_data['num_years'], int, 0) max_years = max(num_years, 10) values = [ increase_percentage, strict_increase_percentage, positive_percentage, num_years / float(max_years) ] return Utils.average(values) * 100
def analyze_ticker(ticker, time_to_live=30 * 24 * 60 * 60): try: analyzed_data = FileStorageDAO.get_analyzed_data(ticker) if time.time() - float(analyzed_data.get('last_updated_date', 0)) < time_to_live: return except FileNotFoundError: # do nothing if no file exists pass organized_data = FileStorageDAO.get_organized_data(ticker) earnings_score = DataAnalyzer.calculate_earnings_score(organized_data) revenue_score = DataAnalyzer.calculate_revenue_score(organized_data) earnings_growth_score = DataAnalyzer.calculate_earnings_growth_score(organized_data) revenue_growth_score = DataAnalyzer.calculate_revenue_growth_score(organized_data) overall_score = Utils.average([earnings_score, revenue_score, earnings_growth_score, revenue_growth_score]) analyzed_data = { 'price_target': DataAnalyzer.calculate_price_target(organized_data), 'earnings_score': earnings_score, 'earnings_growth_score': earnings_growth_score, 'revenue_score': revenue_score, 'revenue_growth_score': revenue_growth_score, 'overall_score': overall_score, 'organized_data': organized_data, 'last_updated_date': time.time(), } FileStorageDAO.save_analyzed_data(ticker, analyzed_data)
def organize_ticker(ticker, time_to_live=30 * 24 * 60 * 60): try: info = FileStorageDAO.get_organized_data(ticker) if time.time() - float(info.get('last_updated_date', 0)) < time_to_live: return except FileNotFoundError: # do nothing pass income_statement = FileStorageDAO.get_income_statement(ticker) # balance_sheet = FileStorageDAO.get_balance_sheet(ticker) # cash_flow_statement = FileStorageDAO.get_cash_flow_statement(ticker) earnings = IncomeStatementUtilities.get_earnings(income_statement) revenue = IncomeStatementUtilities.get_revenue(income_statement) num_years = IncomeStatementUtilities.get_num_years(income_statement) organized_data = { # earnings attributes 'average_earnings': Utils.average(earnings), 'earnings': earnings, 'earnings_positive_percentage': Utils.calculate_percent_positive(earnings), 'earnings_increase_percentage': Utils.calculate_increase_percentage(earnings), 'earnings_strict_increase_percentage': Utils.calculate_strict_increase_percentage(earnings), 'earnings_growth': DataOrganizer.get_growth(earnings, num_years - 1), # revenue attributes 'average_revenue': Utils.average(revenue), 'revenue': revenue, 'revenue_positive_percentage': Utils.calculate_percent_positive(revenue), 'revenue_increase_percentage': Utils.calculate_increase_percentage(revenue), 'revenue_strict_increase_percentage': Utils.calculate_strict_increase_percentage(revenue), 'revenue_growth': DataOrganizer.get_growth(revenue, num_years - 1), 'num_years': num_years, 'last_updated_date': time.time(), } FileStorageDAO.save_organized_data(ticker, organized_data)
def get_growth(statements, num_years, attr): if len(statements) < num_years or len(statements) == 0: return 'no data' try: starting_value = float(statements[num_years].get(attr)) ending_value = float(statements[0].get(attr)) return Utils.calculate_yoy_return(starting_value, ending_value, num_years) except: return 'error'
from scripts.utilities.utils import Utils from scripts.report_generator import ReportGenerator print(Utils.calculate_yoy_return(375, 1800)) print(Utils.calculate_yoy_return(1800, 375)) print(Utils.calculate_yoy_return(375, -1800)) print(Utils.calculate_yoy_return(-375, -1800)) print(Utils.calculate_yoy_return(-375, 1800)) ReportGenerator.generate_report('AMZN')
def get_growth(values, num_years): if len(values) <= 1: return 0 return Utils.calculate_yoy_return(values[0], values[-1], num_years)
class FinancialModelingPrepClient: API_KEY = Utils.get_api_key() INCOME_STATEMENT = 'income_statement' BALANCE_SHEET = 'balance_sheet' CASH_FLOW_STATEMENT = 'cash_flow_statement' @staticmethod def get_financial_ratios(ticker): url = 'https://financialmodelingprep.com/api/v3/key-metrics/' + ticker + '?apikey=' + FinancialModelingPrepClient.API_KEY return FinancialModelingPrepClient.json_get(url) @staticmethod def get_financial_ratios_ttm(ticker): url = 'https://financialmodelingprep.com/api/v3/ratios-ttm/' + ticker + '?apikey=' + FinancialModelingPrepClient.API_KEY return FinancialModelingPrepClient.json_get(url) @staticmethod def get_company_key_metrics_ttm(ticker): url = 'https://financialmodelingprep.com/api/v3/key-metrics-ttm/' + ticker + '?apikey=' + FinancialModelingPrepClient.API_KEY return FinancialModelingPrepClient.json_get(url) @staticmethod def get_company_quote_batch(tickers): tickers_str = '' for ticker in tickers: tickers_str += str(ticker) + ',' url = 'https://financialmodelingprep.com/api/v3/quote/' + tickers_str + '?apikey=' + FinancialModelingPrepClient.API_KEY return FinancialModelingPrepClient.json_get(url) @staticmethod def get_financial_ratios_batch(tickers): """ Financial Modeling Prep doesn't have a batch API, so just loop through and use the single API :param tickers: stock symbols to fetch for :return: a list of json that contains financial ratios """ ret = [] for ticker in tickers: ret.append( FinancialModelingPrepClient.get_financial_ratios(ticker)) return ret @staticmethod def get_financial_ratios_ttm_batch(tickers): """ Financial Modeling Prep doesn't have a batch API, so just loop through and use the single API :param tickers: stock symbols to fetch for :return: a list of json that contains financial ratios """ ret = [] for ticker in tickers: try: ret.append( FinancialModelingPrepClient.get_financial_ratios_ttm( ticker)) except: ret.append({}) return ret @staticmethod def get_company_key_metrics_ttm_batch(tickers): """ Financial Modeling Prep doesn't have a batch API, so just loop through and use the single API :param tickers: stock symbols to fetch for :return: a list of json that contains financial ratios """ ret = [] for ticker in tickers: try: ret.append( FinancialModelingPrepClient.get_company_key_metrics_ttm( ticker)) except: ret.append({}) return ret @staticmethod def get_income_statement(ticker): return FinancialModelingPrepClient.get_income_statements_batch( [ticker]) @staticmethod def get_income_statements_batch(tickers): return FinancialModelingPrepClient.get_batch( tickers, FinancialModelingPrepClient.INCOME_STATEMENT) @staticmethod def get_balance_sheet(ticker): return FinancialModelingPrepClient.get_balance_sheets_batch([ticker]) @staticmethod def get_balance_sheets_batch(tickers): return FinancialModelingPrepClient.get_batch( tickers, FinancialModelingPrepClient.BALANCE_SHEET) @staticmethod def get_batch(tickers, statement_type): # TODO: See if the batch API is fixed.. ret = [] for ticker in tickers: # time.sleep(1) ret.append( FinancialModelingPrepClient. json_get_single_financial_statement(ticker, statement_type)) return ret @staticmethod def get_cash_flow_sheet(ticker): return FinancialModelingPrepClient.get_cash_flow_sheets_batch([ticker]) @staticmethod def get_cash_flow_statements_batch(tickers): return FinancialModelingPrepClient.get_batch( tickers, FinancialModelingPrepClient.CASH_FLOW_STATEMENT) @staticmethod def get_tickers(): url = 'https://financialmodelingprep.com/api/v3/company/stock/list?apikey=' + FinancialModelingPrepClient.API_KEY return FinancialModelingPrepClient.json_get(url) @staticmethod def json_get(url, retries=3): # print('url: ' + url) num_tries = 0 while num_tries < retries: try: response = urlopen(url) data = response.read().decode("utf-8") return json.loads(data) except Exception as err: num_tries += 1 print("Error calling FinancialModelingPrep: {0}".format(err)) print("Unexpected error:", sys.exc_info()[0]) print("url: " + url) print('Retrying ' + str(num_tries)) raise Exception('Error retrieving data from FinancialModelingPrep') @staticmethod def json_get_single_financial_statement(ticker, statement_type): if statement_type == 'income_statement': url = 'https://financialmodelingprep.com/api/v3/income-statement/' elif statement_type == FinancialModelingPrepClient.BALANCE_SHEET: url = 'https://financialmodelingprep.com/api/v3/balance-sheet-statement/' elif statement_type == 'cash_flow_statement': url = 'https://financialmodelingprep.com/api/v3/cash-flow-statement/' url = url + ticker + '?apikey=' + FinancialModelingPrepClient.API_KEY data = FinancialModelingPrepClient.json_get(url) return data @staticmethod def json_get_financial_statement(tickers, url): if len(tickers) <= 0: return arguments = tickers[len(tickers) - 1] for i in range(len(tickers) - 2, -1, -1): arguments += ',' + tickers[i] url = url + arguments + '?apikey=' + FinancialModelingPrepClient.API_KEY print('url ' + url) data = FinancialModelingPrepClient.json_get(url) try: data = data['financialStatementList'] except KeyError: data = [data] return data
def generate_report(ticker, time_to_live=0): try: info = FileStorageDAO.get_company_report(ticker) if time.time() - float(info.get('last_updated_date', 0)) < time_to_live: return except (FileNotFoundError, json.decoder.JSONDecodeError): # continue on pass except BaseException as e: print("Unexpected exception when trying to generate report: " + str(e)) return income_statements = FinancialStatementConverter.convert_income_statement_data( ticker) balance_sheets = FinancialStatementConverter.convert_balance_statement_data( ticker) cash_flow_statements = FinancialStatementConverter.convert_cash_flow_statement_data( ticker) key_ratios = FinancialStatementConverter.convert_key_ratio_data(ticker) company_key_metrics_ttm = FinancialStatementConverter.convert_company_key_metrics_ttm_data( ticker) company_quote = FinancialStatementConverter.convert_company_quote_data( ticker) # Part 1 - Growth rates equity_growth = ReportGenerator.get_growth_list( balance_sheets, BalanceSheet.SHAREHOLDERS_EQUITY) revenue_growth = ReportGenerator.get_growth_list( income_statements, IncomeStatement.REVENUE) earnings_growth = ReportGenerator.get_growth_list( income_statements, IncomeStatement.NET_INCOME) operating_cash_growth = ReportGenerator.get_growth_list( cash_flow_statements, CashFlowStatement.OPERATING_CASH_FLOW) try: relevant_growth_rates = [ equity_growth[2], equity_growth[9], revenue_growth[2], revenue_growth[9], earnings_growth[2], earnings_growth[9], operating_cash_growth[2], operating_cash_growth[9] ] # filter out any None values lowest_growth_rate = min(rate for rate in relevant_growth_rates if rate is not None) conservative_growth_rate = min(.15, lowest_growth_rate) except: lowest_growth_rate = 0 conservative_growth_rate = 0 # Part 2 - P/E and EPS pe_ratios = ReportGenerator.get_list(key_ratios, KeyRatios.PE_RATIO) positive_pe_ratios = Utils.remove_negative_numbers(pe_ratios) estimated_future_pe = max( min(lowest_growth_rate * 2 * 100, Utils.average(positive_pe_ratios)), 0) conservative_future_pe = 15 if len(income_statements) > 0: eps = income_statements[0].get(IncomeStatement.EPS) eps_diluted = income_statements[0].get(IncomeStatement.EPS_DILUTED) else: eps = 0 eps_diluted = 0 if company_key_metrics_ttm is None: eps_ttm = 0 else: eps_ttm = company_key_metrics_ttm.get(CompanyKeyMetricsTTM.EPS) if company_quote is None: shares_outstanding = 0 else: shares_outstanding = company_quote.get( CompanyQuote.SHARES_OUTSTANDING) # Part 3 - Intrinsic value calculation intrinsic_value = Utils.calculate_intrinsic_value( eps, lowest_growth_rate, estimated_future_pe) conservative_intrinsic_value = Utils.calculate_intrinsic_value( eps_diluted, conservative_growth_rate, conservative_future_pe) intrinsic_value_ttm = Utils.calculate_intrinsic_value( eps_ttm, lowest_growth_rate, estimated_future_pe) conservative_intrinsic_value_ttm = Utils.calculate_intrinsic_value( eps_ttm, conservative_growth_rate, conservative_future_pe) # Part 4 - Create and populate the company report company_report = CompanyReport() company_report.set_attr( CompanyReport.DATES, ReportGenerator.get_list(income_statements, IncomeStatement.DATE)) # Set growth rates company_report.set_attr(CompanyReport.EQUITY_GROWTH, equity_growth) company_report.set_attr(CompanyReport.REVENUE_GROWTH, revenue_growth) company_report.set_attr(CompanyReport.EARNINGS_GROWTH, earnings_growth) company_report.set_attr(CompanyReport.OPERATING_CASH_GROWTH, operating_cash_growth) # Debt, Revenue, Earnings, Equity, ROIC, Return on Equity, Debt to Earnings, Shares Outstanding company_report.set_attr( CompanyReport.RETURN_ON_INVESTED_CAPITAL, ReportGenerator.get_list(key_ratios, KeyRatios.ROIC)) company_report.set_attr( CompanyReport.RETURN_ON_EQUITY, ReportGenerator.get_list(key_ratios, KeyRatios.ROE)) company_report.set_attr( CompanyReport.TOTAL_DEBT, ReportGenerator.get_list(balance_sheets, BalanceSheet.TOTAL_DEBT)) company_report.set_attr( CompanyReport.REVENUE, ReportGenerator.get_list(income_statements, IncomeStatement.REVENUE)) company_report.set_attr( CompanyReport.EARNINGS, ReportGenerator.get_list(income_statements, IncomeStatement.NET_INCOME)) company_report.set_attr(CompanyReport.EPS_TTM, eps_ttm) company_report.set_attr( CompanyReport.EQUITY, ReportGenerator.get_list(balance_sheets, BalanceSheet.SHAREHOLDERS_EQUITY)) # other company_report.set_attr(CompanyReport.TICKER, ticker) company_report.set_attr(CompanyReport.NUM_INCOME_STATEMENTS, len(income_statements)) company_report.set_attr(CompanyReport.NUM_BALANCE_SHEETS, len(balance_sheets)) company_report.set_attr(CompanyReport.NUM_CASH_FLOW_STATEMENTS, len(cash_flow_statements)) company_report.set_attr(CompanyReport.EPS, eps) company_report.set_attr(CompanyReport.EPS_DILUTED, eps_diluted) company_report.set_attr(CompanyReport.PE_RATIOS, pe_ratios) company_report.set_attr(CompanyReport.AVERAGE_PE_RATIO, estimated_future_pe) company_report.set_attr(CompanyReport.SHARES_OUTSTANDING, shares_outstanding) company_report.set_attr( CompanyReport.DEBT_TO_EARNINGS, ReportGenerator.get_list(key_ratios, KeyRatios.DEBT_TO_EARNINGS)) company_report.set_attr(CompanyReport.INTRINSIC_VALUE, intrinsic_value) company_report.set_attr(CompanyReport.INTRINSIC_VALUE_GROWTH_RATE, lowest_growth_rate) company_report.set_attr(CompanyReport.CONSERVATIVE_INTRINSIC_VALUE, conservative_intrinsic_value) company_report.set_attr( CompanyReport.CONSERVATIVE_INTRINSIC_VALUE_GROWTH_RATE, conservative_growth_rate) company_report.set_attr(CompanyReport.INTRINSIC_VALUE_USING_TTM_EPS, intrinsic_value_ttm) company_report.set_attr( CompanyReport.CONSERVATIVE_INTRINSIC_VALUE_USING_TTM_EPS, conservative_intrinsic_value_ttm) FileStorageDAO.save_report(company_report) # Part 5 - create and save the json version of the company report company_report_json = { CompanyReport.DATES: ReportGenerator.get_list(income_statements, IncomeStatement.DATE), CompanyReport.EQUITY_GROWTH: equity_growth, CompanyReport.REVENUE_GROWTH: revenue_growth, CompanyReport.EARNINGS_GROWTH: earnings_growth, CompanyReport.OPERATING_CASH_GROWTH: operating_cash_growth, CompanyReport.RETURN_ON_INVESTED_CAPITAL: ReportGenerator.get_list(key_ratios, KeyRatios.ROIC), CompanyReport.RETURN_ON_EQUITY: ReportGenerator.get_list(key_ratios, KeyRatios.ROE), CompanyReport.TOTAL_DEBT: ReportGenerator.get_list(balance_sheets, BalanceSheet.TOTAL_DEBT), CompanyReport.REVENUE: ReportGenerator.get_list(income_statements, IncomeStatement.REVENUE), CompanyReport.EARNINGS: ReportGenerator.get_list(income_statements, IncomeStatement.NET_INCOME), CompanyReport.EPS_TTM: eps_ttm, CompanyReport.EQUITY: ReportGenerator.get_list(balance_sheets, BalanceSheet.SHAREHOLDERS_EQUITY), CompanyReport.TICKER: ticker, CompanyReport.NUM_INCOME_STATEMENTS: len(income_statements), CompanyReport.NUM_BALANCE_SHEETS: len(balance_sheets), CompanyReport.NUM_CASH_FLOW_STATEMENTS: len(cash_flow_statements), CompanyReport.EPS: eps, CompanyReport.EPS_DILUTED: eps_diluted, CompanyReport.PE_RATIOS: pe_ratios, CompanyReport.AVERAGE_PE_RATIO: estimated_future_pe, CompanyReport.SHARES_OUTSTANDING: shares_outstanding, CompanyReport.DEBT_TO_EARNINGS: ReportGenerator.get_list(key_ratios, KeyRatios.DEBT_TO_EARNINGS), CompanyReport.INTRINSIC_VALUE: intrinsic_value, CompanyReport.INTRINSIC_VALUE_GROWTH_RATE: lowest_growth_rate, CompanyReport.CONSERVATIVE_INTRINSIC_VALUE: conservative_intrinsic_value, CompanyReport.CONSERVATIVE_INTRINSIC_VALUE_GROWTH_RATE: conservative_growth_rate, CompanyReport.INTRINSIC_VALUE_USING_TTM_EPS: intrinsic_value_ttm, CompanyReport.CONSERVATIVE_INTRINSIC_VALUE_USING_TTM_EPS: conservative_intrinsic_value_ttm, 'last_updated_date': time.time(), 'last_updated_date_human': str(datetime.datetime.now()) } FileStorageDAO.save_company_report_json(ticker, company_report_json)