import yfinance as yf import numpy as np import pandas as pd import plotly.graph_objs as go import gradio as gr def plot_volatility_bands(ticker, reference_year): # Retrieving historical data and performing some preprocessing df = yf.download(ticker) df['Returns'] = df['Adj Close'].pct_change(1) df['Adj Low'] = df['Low'] - (df['Close'] - df['Adj Close']) df['Adj High'] = df['High'] - (df['Close'] - df['Adj Close']) df['Adj Open'] = df['Open'] - (df['Close'] - df['Adj Close']) df = df.fillna(0) # Obtaining the annualized volatility T = 20 df['Annualized_Vol'] = np.round(df['Returns'].rolling(T).std()*np.sqrt(252), 2) # Calculating Bands High_Band_1std = df.loc[reference_year]["Annualized_Vol"][-1]*df.loc[reference_year]["Adj Close"][-1] + df.loc[reference_year]["Adj Close"][-1] Low_Band_1std = df.loc[reference_year]["Adj Close"][-1] - df.loc[reference_year]["Annualized_Vol"][-1]*df.loc[reference_year]["Adj Close"][-1] High_Band_2std = 2*df.loc[reference_year]["Annualized_Vol"][-1]*df.loc[reference_year]["Adj Close"][-1] + df.loc[reference_year]["Adj Close"][-1] Low_Band_2std = df.loc[reference_year]["Adj Close"][-1] - 2*df.loc[reference_year]["Annualized_Vol"][-1]*df.loc[reference_year]["Adj Close"][-1] High_Band_3std = 3*df.loc[reference_year]["Annualized_Vol"][-1]*df.loc[reference_year]["Adj Close"][-1] + df.loc[reference_year]["Adj Close"][-1] Low_Band_3std = df.loc[reference_year]["Adj Close"][-1] - 3*df.loc[reference_year]["Annualized_Vol"][-1]*df.loc[reference_year]["Adj Close"][-1] # Creating Candlestick chart candlestick = go.Candlestick(x = df.loc[str(int(reference_year) + 1)].index, open = df.loc[str(int(reference_year) + 1)]['Adj Open'], high = df.loc[str(int(reference_year) + 1)]['Adj High'], low = df.loc[str(int(reference_year) + 1)]['Adj Low'], close = df.loc[str(int(reference_year) + 1)]['Adj Close'], increasing = dict(line=dict(color = 'red')), decreasing = dict(line=dict(color = 'black')), name = 'Candlesticks') # Defining layout layout = go.Layout(title = {'text': f'Volatility-Based Supply and Demand Levels ({ticker})

    Yearly Forecast - {str(int(reference_year) + 1)}
', 'x': .035, 'xanchor': 'left'}, yaxis = dict(title = 'Price (USD)', tickfont=dict(size=16)), xaxis = dict(title = 'Date'), template = 'seaborn', plot_bgcolor = '#F6F5F5', paper_bgcolor = '#F6F5F5', height = 450, width = 650, showlegend=False, xaxis_rangeslider_visible = False) fig = go.Figure(data = [candlestick], layout = layout) # Fixing the empty spaces in the X-Axis dt_all = pd.date_range(start = df.index[0] , end = df.index[-1] , freq = "D") dt_all_py = [d.to_pydatetime() for d in dt_all] dt_obs_py = [d.to_pydatetime() for d in df.index] dt_breaks = [d for d in dt_all_py if d not in dt_obs_py] fig.update_xaxes( rangebreaks = [dict(values = dt_breaks)] ) # 1σ fig.add_hline(y = High_Band_1std, line_width = 2, line_dash = "dot", line_color = "green") fig.add_hline(y = Low_Band_1std, line_width = 2, line_dash = "dot", line_color = "red") # 2σ fig.add_hline(y = High_Band_2std, line_width = 4, line_dash = "dash", line_color = "green") fig.add_hline(y = Low_Band_2std, line_width = 4, line_dash = "dash", line_color = "red") # 3σ fig.add_hline(y = High_Band_3std, line_width = 6, line_dash = "dashdot", line_color = "green") fig.add_hline(y = Low_Band_3std, line_width = 6, line_dash = "dashdot", line_color = "red") fig.show() # Printing Supply and Demand Levels print(f"\nVolatility-Based Supply and Demand Levels for {ticker} in {int(reference_year) + 1}\n") print(f"Supply Level 3σ: {High_Band_3std.round(2)}\n") print(f"Supply Level 2σ: {High_Band_2std.round(2)}\n") print(f"Supply Level 1σ: {High_Band_1std.round(2)}\n") print('-' * 165, '\n') print(f"Demand Level 1σ: {Low_Band_1std.round(2)}\n") print(f"Demand Level 2σ: {Low_Band_2std.round(2)}\n") print(f"Demand Level 3σ: {Low_Band_3std.round(2)}\n") # Creating the text output text_info = f""" Volatility-Based Supply and Demand Levels for {ticker} in {int(reference_year) + 1}\n Supply Level 3σ: {High_Band_3std.round(2)}\n Supply Level 2σ: {High_Band_2std.round(2)}\n Supply Level 1σ: {High_Band_1std.round(2)}\n -------------------------\n Demand Level 1σ: {Low_Band_1std.round(2)}\n Demand Level 2σ: {Low_Band_2std.round(2)}\n Demand Level 3σ: {Low_Band_3std.round(2)}\n """ return fig, text_info def wrapper_func(ticker, reference_year): try: fig, text_info = plot_volatility_bands(ticker, str(int(reference_year))) return fig, text_info except Exception as e: error_message = str(e) return error_message, error_message iface = gr.Interface( title = '📈 Volatility-Based Supply and Demand Levels 📉', description="""
The ideas presented in this app are based on the research paper Supply and Demand Levels Forecasting Based on Returns Volatility,authored by Leandro Guerra.
In the Kaggle Notebook, Volatility-Based Supply & Demand Levels Forecasting, you can read further on how these supply and demand levels are calculated.

Intructions
Enter the ticker of a security as it is displayed on Yahoo Finance. Then inform a year of reference.
The supply and demand levels are forecastings for the following year. If you inform 2022 as input, the plot will contain the forecastings for 2023.

• Dot lines represent one standard deviation (68.7% probability).
• Dash lines represent two standard deviations (95.4% probability).
• Dash and dot lines represent three standard deviations (99.7% probability).""", fn=wrapper_func, inputs=[ gr.Textbox(label="Enter the Ticker as it Appears on Yahoo Finance"), gr.Number(label="Enter the Year of Reference") ], outputs=[ gr.Plot(label = ""), gr.Textbox(label = "") ], css=".gr-input {width: 60px;}" ) iface.launch()