import gradio as gr import yfinance as yf from retry import retry from tqdm.auto import tqdm import pandas as pd import time import mplfinance as mpf from pypfopt import risk_models, expected_returns from pypfopt import plotting from pypfopt import EfficientFrontier, objective_functions import numpy as np from warnings import filterwarnings filterwarnings("ignore") data = pd.DataFrame() def load_data(ticker_list="", start_year=2021): data = yf.download( list(map(lambda x: x.strip().upper(), ticker_list.split(','))), start=f'{start_year}-01-01', interval='1d', keepna=True, period="5y", rounding=True, )['Close'].reset_index().fillna(method='ffill') data.Date = pd.to_datetime(data.Date) return data def _prep_evaluation_report( hist_data, backtest_days, investment_size, weights ): allocation_df = pd.DataFrame({k: [weights[k]] for k in weights}).T testing_data = hist_data[-backtest_days:].copy() allocation_df.columns = ['weights'] init_df = testing_data.iloc[0] end_df = testing_data.iloc[-1] allocation_df = allocation_df.merge( init_df, left_index=True, right_index=True) allocation_df = allocation_df.merge( end_df, left_index=True, right_index=True) allocation_df.columns = ['weights', 'cost_price', 'sell_price'] allocation_df['amount_allocation'] = allocation_df.weights * \ investment_size allocation_df['unit_pnl'] = allocation_df.sell_price - \ allocation_df.cost_price allocation_df['units'] = allocation_df.weights * \ investment_size // allocation_df.cost_price allocation_df['value'] = allocation_df.units * allocation_df.cost_price allocation_df['pnl'] = allocation_df.units * allocation_df.unit_pnl profit = np.round(allocation_df.pnl.sum(), 2) cagr = np.round( ( ( (investment_size + profit)/investment_size )**( 1/(backtest_days/365) ) - 1 )*100, 2 ) return allocation_df.round(2).reset_index(), profit, cagr def re_optimize( hist_data: pd.DataFrame, allocation_df: pd.DataFrame, backtest_days: int, investment_size: int ): return find_weights( hist_data.drop( columns=allocation_df[allocation_df.units == 0]['index'] ), backtest_days, investment_size ) def find_weights( hist_data: pd.DataFrame, backtest_days: int, investment_size: int ): training_data = hist_data[:-backtest_days].copy().set_index( 'Date' ).fillna(0).astype(float) mu = expected_returns.capm_return(training_data) S = risk_models.semicovariance(training_data) initial_weights = np.array( [1/len(training_data.columns)] * len(training_data.columns)) ef = EfficientFrontier(mu, S) ef.add_objective( objective_functions.transaction_cost, w_prev=initial_weights, k=0.02 ) ef.add_objective(objective_functions.L2_reg) ef.max_sharpe() weights = ef.clean_weights() ( expected_annual_return, annual_volatility, sharpe_ratio ) = ef.portfolio_performance() pnl_df, profit, cagr = _prep_evaluation_report( hist_data, backtest_days, investment_size, weights ) return f''' > ## Portfolio Performance |Expected Annual Return|Annual Volatility|Sharpe Ratio|Profit|Profit %|CAGR| |:---:|:---:|:---:|:---:|:---:|:---:| |{expected_annual_return*100:.2f}|{annual_volatility*100:.2f}%|{sharpe_ratio:.2f}|{profit:.2f}|{profit/investment_size*100:.2f}%|{cagr:.2f}| ''', pnl_df.sort_values('pnl', ascending=False) with gr.Blocks() as demo: # with gr.Blocks() as demo: with gr.Row(): with gr.Column(): stocks = gr.Textbox( label="TickerCodes", show_label=True, interactive=True ) backtest_days = gr.Slider( minimum=1, maximum=200, value=90, label="Backtest Days", interactive=True ) investment_size = gr.Slider( minimum=10_000, maximum=10_00_000, value=100_000, interactive=True, step=10_000, label="Investment Amount" ) start_year = gr.Slider( minimum=2020, maximum=2024, interactive=True, value=2021, label="Analysis Start Year" ) with gr.Row(): fetch_data = gr.Button("Load Data") optimize = gr.Button("Optimize") reoptimize = gr.Button("Re-Optimize") with gr.Row(): with gr.Column(): performance = gr.Markdown() pnl_statement = gr.DataFrame() historical_data = gr.DataFrame() fetch_data.click( load_data, [stocks, start_year], [historical_data] ) optimize.click( find_weights, [historical_data, backtest_days, investment_size], [performance, pnl_statement] ) reoptimize.click( re_optimize, [historical_data, pnl_statement, backtest_days, investment_size], [performance, pnl_statement] ) gr.Examples( examples=[ [','.join( ['ADANIENT.BO', 'ADANIPORTS.NS', 'APOLLOHOSP.NS', 'ASIANPAINT.NS', 'AXISBANK.BO', 'BAJAJ-AUTO.NS', 'BAJAJFINSV.NS', 'BAJFINANCE.NS', 'BHARTIARTL.BO', 'BPCL.NS', 'BRITANNIA.NS', 'CIPLA.NS', 'COALINDIA.NS', 'DIVISLAB.NS', 'DRREDDY.BO', 'EICHERMOT.BO', 'GRASIM.NS', 'HCLTECH.NS', 'HDFCBANK.NS', 'HDFCLIFE.BO', 'HEROMOTOCO.BO', 'HINDALCO.BO', 'HINDUNILVR.BO', 'ICICIBANK.NS', 'INDUSINDBK.NS', 'INFY.NS', 'ITC.NS', 'JSWSTEEL.BO', 'KOTAKBANK.BO', 'LT.BO', 'LTIM.BO', 'M&M.NS', 'MARUTI.NS', 'NTPC.BO', 'ONGC.NS', 'POWERGRID.BO', 'RELIANCE.NS', 'SBILIFE.BO', 'SBIN.NS', 'SHRIRAMFIN.BO', 'SUNPHARMA.NS', 'TATACONSUM.BO', 'TATAMOTORS.BO', 'TATASTEEL.BO', 'TCS.NS', 'TECHM.BO', 'TITAN.BO', 'ULTRACEMCO.NS', 'WIPRO.BO' ] ), 90, 100000, 2021 ] ], inputs=[stocks, backtest_days, investment_size, start_year] ) demo.launch(debug=True, )