File size: 5,262 Bytes
1b83e39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cae4a9c
dae3936
 
1b83e39
 
 
 
dae3936
1b83e39
 
 
 
 
 
 
dae3936
 
1b83e39
 
 
 
 
 
dae3936
1b83e39
 
 
 
 
 
 
 
dae3936
 
1b83e39
dae3936
1b83e39
 
dae3936
 
 
 
 
 
 
 
 
1b83e39
 
 
 
 
 
 
 
dae3936
 
1b83e39
 
 
 
 
 
 
 
 
 
 
 
 
 
dae3936
1b83e39
 
dae3936
 
1b83e39
 
 
dae3936
1b83e39
 
 
dae3936
1b83e39
dae3936
 
 
1b83e39
 
 
 
dae3936
 
 
 
1b83e39
dae3936
 
 
1b83e39
fbdf74c
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
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 output_results(start_date, end_date, tickers_string, investment_amount):
    tickers = tickers_string.split(',')
    
    # Get Stock Prices
    stocks_df = yf.download(tickers, start=start_date, end=end_date)['Adj Close']
    company_names = stocks_df['Adj Close'].columns.tolist()
    # 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')
    
    # Calculate 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.04)
    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').reset_index()
    weights_df.columns = ['Tickers', 'Weights']

    # Get the latest prices
    latest_prices = get_latest_prices(stocks_df)

    # Allocate the stocks based on the given budget
    da = DiscreteAllocation(weights, latest_prices, total_portfolio_value=investment_amount)
    allocation, leftover = da.lp_portfolio()
    
    allocation_df = pd.DataFrame(list(allocation.items()), columns=['Ticker', 'Shares'])
    
    # 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, allocation_df,  \
            expected_annual_return, leftover.round(), fig_indiv_prices, fig_cum_returns


with gr.Blocks() as app:
    with gr.Row():
        gr.HTML("<h1>Indian 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("TCS.NS,INFY.NS,RELIANCE.NS,HDFCBANK.NS,ICICIBANK.NS,SBIN.NS", 
                                    label='Enter all stock tickers to be included in portfolio separated \
                                    by commas WITHOUT spaces, e.g. "TCS.NS,INFY.NS,RELIANCE.NS,HDFCBANK.NS,ICICIBANK.NS,SBIN.NS"')
        investment_amount = gr.Number(label="Investment Amount (in ₹)")
        btn = gr.Button("Get Optimized Portfolio")
       
    # with gr.Row():
    #     gr.HTML("<h3>Optimized Portfolio Metrics</h3>")
        
    with gr.Row():
        expected_annual_return = gr.Text(label="Expected Annual Return")
        leftover = gr.Number(label="Leftover Amount (in ₹)")            
   
    with gr.Row():        
        fig_cum_returns_optimized = gr.Plot(label="Cumulative Returns of Optimized Portfolio (Starting Price of ₹100)")
        allocation_df = gr.DataFrame(label="Stock Allocation")
        
    # 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")
        
    # with gr.Row():
    #     allocation_df = gr.DataFrame(label="Stock Allocation")
        #leftover = gr.Number(label="Leftover Amount (in ₹)")

    btn.click(fn=output_results, inputs=[start_date, end_date, tickers_string, investment_amount], 
              outputs=[fig_cum_returns_optimized, allocation_df, \
                       expected_annual_return,leftover, fig_indiv_prices, fig_cum_returns])

app.launch()