QuantumLearner commited on
Commit
e8235fe
·
verified ·
1 Parent(s): 3b430d3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -56
app.py CHANGED
@@ -10,7 +10,7 @@ warnings.filterwarnings("ignore")
10
  import os
11
 
12
  # Financial Modeling Prep API Key
13
- FMP_API_KEY = os.getenv("FMP_API_KEY")
14
 
15
  # Function to get data from Finviz
16
  def get_finviz_data(ticker):
@@ -34,11 +34,21 @@ def get_finviz_data(ticker):
34
 
35
  # Function to calculate beta
36
  def calculate_beta(ticker, start_date="2018-01-01", end_date=None, market_ticker="^GSPC"):
37
- stock_data = yf.download(ticker, start=start_date, end=end_date)['Adj Close']
38
- market_data = yf.download(market_ticker, start=start_date, end=end_date)['Adj Close']
39
 
40
- stock_returns = stock_data.pct_change().dropna()
41
- market_returns = market_data.pct_change().dropna()
 
 
 
 
 
 
 
 
 
 
42
 
43
  combined_data = pd.concat([stock_returns, market_returns], axis=1).dropna()
44
  combined_data.columns = ['Stock_Returns', 'Market_Returns']
@@ -52,14 +62,26 @@ def calculate_beta(ticker, start_date="2018-01-01", end_date=None, market_ticker
52
 
53
  # Function to get risk-free rate
54
  def get_risk_free_rate(risk_free_rate_ticker="^TYX"):
55
- treasury_data = yf.download(risk_free_rate_ticker, period="1d")['Close']
56
- risk_free_rate = treasury_data.iloc[-1] / 100
 
 
 
 
 
57
  return risk_free_rate
58
 
59
  # Function to calculate market risk premium
60
  def calculate_market_risk_premium(market_ticker="^GSPC", start_date="2018-01-01", end_date=None, risk_free_rate_ticker="^TYX"):
61
- market_data = yf.download(market_ticker, start=start_date, end=end_date)['Adj Close']
62
- market_returns = market_data.pct_change().dropna()
 
 
 
 
 
 
 
63
  average_annual_market_return = market_returns.mean() * 252
64
 
65
  risk_free_rate = get_risk_free_rate(risk_free_rate_ticker)
@@ -220,7 +242,7 @@ def get_dcf_valuation(ticker, api_key):
220
  def get_levered_dcf_valuation(ticker, api_key):
221
  url = f"https://financialmodelingprep.com/api/v4/advanced_levered_discounted_cash_flow?symbol={ticker}&apikey={api_key}"
222
  response = requests.get(url)
223
- if (response.status_code == 200):
224
  data = response.json()
225
  if data:
226
  return data[0]['equityValuePerShare']
@@ -324,7 +346,13 @@ def perform_dcf_analysis(ticker, start_date="2018-01-01", end_date=None, market_
324
  required_return = calculate_required_return(risk_free_rate, beta, market_risk_premium)
325
  cost_of_equity = calculate_cost_of_equity(beta, market_risk_premium, risk_free_rate)
326
 
327
- current_price = yf.download(ticker, period="1d")['Adj Close'].iloc[-1]
 
 
 
 
 
 
328
  outstanding_shares = get_outstanding_shares(ticker)
329
  market_cap = current_price * outstanding_shares
330
 
@@ -367,48 +395,26 @@ st.title("Stock Price Fair Valuation with DCF")
367
  # Explanation of the analysis
368
  st.markdown("""
369
  This app performs a Discounted Cash Flow (DCF) analysis to estimate the intrinsic value of a stock.
370
-
371
  It calculates the present value of expected future cash flows, factoring in growth rates, beta, cost of equity, cost of debt, and market conditions.
372
  """)
373
 
374
- #st.markdown("### DCF Model Overview")
375
-
376
  with st.expander("DCF Analysis Overview", expanded=False):
377
  st.latex(r"""
378
  \text{DCF} = \sum_{t=1}^{n} \frac{\text{FCF}_t}{(1 + r)^t} + \frac{\text{TV}}{(1 + r)^n}
379
  """)
380
 
381
- # st.markdown("""
382
- # Where:
383
- # - \( FCF_t \) is the Free Cash Flow in year \( t \)
384
- # - \( r \) is the discount rate (WACC)
385
- # - \( n \) is the number of projected years
386
- # - \( TV \) is the Terminal Value
387
- # """)
388
-
389
  st.markdown("""
390
  - **Cost of Equity** is calculated using CAPM, considering the risk-free rate, the stock’s beta, and the market risk premium.
391
-
392
  - **Cost of Debt** is the effective interest rate on debt, adjusted for the tax benefit.
393
-
394
  - **WACC** combines the cost of equity and after-tax cost of debt, weighted by the company’s capital structure. It’s used as the discount rate in the DCF analysis.
395
-
396
  - **Free Cash Flow (FCF)** is the cash available after capital expenditures, used for projecting future cash flows.
397
-
398
  - **Terminal Value (TV)** estimates the company’s value beyond the forecast period using a terminal growth rate, which is a conservative long-term rate.
399
-
400
  - **Growth Rates** are the analysts' forecasts for EPS growth over the next 5 years, which are used to project near-term FCF.
401
-
402
  - **Intrinsic Value per Share** is the total present value of future cash flows divided by shares outstanding.
403
-
404
  - **Margin of Safety** is the difference between the intrinsic value and the market price, providing a buffer for investment decisions.
405
-
406
  For more details on the methodology, [click here](https://entreprenerdly.com/calculate-fair-value-of-stocks-using-dcf-with-public-data/).
407
  """)
408
 
409
-
410
-
411
-
412
  with st.sidebar:
413
  with st.expander("How to Use the App", expanded=False):
414
  st.markdown("""
@@ -427,32 +433,37 @@ with st.sidebar:
427
  risk_free_rate_ticker = st.text_input("Risk-Free Rate Ticker", "^TYX", help="Enter the ticker for the risk-free rate, e.g., '^TYX' for the 30-year Treasury yield.")
428
 
429
  if st.sidebar.button("Run DCF Analysis"):
430
- with st.spinner("Performing DCF analysis..."):
431
- analysis_results = perform_dcf_analysis(
432
- ticker_symbol,
433
- start_date=start_date_for_beta.strftime('%Y-%m-%d'),
434
- market_ticker=market_ticker_for_beta,
435
- terminal_growth_rate=terminal_growth_rate,
436
- risk_free_rate_ticker=risk_free_rate_ticker
437
- )
438
-
439
- if analysis_results:
440
- # Retrieve DCF and Levered DCF valuations
441
- automatic_dcf_valuation = get_dcf_valuation(ticker_symbol, FMP_API_KEY)
442
- levered_dcf_valuation = get_levered_dcf_valuation(ticker_symbol, FMP_API_KEY)
 
443
 
444
- # Add DCF valuations to the analysis results
445
- analysis_results['Automatic_DCF_Valuation'] = automatic_dcf_valuation
446
- analysis_results['Levered_DCF_Valuation'] = levered_dcf_valuation
447
 
448
- # Plot the results
449
- plot_intrinsic_value_vs_stock_price(analysis_results)
450
 
451
- # Print key metrics
452
- print_key_metrics(analysis_results)
453
 
454
- # Display financial dataframes
455
- display_financial_dataframes(analysis_results)
 
 
 
 
456
 
457
  hide_streamlit_style = """
458
  <style>
@@ -460,4 +471,4 @@ hide_streamlit_style = """
460
  footer {visibility: hidden;}
461
  </style>
462
  """
463
- st.markdown(hide_streamlit_style, unsafe_allow_html=True)
 
10
  import os
11
 
12
  # Financial Modeling Prep API Key
13
+ FMP_API_KEY = os.getenv("FMP_API_KEY")
14
 
15
  # Function to get data from Finviz
16
  def get_finviz_data(ticker):
 
34
 
35
  # Function to calculate beta
36
  def calculate_beta(ticker, start_date="2018-01-01", end_date=None, market_ticker="^GSPC"):
37
+ stock_data = yf.download(ticker, start=start_date, end=end_date, auto_adjust=False)
38
+ market_data = yf.download(market_ticker, start=start_date, end=end_date, auto_adjust=False)
39
 
40
+ if isinstance(stock_data.columns, pd.MultiIndex):
41
+ stock_data.columns = stock_data.columns.get_level_values(0)
42
+ if isinstance(market_data.columns, pd.MultiIndex):
43
+ market_data.columns = market_data.columns.get_level_values(0)
44
+
45
+ if stock_data.empty or market_data.empty:
46
+ raise ValueError(f"No data retrieved for {ticker} or {market_ticker}")
47
+ if len(stock_data) < 2 or len(market_data) < 2:
48
+ raise ValueError("Insufficient data points for beta calculation")
49
+
50
+ stock_returns = stock_data['Adj Close'].pct_change().dropna()
51
+ market_returns = market_data['Adj Close'].pct_change().dropna()
52
 
53
  combined_data = pd.concat([stock_returns, market_returns], axis=1).dropna()
54
  combined_data.columns = ['Stock_Returns', 'Market_Returns']
 
62
 
63
  # Function to get risk-free rate
64
  def get_risk_free_rate(risk_free_rate_ticker="^TYX"):
65
+ treasury_data = yf.download(risk_free_rate_ticker, period="1d", auto_adjust=False)
66
+ if isinstance(treasury_data.columns, pd.MultiIndex):
67
+ treasury_data.columns = treasury_data.columns.get_level_values(0)
68
+ if treasury_data.empty:
69
+ raise ValueError(f"No data retrieved for {risk_free_rate_ticker}")
70
+
71
+ risk_free_rate = treasury_data['Close'].iloc[-1] / 100
72
  return risk_free_rate
73
 
74
  # Function to calculate market risk premium
75
  def calculate_market_risk_premium(market_ticker="^GSPC", start_date="2018-01-01", end_date=None, risk_free_rate_ticker="^TYX"):
76
+ market_data = yf.download(market_ticker, start=start_date, end=end_date, auto_adjust=False)
77
+ if isinstance(market_data.columns, pd.MultiIndex):
78
+ market_data.columns = market_data.columns.get_level_values(0)
79
+ if market_data.empty:
80
+ raise ValueError(f"No data retrieved for {market_ticker}")
81
+ if len(market_data) < 2:
82
+ raise ValueError("Insufficient data points for market risk premium calculation")
83
+
84
+ market_returns = market_data['Adj Close'].pct_change().dropna()
85
  average_annual_market_return = market_returns.mean() * 252
86
 
87
  risk_free_rate = get_risk_free_rate(risk_free_rate_ticker)
 
242
  def get_levered_dcf_valuation(ticker, api_key):
243
  url = f"https://financialmodelingprep.com/api/v4/advanced_levered_discounted_cash_flow?symbol={ticker}&apikey={api_key}"
244
  response = requests.get(url)
245
+ if response.status_code == 200:
246
  data = response.json()
247
  if data:
248
  return data[0]['equityValuePerShare']
 
346
  required_return = calculate_required_return(risk_free_rate, beta, market_risk_premium)
347
  cost_of_equity = calculate_cost_of_equity(beta, market_risk_premium, risk_free_rate)
348
 
349
+ current_price_data = yf.download(ticker, period="1d", auto_adjust=False)
350
+ if isinstance(current_price_data.columns, pd.MultiIndex):
351
+ current_price_data.columns = current_price_data.columns.get_level_values(0)
352
+ if current_price_data.empty:
353
+ raise ValueError(f"No current price data retrieved for {ticker}")
354
+ current_price = current_price_data['Adj Close'].iloc[-1]
355
+
356
  outstanding_shares = get_outstanding_shares(ticker)
357
  market_cap = current_price * outstanding_shares
358
 
 
395
  # Explanation of the analysis
396
  st.markdown("""
397
  This app performs a Discounted Cash Flow (DCF) analysis to estimate the intrinsic value of a stock.
 
398
  It calculates the present value of expected future cash flows, factoring in growth rates, beta, cost of equity, cost of debt, and market conditions.
399
  """)
400
 
 
 
401
  with st.expander("DCF Analysis Overview", expanded=False):
402
  st.latex(r"""
403
  \text{DCF} = \sum_{t=1}^{n} \frac{\text{FCF}_t}{(1 + r)^t} + \frac{\text{TV}}{(1 + r)^n}
404
  """)
405
 
 
 
 
 
 
 
 
 
406
  st.markdown("""
407
  - **Cost of Equity** is calculated using CAPM, considering the risk-free rate, the stock’s beta, and the market risk premium.
 
408
  - **Cost of Debt** is the effective interest rate on debt, adjusted for the tax benefit.
 
409
  - **WACC** combines the cost of equity and after-tax cost of debt, weighted by the company’s capital structure. It’s used as the discount rate in the DCF analysis.
 
410
  - **Free Cash Flow (FCF)** is the cash available after capital expenditures, used for projecting future cash flows.
 
411
  - **Terminal Value (TV)** estimates the company’s value beyond the forecast period using a terminal growth rate, which is a conservative long-term rate.
 
412
  - **Growth Rates** are the analysts' forecasts for EPS growth over the next 5 years, which are used to project near-term FCF.
 
413
  - **Intrinsic Value per Share** is the total present value of future cash flows divided by shares outstanding.
 
414
  - **Margin of Safety** is the difference between the intrinsic value and the market price, providing a buffer for investment decisions.
 
415
  For more details on the methodology, [click here](https://entreprenerdly.com/calculate-fair-value-of-stocks-using-dcf-with-public-data/).
416
  """)
417
 
 
 
 
418
  with st.sidebar:
419
  with st.expander("How to Use the App", expanded=False):
420
  st.markdown("""
 
433
  risk_free_rate_ticker = st.text_input("Risk-Free Rate Ticker", "^TYX", help="Enter the ticker for the risk-free rate, e.g., '^TYX' for the 30-year Treasury yield.")
434
 
435
  if st.sidebar.button("Run DCF Analysis"):
436
+ try:
437
+ with st.spinner("Performing DCF analysis..."):
438
+ analysis_results = perform_dcf_analysis(
439
+ ticker_symbol,
440
+ start_date=start_date_for_beta.strftime('%Y-%m-%d'),
441
+ market_ticker=market_ticker_for_beta,
442
+ terminal_growth_rate=terminal_growth_rate,
443
+ risk_free_rate_ticker=risk_free_rate_ticker
444
+ )
445
+
446
+ if analysis_results:
447
+ # Retrieve DCF and Levered DCF valuations
448
+ automatic_dcf_valuation = get_dcf_valuation(ticker_symbol, FMP_API_KEY)
449
+ levered_dcf_valuation = get_levered_dcf_valuation(ticker_symbol, FMP_API_KEY)
450
 
451
+ # Add DCF valuations to the analysis results
452
+ analysis_results['Automatic_DCF_Valuation'] = automatic_dcf_valuation
453
+ analysis_results['Levered_DCF_Valuation'] = levered_dcf_valuation
454
 
455
+ # Plot the results
456
+ plot_intrinsic_value_vs_stock_price(analysis_results)
457
 
458
+ # Print key metrics
459
+ print_key_metrics(analysis_results)
460
 
461
+ # Display financial dataframes
462
+ display_financial_dataframes(analysis_results)
463
+ else:
464
+ st.error("DCF analysis failed. Check ticker or API key.")
465
+ except Exception as e:
466
+ st.error(f"An error occurred while running the analysis: {e}")
467
 
468
  hide_streamlit_style = """
469
  <style>
 
471
  footer {visibility: hidden;}
472
  </style>
473
  """
474
+ st.markdown(hide_streamlit_style, unsafe_allow_html=True)