File size: 3,629 Bytes
a4a51f6
295d75e
 
 
 
 
 
 
 
 
 
 
 
 
129481e
295d75e
 
4c35a81
 
295d75e
 
4136930
295d75e
4136930
 
 
 
295d75e
 
4c35a81
4136930
 
 
 
 
295d75e
 
 
 
 
fc827bc
 
 
 
 
4a7915c
4c35a81
4136930
 
 
 
 
 
 
 
 
 
 
 
 
6e49075
4a7915c
ce4dda8
e32d2bd
 
ce4dda8
6e49075
4a7915c
6e49075
e32d2bd
4a7915c
295d75e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import altair as alt
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import yfinance as yf

# The efficient frontier function modified for use in Streamlit
def plot_efficient_frontier(dataframes, names):
    # Your efficient frontier function with slight modifications for Streamlit
    # This is the same function you've been working with, so ensure it's correctly adjusted for Streamlit output
    
    # Ensure you include the code for the function here
    # Calculate daily returns for each asset
    returns = pd.concat([df['Close'].pct_change().dropna() for df in dataframes], axis=1)
    returns.columns = names

    mean_returns = returns.mean() * 252
    cov_matrix = returns.cov() * 252

    num_portfolios = 10000

    all_weights = np.zeros((num_portfolios, len(dataframes)))
    ret_arr = np.zeros(num_portfolios)
    vol_arr = np.zeros(num_portfolios)
    sharpe_arr = np.zeros(num_portfolios)

    for i in range(num_portfolios):
        weights = np.random.random(len(dataframes))
        weights /= np.sum(weights)
        all_weights[i, :] = weights

        ret_arr[i] = np.dot(weights, mean_returns)
        vol_arr[i] = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
        sharpe_arr[i] = ret_arr[i] / vol_arr[i]

    max_sharpe_idx = np.argmax(sharpe_arr)
    max_sharpe_return = ret_arr[max_sharpe_idx]
    max_sharpe_volatility = vol_arr[max_sharpe_idx]

    # Find the portfolio with the minimum volatility
    min_vol_idx = np.argmin(vol_arr)
    min_vol_return = ret_arr[min_vol_idx]
    min_vol_volatility = vol_arr[min_vol_idx]

    

    fig, ax = plt.subplots()
    scatter = ax.scatter(vol_arr, ret_arr, c=sharpe_arr, cmap='Blues')
    colorbar = plt.colorbar(scatter, ax=ax)
    colorbar.set_label('Sharpe Ratio')
    ax.scatter(max_sharpe_volatility, max_sharpe_return, c='red', s=200, marker='*', label='Optimal Portfolio - Max Sharpe Ratio')
    ax.scatter(min_vol_volatility, min_vol_return, c='purple', s=150, edgecolors='black', marker='o', label='Minimum Volatility Portfolio')
    ax.set_xlabel('Volatility')
    ax.set_ylabel('Return')
    # Set axes starting points
    ax.set_xlim(left=0)
    ax.set_ylim(bottom=0)
    ax.set_title('Efficient Frontier')
    st.pyplot(fig)
    
    optimal_weights = all_weights[max_sharpe_idx]
    st.divider()
    st.subheader(f"Expected Annual Return: {max_sharpe_return:.2f}")
    st.subheader(f"Annual Volatility/Risk: {max_sharpe_volatility:.2f}")
    st.divider()
    st.subheader("Optimal Portfolio Weights:")
    for i, name in enumerate(names):
        st.write(f"**{name}:** {100*optimal_weights[i]:.2f}%")
    

# Streamlit application layout
st.title("Portfolio Optimization with Efficient Frontier")

# User input for tickers
user_input = st.text_input("Enter tickers separated by commas (e.g., AAPL,MSFT,GOOGL)", "NVDA,AMD,AAPL,MSFT,GOOGL")

# Process input tickers
tickers = [ticker.strip().upper() for ticker in user_input.split(',')]

# Fetch data and calculate efficient frontier upon button click
if st.button("Optimize Portfolio"):
    # Fetch stock data
    timeframe = '1y'  # Define the timeframe for data fetching
    dataframes = []
    for ticker in tickers:
        tick = yf.Ticker(ticker)
        stock_data = tick.history(period=timeframe)
        dataframes.append(stock_data)
    
    # Ensure we have the data before proceeding
    if len(dataframes) > 0:
        # Plot efficient frontier and display optimal weights
        plot_efficient_frontier(dataframes, tickers)
    else:
        st.write("Please enter valid tickers.")

# Instructions to run the app