File size: 5,594 Bytes
b1965dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55e240c
b1965dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2f32199
7ee8e05
2f32199
b1965dd
 
 
 
 
 
 
 
2f32199
b1965dd
 
55e240c
7327eda
b1965dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13d8d7a
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
import gradio as gr
import yfinance as yf
from pypfopt.discrete_allocation import DiscreteAllocation, get_latest_prices
from pypfopt import EfficientFrontier
from pypfopt import risk_models
from pypfopt import expected_returns
from pypfopt import plotting
import copy
import numpy as np
import pandas as pd
import plotly.express as px
import matplotlib.pyplot as plt
from datetime import datetime
import datetime

def plot_cum_returns(data, title):    
    daily_cum_returns = 1 + data.dropna().pct_change()
    daily_cum_returns = daily_cum_returns.cumprod()*100
    fig = px.line(daily_cum_returns, title=title)
    return fig

def plot_efficient_frontier_and_max_sharpe(mu, S): 
    # Optimize portfolio for max Sharpe ratio and plot it out with efficient frontier curve
    ef = EfficientFrontier(mu, S)
    fig, ax = plt.subplots(figsize=(6,4))
    ef_max_sharpe = copy.deepcopy(ef)
    plotting.plot_efficient_frontier(ef, ax=ax, show_assets=False)
    # Find the max sharpe portfolio
    ef_max_sharpe.max_sharpe(risk_free_rate=0.02)
    ret_tangent, std_tangent, _ = ef_max_sharpe.portfolio_performance()
    ax.scatter(std_tangent, ret_tangent, marker="*", s=100, c="r", label="Max Sharpe")
    # Generate random portfolios with random weights
    n_samples = 1000
    w = np.random.dirichlet(np.ones(ef.n_assets), n_samples)
    rets = w.dot(ef.expected_returns)
    stds = np.sqrt(np.diag(w @ ef.cov_matrix @ w.T))
    sharpes = rets / stds
    ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r")
    # Output
    ax.legend()
    return fig

def output_results(start_date, end_date, tickers_string):
    tickers = tickers_string.split(',')
    
    # Get Stock Prices
    stocks_df = yf.download(tickers, start=start_date, end=end_date)['Adj Close']
    
    # Plot Individual Stock Prices
    fig_indiv_prices = px.line(stocks_df, title='Price of Individual Stocks')
        
    # Plot Individual Cumulative Returns
    fig_cum_returns = plot_cum_returns(stocks_df, 'Cumulative Returns of Individual Stocks Starting with $100')
    
    # Calculatge and Plot Correlation Matrix between Stocks
    corr_df = stocks_df.corr().round(2)
    fig_corr = px.imshow(corr_df, text_auto=True, title = 'Correlation between Stocks')

    # Calculate expected returns and sample covariance matrix for portfolio optimization later
    mu = expected_returns.mean_historical_return(stocks_df)
    S = risk_models.sample_cov(stocks_df)

    # Plot efficient frontier curve
    fig_efficient_frontier = plot_efficient_frontier_and_max_sharpe(mu, S)

    # Get optimized weights
    ef = EfficientFrontier(mu, S)
    ef.max_sharpe(risk_free_rate=0.02)
    weights = ef.clean_weights()
    expected_annual_return, annual_volatility, sharpe_ratio = ef.portfolio_performance()
    
    expected_annual_return, annual_volatility, sharpe_ratio = '{}%'.format((expected_annual_return*100).round(2)), \
    '{}%'.format((annual_volatility*100).round(2)), \
    '{}%'.format((sharpe_ratio*100).round(2))
    
    weights_df = pd.DataFrame.from_dict(weights, orient = 'index')
    weights_df = weights_df.reset_index()
    weights_df.columns = ['Tickers', 'Weights']

    # Calculate returns of portfolio with optimized weights
    stocks_df['Optimized Portfolio'] = 0
    for ticker, weight in weights.items():
        stocks_df['Optimized Portfolio'] += stocks_df[ticker]*weight

    # Plot Cumulative Returns of Optimized Portfolio
    fig_cum_returns_optimized = plot_cum_returns(stocks_df['Optimized Portfolio'], 'Cumulative Returns of Optimized Portfolio Starting with $100')

    return  fig_cum_returns_optimized, weights_df, fig_efficient_frontier, fig_corr,   \
            expected_annual_return, annual_volatility, sharpe_ratio, fig_indiv_prices, fig_cum_returns


with gr.Blocks() as app:
    with gr.Row():
        gr.HTML("<h1>Bohmian's Stock Portfolio Optimizer</h1>")
    
    with gr.Row():
        start_date = gr.Textbox("2013-01-01", label="Start Date")
        end_date = gr.Textbox(datetime.datetime.now().date(), label="End Date")
    
    with gr.Row():        
        tickers_string = gr.Textbox("MA,META,V,AMZN,JPM,BA", 
                                    label='Enter all stock tickers to be included in portfolio separated \
                                    by commas WITHOUT spaces, e.g. "MA,META,V,AMZN,JPM,BA"')
        btn = gr.Button("Get Optimized Portfolio")
       
    with gr.Row():
        gr.HTML("<h3>Optimizied Portfolio Metrics</h3>")
        
    with gr.Row():
        expected_annual_return = gr.Text(label="Expected Annual Return")
        annual_volatility = gr.Text(label="Annual Volatility")
        sharpe_ratio = gr.Text(label="Sharpe Ratio")            
   
    with gr.Row():        
        fig_cum_returns_optimized = gr.Plot(label="Cumulative Returns of Optimized Portfolio (Starting Price of $100)")
        weights_df = gr.DataFrame(label="Optimized Weights of Each Ticker")
        
    with gr.Row():
        fig_efficient_frontier = gr.Plot(label="Efficient Frontier")
        fig_corr = gr.Plot(label="Correlation between Stocks")
    
    with gr.Row():
        fig_indiv_prices = gr.Plot(label="Price of Individual Stocks")
        fig_cum_returns = gr.Plot(label="Cumulative Returns of Individual Stocks Starting with $100")

    btn.click(fn=output_results, inputs=[start_date, end_date, tickers_string], 
              outputs=[fig_cum_returns_optimized, weights_df, fig_efficient_frontier, fig_corr,   \
            expected_annual_return, annual_volatility, sharpe_ratio, fig_indiv_prices, fig_cum_returns])

app.launch()