File size: 7,219 Bytes
4ee8ea3
 
 
 
 
 
 
 
 
a906ac4
4ee8ea3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6834747
4ee8ea3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f57be2
4ee8ea3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343a02a
4ee8ea3
 
f732c53
343a02a
 
f732c53
6c634e7
 
4ee8ea3
 
176e9df
802d262
 
176e9df
802d262
5cf0dbb
 
 
 
 
 
 
f732c53
 
343a02a
 
f732c53
 
3c1f5a2
 
7f57be2
3c1f5a2
4ee8ea3
 
 
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
131
132
133
134
135
136
137
138
139
140
141
142
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, multi_level_index=False, auto_adjust=False)
    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'<b>Volatility-Based Supply and Demand Levels ({ticker})<br><br><sup>&nbsp;&nbsp;&nbsp;&nbsp;<i>Yearly Forecast - {str(int(reference_year) + 1)}</i></sup></b>',
                                'x': .035, 'xanchor': 'left'},
                       yaxis = dict(title = '<b>Price (USD)</b>',
                                   tickfont=dict(size=16)),
                       xaxis = dict(title = '<b>Date</b>'),
                       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="""<br> The ideas presented in this app are based on the research paper <a href = "https://www.outspokenmarket.com/uploads/8/8/2/3/88233040/supply_and_demand_levels_forecasting_based_on_returns_volatility.pdf">Supply and Demand Levels Forecasting Based on Returns Volatility</a>,authored by <a href = "https://www.outspokenmarket.com/">Leandro Guerra</a>. <br>
                In the Kaggle Notebook, <a href = "https://www.kaggle.com/code/lusfernandotorres/volatility-based-supply-and-demand-levels/notebook">Volatility-Based Supply & Demand Levels Forecasting</a>, you can read further on how these supply and demand levels are calculated. <br><br>
                <b>Intructions</b><br>
                Enter the ticker of a security as it is displayed on Yahoo Finance. Then inform a year of reference. <br>
                The supply and demand levels are forecastings for the following year. If you inform <i>2022</i> as input, the plot will contain the forecastings for <i>2023</i>. <br><br>
                
                β€’ Dot lines represent one standard deviation (68.7% probability). <br>

                β€’ Dash lines represent two standard deviations (95.4% probability). <br>

                β€’ 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()