import gradio as gr import pandas as pd import yfinance as yf from datetime import timedelta,datetime import pytz import matplotlib.pyplot as plt from PIL import Image import numpy as np nifty_list = ["ADANIENT","ADANIPORTS","APOLLOHOSP","ASIANPAINT","AXISBANK","BAJAJ-AUTO","BAJFINANCE","BAJAJFINSV","BPCL","BHARTIARTL","BRITANNIA","CIPLA","COALINDIA","DIVISLAB","DRREDDY","EICHERMOT","GRASIM","HCLTECH","HDFCBANK","HDFCLIFE","HEROMOTOCO","HINDALCO","HINDUNILVR","ICICIBANK","ITC","INDUSINDBK","INFY","JSWSTEEL","KOTAKBANK","LTIM","LT","M&M","MARUTI","NTPC","NESTLEIND","ONGC","POWERGRID","RELIANCE","SBILIFE","SBIN","SUNPHARMA","TCS","TATACONSUM","TATAMOTORS","TATASTEEL","TECHM","TITAN","UPL","ULTRACEMCO","WIPRO","%5ENSEI"] class Stocks: def __init__(self, symbol): self.symbol = symbol self.data = self.fetch_data() def fetch_data(self): try: ticker_symbol = self.symbol if self.symbol[0] == '%' else f"{self.symbol}.ns" data = yf.Ticker(ticker_symbol).history(period="10y", auto_adjust=True) return data except Exception as e: print(f"Error fetching data for {self.symbol}: {e}") return None def currentdateavailability(self, curDate): if curDate in self.data.index: return curDate else: curDate_dt = datetime.strptime(curDate, "%Y-%m-%d") newcDate_dt = curDate_dt - timedelta(days=1) newcDate_str = newcDate_dt.strftime("%Y-%m-%d") return self.currentdateavailability(newcDate_str) def CurPrice(self, curDate=None): curDate = self.currentdateavailability(curDate) return self.data.loc[curDate, 'Close'] if curDate is not None else self.data.iloc[-1]['Close'] def NDayRet(self, N, curDate): curDate = self.currentdateavailability(curDate) NDate = self.data.index[self.data.index.get_loc(curDate) - N] return self.data.loc[curDate, 'Close'] - self.data.loc[NDate, 'Close'] def DailyRet(self, curDate): curDate = self.currentdateavailability(curDate) return self.data.loc[curDate, 'Close'] - self.data.loc[curDate, 'Open'] def Last30daysPrice(self, curDate=None): if curDate is not None: curDate = self.currentdateavailability(curDate) curDate_index = self.data.index.get_loc(curDate) return self.data.iloc[curDate_index - 30:curDate_index]['Close'].values else: return self.data.iloc[-30:]['Close'].values # This below function returns last 30 calender days close prices i.e. 30 days including holidays so less than 30 days close values are returned. Above fuction gives last 30 trading day close prices. # def Last30daysPrice(self, curDate=None): # curDate = self.currentdateavailability(curDate) # if curDate is not None: # curDate_dt = datetime.strptime(curDate, "%Y-%m-%d") # days_ago_30 = curDate_dt - timedelta(days=30) # thirty_days_ago_date = days_ago_30.strftime("%Y-%m-%d") # thirty_days_ago_date = self.currentdateavailability(thirty_days_ago_date) # curDate_index = self.data.index.get_loc(curDate) # thirty_days_ago_index = self.data.index.get_loc(thirty_days_ago_date) # return self.data.iloc[thirty_days_ago_index:curDate_index + 1]['Close'].values # else: # return self.data.iloc[-30:]['Close'].values stocks_dict = {symbol: Stocks(symbol) for symbol in nifty_list} nifty_stocks = {symbol: stocks_dict[symbol] for symbol in nifty_list[:-1]} nifty50 = {"nifty50": stocks_dict[nifty_list[-1]]} def dateoffset(input_date_str): input_date_dt = datetime.strptime(input_date_str, "%Y-%m-%d") new_date_dt = input_date_dt - timedelta(days=1) new_date_str = new_date_dt.strftime("%Y-%m-%d") return new_date_str def setdates(startdate, enddate): while startdate not in nifty50["nifty50"].data.index: startdate = dateoffset(startdate) while enddate not in nifty50["nifty50"].data.index: enddate = dateoffset(enddate) return startdate, enddate def organisedata(startdate, enddate): startdate, enddate = setdates(startdate, enddate) symbols = list(nifty_stocks.keys()) common_index = nifty50["nifty50"].data.loc[startdate:enddate].index data_frame = pd.DataFrame(index=symbols, columns=common_index) for symbol, stock_object in nifty_stocks.items(): stock_data = stock_object.data.loc[startdate:enddate, 'Close'] data_frame.loc[symbol] = stock_data.reindex(common_index).values return data_frame def previoustimeframedata(n, startdate): startdate_dt = pd.to_datetime(startdate) ndaysagodate = startdate_dt - timedelta(days=int(n)) ndaysagodate_str = ndaysagodate.strftime("%Y-%m-%d") startdate_str = startdate_dt.strftime("%Y-%m-%d") return organisedata(ndaysagodate_str, startdate_str) def portfoliooperations(equity,startdate,ndaywindow,portfolio): startdate_dt = pd.to_datetime(startdate) windowenddate = startdate_dt + timedelta(days=int(ndaywindow)) windowenddate_str = windowenddate.strftime("%Y-%m-%d") startdate,windowenddate = setdates(startdate,windowenddate_str) window_data = organisedata(startdate,windowenddate) differences = window_data.iloc[:, -1] - window_data.iloc[:, 0] next_portfolio = differences[differences > 0].index.tolist() portfolio_sum = window_data.loc[portfolio, window_data.columns[0]].sum() multiplier = equity / portfolio_sum if portfolio_sum != 0 else 0 portfolio_value = pd.DataFrame(index=window_data.columns, columns=['value']) for date in window_data.columns: portfolio_sum = window_data.loc[portfolio, date].sum() portfolio_value.loc[date, 'value'] = portfolio_sum * multiplier return next_portfolio,portfolio_value def mainfunction (equity,startdate,enddate,ndaywindow): pastwindow = previoustimeframedata(n=ndaywindow,startdate=startdate) # No Errors untill here differences = pastwindow.iloc[:, -1] - pastwindow.iloc[:, 0] portfolio = differences[differences > 0].index.tolist() # No Errors untill here portfolio,portfolio_value = portfoliooperations(equity=equity,startdate=startdate,ndaywindow=ndaywindow,portfolio=portfolio) enddate_tz = datetime.strptime(enddate,"%Y-%m-%d").replace(tzinfo=pytz.timezone('Asia/Kolkata')) while portfolio_value.index[-1] < pd.to_datetime(enddate_tz) - timedelta(days=int(ndaywindow)): portfolio,new_portfolio_value = portfoliooperations(equity=equity,startdate=startdate,ndaywindow=ndaywindow,portfolio=portfolio) portfolio_value = pd.concat([portfolio_value, new_portfolio_value]) startdate = (pd.to_datetime(startdate)+ timedelta(days=int(ndaywindow))).strftime("%Y-%m-%d") equity = portfolio_value.iloc[-1, 0] return portfolio_value def calculate_cagr(series): total_return = (series.iloc[-1] / series.iloc[0]) - 1 num_years = len(series) / 252 cagr = (1 + total_return) ** (1 / num_years) - 1 return cagr * 100 def calculate_volatility(series): return series.pct_change().std() * np.sqrt(252) * 100 def calculate_sharpe_ratio(series, risk_free_rate=0): cagr = calculate_cagr(series) volatility = calculate_volatility(series) sharpe_ratio = (cagr - risk_free_rate) / volatility return sharpe_ratio def final_function(equity,startdate,enddate,ndaywindow): equity = int(equity) ndaywindow = int(ndaywindow) portfolio_value = mainfunction(equity=equity,startdate=startdate,enddate=enddate,ndaywindow=ndaywindow) nifty_data = nifty50["nifty50"].data subset_data = nifty_data[startdate:enddate] initial_nifty = subset_data['Close'][0] nifty_dataseries = (equity/initial_nifty)*subset_data['Close'] plt.figure(figsize=(10, 6)) plt.plot(portfolio_value['value'], label='Strategy') plt.plot(nifty_dataseries, label='Nifty50 as Benchmark') plt.title('Benchmark vs Strategy') plt.xlabel('Date') plt.ylabel('Equity') plt.legend() image_path = "output_plot.png" plt.savefig(image_path) plt.close() image = Image.open(image_path) strategy_cagr = calculate_cagr(portfolio_value['value']) strategy_volatility = calculate_volatility(portfolio_value['value']) strategy_sharpe_ratio = calculate_sharpe_ratio(portfolio_value['value']) benchmark_cagr = calculate_cagr(nifty_dataseries) benchmark_volatility = calculate_volatility(nifty_dataseries) benchmark_sharpe_ratio = calculate_sharpe_ratio(nifty_dataseries) return image, strategy_cagr, strategy_volatility, strategy_sharpe_ratio, benchmark_cagr, benchmark_volatility, benchmark_sharpe_ratio title = "Portfolio tracking Nifty50 Stocks" description = """ This App Demo is made for an Assignment. This Demo takes Initial Equity, Start Date, End Date, Time Window as inputs. Due to COVID 19 causing the fall of almost all Stock Prices, some Dates might result in strategy falling to zero at March 2020. Hence please try other dates """ iface = gr.Interface( fn=final_function, inputs=[ gr.Textbox(label="Equity",placeholder="Enter Equity Number"), gr.Textbox(label="Start Date",placeholder="YYYY-MM-DD"), gr.Textbox(label="End Date",placeholder="YYYY-MM-DD"), gr.Textbox(label="N-day Window",placeholder="Enter Window in Days") ], outputs=[ gr.Image(type="pil"), gr.Textbox(label="Strategy CAGR (%)"), gr.Textbox(label="Strategy Volatility (%)"), gr.Textbox(label="Strategy Sharpe Ratio"), gr.Textbox(label="Benchmark CAGR (%)"), gr.Textbox(label="Benchmark Volatility (%)"), gr.Textbox(label="Benchmark Sharpe Ratio") ], title=title, description=description, ) if __name__ == "__main__": iface.launch()