Spaces:
Runtime error
Runtime error
Upload 2 files
Browse files- .streamlit/config.toml +3 -0
- app.py +147 -0
.streamlit/config.toml
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
[theme]
|
2 |
+
base="dark"
|
3 |
+
primaryColor="#32d18b"
|
app.py
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import yfinance as yf
|
3 |
+
import numpy as np
|
4 |
+
import plotly.graph_objects as go
|
5 |
+
from plotly.subplots import make_subplots
|
6 |
+
from sklearn.cluster import AgglomerativeClustering
|
7 |
+
import streamlit as st
|
8 |
+
import requests
|
9 |
+
from streamlit_lottie import st_lottie
|
10 |
+
|
11 |
+
st.set_page_config(page_title = "Support and resistance levels",
|
12 |
+
page_icon = ':π:',
|
13 |
+
layout = 'wide')
|
14 |
+
|
15 |
+
st.title('π Technical analysis π')
|
16 |
+
st.header('Find support and resistance levels for :blue[price action] analysis!')
|
17 |
+
st.divider()
|
18 |
+
|
19 |
+
def load_lottieurl(url: str):
|
20 |
+
r = requests.get(url)
|
21 |
+
if r.status_code != 200:
|
22 |
+
return None
|
23 |
+
return r.json()
|
24 |
+
|
25 |
+
lottie_url__money = "https://assets1.lottiefiles.com/packages/lf20_06a6pf9i.json"
|
26 |
+
lottie_money = load_lottieurl(lottie_url__money)
|
27 |
+
|
28 |
+
st.sidebar.header('Please choose parameters: ')
|
29 |
+
|
30 |
+
ticker = st.text_input('Select stock to analyse: (Make sure the ticker you search for is supported by Yahoo! Finance).', 'BNB-USD')
|
31 |
+
|
32 |
+
interval = '1h'
|
33 |
+
|
34 |
+
num_clusters = st.sidebar.select_slider(
|
35 |
+
'Select a number of clusters',
|
36 |
+
options=[i for i in range(1,8)])
|
37 |
+
|
38 |
+
rolling_wave_length = st.sidebar.select_slider(
|
39 |
+
'''Select a length of rolling wave
|
40 |
+
(how much data to cluster at one time)''',
|
41 |
+
options=[i for i in range(5, 21)])
|
42 |
+
|
43 |
+
period_num = st.sidebar.select_slider(
|
44 |
+
'Select number of days to display on chart',
|
45 |
+
options=[i for i in range(1, 31)])
|
46 |
+
|
47 |
+
period = '{}d'.format(period_num)
|
48 |
+
df = yf.download(ticker, period = period, interval = interval)
|
49 |
+
df.index = pd.to_datetime(df.index).strftime("%d-%m-%Y %H:%M")
|
50 |
+
df = df.drop(columns = ["Adj Close"])
|
51 |
+
|
52 |
+
left_column, right_column = st.columns(2)
|
53 |
+
|
54 |
+
left_column.markdown('<span style="font-size:20px; font-weight:600; letter-spacing:2px;">Preview data:</span>',
|
55 |
+
unsafe_allow_html = True)
|
56 |
+
left_column.dataframe(df, height = 400)
|
57 |
+
|
58 |
+
with right_column:
|
59 |
+
st_lottie(lottie_money, key="money")
|
60 |
+
|
61 |
+
#creating function
|
62 |
+
def calculate_support_resistance(df, rolling_wave_length, num_clusters):
|
63 |
+
date = df.index
|
64 |
+
df.reset_index(inplace=True)
|
65 |
+
|
66 |
+
max_waves_temp = df.High.rolling(rolling_wave_length).max().rename('waves')
|
67 |
+
min_waves_temp = df.Low.rolling(rolling_wave_length).min().rename('waves')
|
68 |
+
|
69 |
+
max_waves = pd.concat([max_waves_temp, pd.Series(np.zeros(len(max_waves_temp)) + 1)], axis=1)
|
70 |
+
min_waves = pd.concat([min_waves_temp, pd.Series(np.zeros(len(min_waves_temp)) + -1)], axis=1)
|
71 |
+
max_waves.drop_duplicates('waves', inplace=True)
|
72 |
+
min_waves.drop_duplicates('waves', inplace=True)
|
73 |
+
|
74 |
+
waves = pd.concat([max_waves, min_waves]).sort_index()
|
75 |
+
waves = waves[waves[0] != waves[0].shift()].dropna()
|
76 |
+
|
77 |
+
x = np.concatenate((waves.waves.values.reshape(-1, 1),
|
78 |
+
(np.zeros(len(waves)) + 1).reshape(-1, 1)), axis=1)
|
79 |
+
|
80 |
+
cluster = AgglomerativeClustering(n_clusters=num_clusters, linkage='ward')
|
81 |
+
cluster.fit_predict(x)
|
82 |
+
waves['clusters'] = cluster.labels_
|
83 |
+
waves2 = waves.loc[waves.groupby('clusters')['waves'].idxmax()]
|
84 |
+
df.index = date
|
85 |
+
waves2.waves.drop_duplicates(keep='first', inplace=True)
|
86 |
+
|
87 |
+
return waves2.reset_index().waves
|
88 |
+
support_resistance_levels = calculate_support_resistance(df, rolling_wave_length, num_clusters)
|
89 |
+
|
90 |
+
#creating a plot
|
91 |
+
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
|
92 |
+
vertical_spacing=0.06, subplot_titles=('OHLC', 'Volume'),
|
93 |
+
row_width=[0.3, 0.7])
|
94 |
+
|
95 |
+
fig.add_trace(go.Candlestick(x=df.index,
|
96 |
+
open=df['Open'],
|
97 |
+
high=df['High'],
|
98 |
+
low=df['Low'],
|
99 |
+
close=df['Close'], name = "Market data"), row = 1, col = 1)
|
100 |
+
|
101 |
+
fig.update_xaxes(
|
102 |
+
rangeslider_visible = False,
|
103 |
+
rangeselector=dict(
|
104 |
+
buttons=list([
|
105 |
+
dict(count=1, label="1d",
|
106 |
+
step="day", stepmode="backward"),
|
107 |
+
dict(count=3, label="3d",
|
108 |
+
step="day", stepmode="backward"),
|
109 |
+
dict(count=7, label="7d",
|
110 |
+
step="day", stepmode="backward"),
|
111 |
+
dict(count=30, label="30d",
|
112 |
+
step="day", stepmode="backward"),
|
113 |
+
dict(step="all")])))
|
114 |
+
|
115 |
+
i = 0
|
116 |
+
for level in support_resistance_levels.to_list():
|
117 |
+
fig.add_hline(y=level, line_width=1,
|
118 |
+
line_dash="dash", row=1, col=1,
|
119 |
+
line_color="snow")
|
120 |
+
i += 1
|
121 |
+
|
122 |
+
colors = []
|
123 |
+
|
124 |
+
for i in range(len(df.Close)):
|
125 |
+
if i != 0:
|
126 |
+
if df.Close[i] > df.Close[i-1]:
|
127 |
+
colors.append('lightgreen')
|
128 |
+
else:
|
129 |
+
colors.append('lightcoral')
|
130 |
+
else:
|
131 |
+
colors.append('lightcoral')
|
132 |
+
|
133 |
+
fig.add_trace(go.Bar(x=df.index, y=df['Volume'], showlegend=False,
|
134 |
+
marker=dict(color=colors)), row=2, col=1)
|
135 |
+
|
136 |
+
fig.update_traces(name= 'Volume', selector=dict(type='bar'))
|
137 |
+
|
138 |
+
text = f'{ticker} Chart'
|
139 |
+
|
140 |
+
fig.update_layout(
|
141 |
+
title=go.layout.Title(
|
142 |
+
text=text,
|
143 |
+
xref="paper",
|
144 |
+
x=0))
|
145 |
+
|
146 |
+
#show chart
|
147 |
+
st.plotly_chart(fig, use_container_width=True)
|