File size: 20,644 Bytes
c11f284
543198a
c11f284
543198a
c11f284
543198a
c11f284
 
 
 
 
 
 
543198a
 
 
 
 
c11f284
 
 
 
543198a
 
 
c11f284
 
 
 
 
 
 
 
 
 
 
 
bfe18ee
c11f284
 
 
 
 
 
 
 
 
 
 
 
 
 
bfe18ee
543198a
c11f284
 
171b4c4
 
 
 
c11f284
171b4c4
c11f284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171b4c4
c11f284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa4751a
 
c11f284
 
 
171b4c4
c11f284
 
 
 
 
 
171b4c4
c11f284
171b4c4
c11f284
171b4c4
c11f284
171b4c4
c11f284
171b4c4
c11f284
171b4c4
c11f284
171b4c4
c11f284
171b4c4
c11f284
171b4c4
c11f284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
549c099
 
 
c11f284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
549c099
 
 
c11f284
 
 
 
 
549c099
 
 
c11f284
 
 
 
bda9bfa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c11f284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c3a703b
 
171b4c4
c11f284
 
 
 
 
 
 
171b4c4
c11f284
 
 
 
 
 
 
 
c942045
bda9bfa
 
 
c942045
c11f284
 
81c6a1c
a50cfae
bda9bfa
c11f284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57ac80c
c11f284
 
c942045
bc5c282
c942045
07c352f
c942045
 
 
 
c11f284
 
 
 
 
 
 
b5a705a
a6b8551
fa4751a
c11f284
 
b5a705a
3bb0125
c11f284
 
 
 
81c6a1c
 
 
c11f284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
be6a548
 
c11f284
 
 
 
0448b5a
6d49eb0
0448b5a
77bac90
c942045
c11f284
6d49eb0
c11f284
77bac90
c942045
c11f284
6d9a37c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# Importing required modules
import pandas as pd # for manipulating financial statements in dataframes
import numpy as np
import plotly.express as px # for visualizing results in interactive plots

# To extract and parse fundamental data like beta and growth estimates from finviz website's HTML
import requests
from bs4 import BeautifulSoup as bs

# For parsing financial statements data from financialmodelingprep api
from urllib.request import urlopen
import json

def get_jsonparsed_data(url):
    response = urlopen(url)
    data = response.read().decode("utf-8")
    return json.loads(data)

# For Gradio App
import gradio as gr


# To read the environment variable to use in API requests later
import os
apiKey = os.environ['FMP_API_KEY'] # the environment variable is set in HuggingFace Spaces directly


############################################################################################################
###### GET DATA FROM FINANCIAL MODELING PREP
############################################################################################################

# Financialmodelingprep api url
base_url = "https://financialmodelingprep.com/api/v3/"

# get financial statements using financial modelling prep API
def get_financial_statements(ticker):
    # quarterly cash flow statements for calculating latest trailing twelve months (TTM) free cash flow
    columns_drop = ['acceptedDate', 'period', 'symbol', 'reportedCurrency', 'cik', 'fillingDate', 'depreciationAndAmortization', 'link', 'finalLink']
    q_cash_flow_statement = pd.DataFrame(get_jsonparsed_data(base_url+'cash-flow-statement/' + ticker + '?period=quarter' + '&apikey=' + apiKey))
    q_cash_flow_statement = q_cash_flow_statement.set_index('date').drop(columns_drop, axis=1).iloc[:4] # extract for last 4 quarters
    latest_year = int(q_cash_flow_statement.iloc[0]['calendarYear'])

    # annual cash flow statements
    cash_flow_statement = pd.DataFrame(get_jsonparsed_data(base_url+'cash-flow-statement/' + ticker + '?apikey=' + apiKey))
    cash_flow_statement = cash_flow_statement.set_index('date').drop(columns_drop, axis=1)
    
    # combine annual and latest TTM cash flow statements
    ttm_cash_flow_statement = q_cash_flow_statement.sum() # sum up last 4 quarters to get TTM cash flow
    cash_flow_statement = cash_flow_statement[::-1].append(ttm_cash_flow_statement.rename('TTM')).drop(['netIncome'], axis=1)
    final_cash_flow_statement = cash_flow_statement[::-1] # reverse list to show most recent ones first
    
    # quarterly balance sheet statements
    columns_drop = ['acceptedDate', 'calendarYear', 'period', 'symbol', 'reportedCurrency', 'cik', 'fillingDate', 'link', 'finalLink']
    q_balance_statement = pd.DataFrame(get_jsonparsed_data(base_url+'balance-sheet-statement/' + ticker + '?period=quarter' + '&apikey=' + apiKey))
    q_balance_statement = q_balance_statement.set_index('date').drop(columns_drop, axis=1)
    q_balance_statement = q_balance_statement.apply(pd.to_numeric, errors='coerce')

    # shares outstanding
    shs_data = get_jsonparsed_data('https://financialmodelingprep.com/api/v4/shares_float?symbol=' + ticker + '&apikey=' + apiKey)
    shares_outstanding = shs_data[0]['outstandingShares']
        
    return q_cash_flow_statement, cash_flow_statement, final_cash_flow_statement, q_balance_statement, latest_year, shares_outstanding
 
 
# check stability of cash flows
def plot_cash_flow(ticker, cash_flow_statement):       
    # DCF model works best only if the free cash flows are POSITIVE, STABLE and STEADILY INCREASING. 
    # So let's plot the graph and verify if this is the case.
    fig_cash_flow = px.bar(cash_flow_statement , y='freeCashFlow', title=ticker + ' Free Cash Flows')
    fig_cash_flow.update_xaxes(type='category', tickangle=270, title='Date')
    fig_cash_flow.update_yaxes(title='Free Cash Flows')
    #fig_cash_flow.show()
    return fig_cash_flow


# get ttm cash flow, most recent total debt and cash & short term investment data from statements
def get_statements_data(final_cash_flow_statement, q_balance_statement):
    cash_flow = final_cash_flow_statement.iloc[0]['freeCashFlow'] # ttm cash flow
    total_debt = q_balance_statement.iloc[0]['totalDebt'] 
    cash_and_ST_investments = q_balance_statement.iloc[0]['cashAndShortTermInvestments']
    return cash_flow, total_debt, cash_and_ST_investments


############################################################################################################
###### GET DATA FROM FINVIZ WEBSITE 
############################################################################################################

# Price, EPS next Y/5Y, Beta, Number of Shares Outstanding
# Extract (using requests.get) and Parse (using Beautiful Soup) data from Finviz table in the Finviz website (see screenshot above), needed to calculate intrinsic value of stock.

# List of data we want to extract from Finviz Table
# Price is the current stock price
# EPS next Y is the estimated earnings growth for next year
# EPS next 5Y is the estimated earnings growth for next 5 years (if this is not present on finviz, we will use EPS next Y instead)
# Beta captures the volatility of the stock, used for estimating discount rate later
# Shs Outstand is the number of shares present in the market
metric = ['Price', 'EPS next Y', 'EPS next 5Y', 'Beta']

def fundamental_metric(soup, metric):
    # the table which stores the data in Finviz has html table attribute class of 'snapshot-td2'
    return soup.find_all(text = metric)[-1].find_next(class_='snapshot-td2').text

# get above metrics from finviz and store as a dict 
def get_finviz_data(ticker):
    try:
        url = ("http://finviz.com/quote.ashx?t=" + ticker.lower())
        soup = bs(requests.get(url,headers={'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0'}).content)
        dict_finviz = {}        
        for m in metric:   
            dict_finviz[m] = fundamental_metric(soup,m)
        for key, value in dict_finviz.items():
            # replace percentages
            if (value[-1]=='%'):
                dict_finviz[key] = value[:-1]
                dict_finviz[key] = float(dict_finviz[key])
            # billion
            if (value[-1]=='B'):
                dict_finviz[key] = value[:-1]
                dict_finviz[key] = float(dict_finviz[key])*1000000000  
            # million
            if (value[-1]=='M'):
                dict_finviz[key] = value[:-1]
                dict_finviz[key] = float(dict_finviz[key])*1000000
            try:
                dict_finviz[key] = float(dict_finviz[key])
            except:
                pass 
    except Exception as e:
        print (e)
        print ('Not successful parsing ' + ticker + ' data.')        
    return dict_finviz


def parse_finviz_dict(finviz_dict):
    EPS_growth_5Y = finviz_dict['EPS next 5Y']
    # sometimes EPS next 5Y is empty and shows as a '-' string, in this case use EPS next Y
    if isinstance(EPS_growth_5Y, str):
        if not EPS_growth_5Y.isdigit():
            EPS_growth_5Y = finviz_dict['EPS next Y']
    EPS_growth_6Y_to_10Y = EPS_growth_5Y/2  # Half the previous growth rate, conservative estimate
    # Long term = previous growth rate or around long term inflation rate, whichever is lower to be conservative estimate
    long_term_growth_rate = np.minimum(EPS_growth_6Y_to_10Y, 3)
    beta = finviz_dict['Beta']
    current_price = finviz_dict['Price']
    
    return EPS_growth_5Y, EPS_growth_6Y_to_10Y, long_term_growth_rate, beta, current_price


## Estimate Discount Rate from Beta
def estimate_discount_rate(beta):
    # Beta shows the volatility of the stock, 
    # the higher the beta, we want to be more conservative by increasing the discount rate also.
    discount_rate = 9
    if(beta<0.80):
        discount_rate = 7
    elif(beta>=0.80 and beta<1):
        discount_rate = 8
    elif(beta>=1 and beta<1.1):
        discount_rate = 8.5
    elif(beta>=1.1 and beta<1.2):
        discount_rate = 9
    elif(beta>=1.2 and beta<1.3):
        discount_rate = 9.5
    elif(beta>=1.3 and beta<1.4):
        discount_rate = 10
    elif(beta>=1.4 and beta<1.6):
        discount_rate = 10.5
    elif(beta>=1.61):
        discount_rate = 11   
    return discount_rate


############################################################################################################
## Calculate Intrinsic Value
############################################################################################################

# 1. First Project Cash Flows from Year 1 to Year 10 using Present (TTM) Free Cash Flow
# 2. Discount the Cash Flows to Present Value
# 3. Calculate the Terminal Value after Year 10 (Discounted to Present Value) Assuming the Company will Grow at a Constant Steady Rate Forever (https://corporatefinanceinstitute.com/resources/financial-modeling/dcf-terminal-value-formula/)
# 4. Add the Cash Flows and the Terminal Value Up
# 5. Then Account for the Cash + Short Term Investments and Subtract Total Debt
# 6. Divide by Total Number of Shares Outstanding

def calculate_intrinsic_value(latest_year, cash_flow, total_debt, cash_and_ST_investments, 
                                  EPS_growth_5Y, EPS_growth_6Y_to_10Y, long_term_growth_rate,
                                  shares_outstanding, discount_rate, current_price):   
    
    # Convert all percentages to decmials
    EPS_growth_5Y_d = EPS_growth_5Y/100
    EPS_growth_6Y_to_10Y_d = EPS_growth_6Y_to_10Y/100
    long_term_growth_rate_d = long_term_growth_rate/100
    discount_rate_d = discount_rate/100
    # print("Discounted Cash Flows\n")
    
    # Lists of projected cash flows from year 1 to year 20
    cash_flow_list = []
    cash_flow_discounted_list = []
    year_list = []
    
    # 1. First Project Cash Flows from Year 1 to Year 10 using Present (TTM) Free Cash Flow
    # 2. Discount Each of the Cash Flows to Present Value
    # Years 1 to 5
    for year in range(1, 6):
        year_list.append(year + latest_year)
        cash_flow*=(1 + EPS_growth_5Y_d)        
        cash_flow_list.append(cash_flow)
        cash_flow_discounted = cash_flow/((1 + discount_rate_d)**year)
        cash_flow_discounted_list.append(cash_flow_discounted)
        # print("Year " + str(year + latest_year) + ": $" + str(cash_flow_discounted)) ## Print out the projected discounted cash flows
    
    # Years 6 to 10
    for year in range(6, 11):
        year_list.append(year + latest_year)
        cash_flow*=(1 + EPS_growth_6Y_to_10Y_d)
        cash_flow_list.append(cash_flow)
        cash_flow_discounted = cash_flow/((1 + discount_rate_d)**year)
        cash_flow_discounted_list.append(cash_flow_discounted)
        # print("Year " + str(year + latest_year) + ": $" + str(cash_flow_discounted)) ## Print out the projected discounted cash flows
    
    # Store all forecasted cash flows in dataframe
    forecast_cash_flows_df = pd.DataFrame.from_dict({'Year': year_list, 'Cash Flow': cash_flow_list, 'Discounted Cash Flow': cash_flow_discounted_list})
    forecast_cash_flows_df = forecast_cash_flows_df.set_index('Year')
    
    # 3. Calculate the Terminal Value after Year 10 (Discounted to Present Value)
    # These are All Future Cash Flows Summed Up
    # Assuming the Company will Grow at a Constant Steady Rate Forever (https://corporatefinanceinstitute.com/resources/financial-modeling/dcf-terminal-value-formula/)
    # Growth in Perpuity Approach    
    cashflow_10Y = cash_flow_discounted_list[-1]
    # Formula to Calculate: https://corporatefinanceinstitute.com/resources/financial-modeling/dcf-terminal-value-formula/
    terminal_value = cashflow_10Y*(1+long_term_growth_rate_d)/(discount_rate_d-long_term_growth_rate_d)
    
    # 4. Add the Cash Flows and the Terminal Value Up
    # 5. Then Account for the Cash + Short Term Investments and Subtract Total Debt
    # 6. Divide by Total Number of Shares Outstanding    
    intrinsic_value = (sum(cash_flow_discounted_list) + terminal_value - total_debt + cash_and_ST_investments)/shares_outstanding
    margin_of_safety = (1-current_price/intrinsic_value)*100
    
    return forecast_cash_flows_df, terminal_value, intrinsic_value, margin_of_safety
    
    
# Discount rate and long term growth rate can change intrinsic value significantly
# So we estimate the intrinsic values for the different discount rates and long term growth rates below and store them in a DataFrame
def calculate_multiple_intrinsic_values(latest_year, cash_flow, total_debt, cash_and_ST_investments, 
                                  EPS_growth_5Y, EPS_growth_6Y_to_10Y, long_term_growth_rate,
                                  shares_outstanding, discount_rate, current_price):   
       
    discount_rates = [discount_rate-2, discount_rate-1.5, 
                  discount_rate-1, discount_rate-0.5, 
                  discount_rate, 
                  discount_rate+0.5, discount_rate+1,
                  discount_rate+1.5, discount_rate+2]
                  
    long_term_growth_rates = [long_term_growth_rate-1, long_term_growth_rate-0.5, 
                          long_term_growth_rate, 
                          long_term_growth_rate+0.5, long_term_growth_rate+1]
                         
                          
    intrinsic_values = {}
    for dr in discount_rates:
        intrinsic_values[dr] = {}
        for lr in long_term_growth_rates:
            _, _, intrinsic_value, _ = calculate_intrinsic_value(latest_year, cash_flow, total_debt, cash_and_ST_investments, 
                                      EPS_growth_5Y, EPS_growth_6Y_to_10Y, lr,
                                      shares_outstanding, dr, current_price)
            intrinsic_values[dr][lr] = intrinsic_value
    
    df_intrinsic_values = pd.DataFrame(intrinsic_values).T
    df_intrinsic_values.index.name = 'Discount Rates'
    df_intrinsic_values = df_intrinsic_values.reset_index()
    
    return df_intrinsic_values


# Plot forecasted cash flows from years 1 to 10, as well as the discounted cash flows
def plot_forecasted_cash_flows(ticker, forecast_cash_flows_df):

    fig_cash_forecast = px.bar(forecast_cash_flows_df, barmode='group', title=ticker + ' Projected Free Cash Flows')
    fig_cash_forecast.update_xaxes(type='category', tickangle=270)
    fig_cash_forecast.update_xaxes(tickangle=270, title='Forecasted Year')
    fig_cash_forecast.update_yaxes(title='Free Cash Flows')
    # fig_cash_forecast.show()
    
    return fig_cash_forecast


# chain all the steps from the functions above together
def run_all_steps(ticker):
    ticker = ticker.upper() # make sure ticker is caps
    
    q_cash_flow_statement, cash_flow_statement, final_cash_flow_statement, q_balance_statement, latest_year, shares_outstanding = get_financial_statements(ticker)

    fig_cash_flow = plot_cash_flow(ticker, cash_flow_statement)

    cash_flow, total_debt, cash_and_ST_investments = get_statements_data(final_cash_flow_statement, q_balance_statement)

    finviz_dict = get_finviz_data(ticker)

    EPS_growth_5Y, EPS_growth_6Y_to_10Y, long_term_growth_rate, beta, current_price = parse_finviz_dict(finviz_dict)

    discount_rate = estimate_discount_rate(beta)

    forecast_cash_flows_df, terminal_value, intrinsic_value, margin_of_safety = calculate_intrinsic_value(latest_year, cash_flow, total_debt, cash_and_ST_investments, 
                                      EPS_growth_5Y, EPS_growth_6Y_to_10Y, long_term_growth_rate,
                                      shares_outstanding, discount_rate, current_price)

    fig_cash_forecast = plot_forecasted_cash_flows(ticker, forecast_cash_flows_df)

    df_intrinsic_values = calculate_multiple_intrinsic_values(latest_year, cash_flow, total_debt, cash_and_ST_investments, 
                                  EPS_growth_5Y, EPS_growth_6Y_to_10Y, long_term_growth_rate,
                                  shares_outstanding, discount_rate, current_price)
    
    
    return q_cash_flow_statement.reset_index(), final_cash_flow_statement.reset_index(), q_balance_statement.reset_index(), fig_cash_flow, \
        str(EPS_growth_5Y) + '%', str(EPS_growth_6Y_to_10Y) + '%', str(long_term_growth_rate) + '%', \
        beta, current_price, \
        str(discount_rate) + '%', forecast_cash_flows_df.reset_index(), terminal_value, intrinsic_value, fig_cash_forecast, str(margin_of_safety) + '%', df_intrinsic_values
      

# Gradio App and UI
with gr.Blocks() as app:
    with gr.Row():
        gr.HTML("<h1>Bohmian's Stock Intrinsic Value Calculator</h1>")

    with gr.Row():        
        ticker = gr.Textbox("AAPL", label='Enter stock ticker to calculate its intrinsic value e.g. "AAPL"')
        btn = gr.Button("Calculate Intrinsic Value")
        
    # Show intrinsic value calculation results
    with gr.Row():
        gr.HTML("<h2>Calculated Intrinsic Value</h2>")
        
    with gr.Row():
        intrinsic_value = gr.Text(label="Intrinsic Value (if this value is negative, it means current cash flow may be negative and this model WOULD NOT WORK, scroll down to check)")
        current_price = gr.Text(label="Actual Stock Price")
        margin_of_safety = gr.Text(label="Margin of Safety")

    # Discount rate and long term growth rate can change intrinsic value significantly, so we show all of the estimates here in a DataFrame
    with gr.Row():    
        gr.HTML("<h2>Intrinsic Values with Different Discount Rates (Each Row) Long Term Growth Rates (Each Column)</h2>")
    with gr.Row():  
        df_intrinsic_values = gr.DataFrame(label="Intrinsic Values with Different Discount Rates/Long Term Growth Rates")

        
    # Show metrics obtained and estimated from FinViz website that were essential for calculations
    with gr.Row():
        gr.HTML("<h2>Metrics Obtained (and Estimated) from FinViz Website</h2>")
    with gr.Row():
        gr.HTML("<h3>https://finviz.com/</h3>")
        
    with gr.Row():
            EPS_growth_5Y = gr.Text(label="EPS Next 5Y (estimated EPS growth for next 5 years)")
            EPS_growth_6Y_to_10Y = gr.Text(label="EPS growth for 6th to 10th year (estimated as half of 5Y rate)")
            long_term_growth_rate = gr.Text(label="Long Term Growth Rate (estimated as the above or 3%, whichever is lower)")  
    
    with gr.Row():
        beta = gr.Text(label="Beta (measures volatility of stock)")
        discount_rate = gr.Text(label="Discount Rate (estimated from beta)")            

       
    # Show detailed actual historical financial statements
    with gr.Row():
        gr.HTML("<h2>Actual Historical Financial Statements Data from Financial Modelling Prep API</h2>")
    with gr.Row():
        gr.HTML("<h3>https://site.financialmodelingprep.com/developer</h3>")
    with gr.Row():
        gr.HTML("<h3>IMPORTANT NOTE: DCF model works best only if the free cash flows are POSITIVE, STABLE and STEADILY INCREASING. Check if this is the case.</h3>")
    
    with gr.Row():        
        fig_cash_flow = gr.Plot(label="Historical Cash Flows")
        
    with gr.Row():   
        q_cash_flow_statement = gr.DataFrame(label="Last 4 Quarterly Cash Flow Statements")
        
    with gr.Row():   
        final_cash_flow_statement = gr.DataFrame(label="TTM + Annual Cash Flow Statements")
    
    with gr.Row():   
        q_balance_statement = gr.DataFrame(label="Quarterly Balance Statements")
    
    
    # Show forecasted cash flows and terminal value
    with gr.Row():
        gr.HTML("<h2>Forecasted Cash Flows for Next 10 Years</h2>")
        
    with gr.Row():
        fig_cash_forecast = gr.Plot(label="Forecasted Cash Flows")
    
    with gr.Row():    
        forecast_cash_flows_df = gr.DataFrame(label="Forecasted Cash Flows")

    with gr.Row():
        terminal_value = gr.Text(label="Terminal Value (after 10th year)")

    btn.click(fn=run_all_steps, inputs=[ticker], 
              outputs=[q_cash_flow_statement, final_cash_flow_statement, q_balance_statement, fig_cash_flow, \
        EPS_growth_5Y, EPS_growth_6Y_to_10Y, long_term_growth_rate, beta, current_price, \
        discount_rate, forecast_cash_flows_df, terminal_value, intrinsic_value, fig_cash_forecast, margin_of_safety, df_intrinsic_values])

    ticker.submit(fn=run_all_steps, inputs=[ticker], 
              outputs=[q_cash_flow_statement, final_cash_flow_statement, q_balance_statement, fig_cash_flow, \
        EPS_growth_5Y, EPS_growth_6Y_to_10Y, long_term_growth_rate, beta, current_price, \
        discount_rate, forecast_cash_flows_df, terminal_value, intrinsic_value, fig_cash_forecast, margin_of_safety, df_intrinsic_values])

app.launch()