Update app.py
Browse files
app.py
CHANGED
@@ -1,23 +1,219 @@
|
|
1 |
-
import
|
|
|
2 |
import yfinance as yf
|
3 |
-
|
4 |
|
5 |
-
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
|
9 |
-
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
-
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
-
|
16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
-
|
19 |
-
st.write("## Result:")
|
20 |
-
st.write(result)
|
21 |
|
22 |
if __name__ == "__main__":
|
23 |
main()
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import numpy as np
|
3 |
import yfinance as yf
|
4 |
+
import streamlit as st
|
5 |
|
6 |
+
# Function to fetch historical stock data using yfinance
|
7 |
+
def fetch_stock_data(ticker, start_date, end_date):
|
8 |
+
stock_data = yf.download(ticker, start=start_date, end=end_date)
|
9 |
+
return stock_data
|
10 |
+
|
11 |
+
# Function to detect head and shoulder patterns
|
12 |
+
def detect_head_shoulder(df, window=3):
|
13 |
+
roll_window = window
|
14 |
+
df['high_roll_max'] = df['High'].rolling(window=roll_window).max()
|
15 |
+
df['low_roll_min'] = df['Low'].rolling(window=roll_window).min()
|
16 |
+
mask_head_shoulder = (
|
17 |
+
(df['high_roll_max'] > df['High'].shift(1)) &
|
18 |
+
(df['high_roll_max'] > df['High'].shift(-1)) &
|
19 |
+
(df['High'] < df['High'].shift(1)) &
|
20 |
+
(df['High'] < df['High'].shift(-1))
|
21 |
+
)
|
22 |
+
mask_inv_head_shoulder = (
|
23 |
+
(df['low_roll_min'] < df['Low'].shift(1)) &
|
24 |
+
(df['low_roll_min'] < df['Low'].shift(-1)) &
|
25 |
+
(df['Low'] > df['Low'].shift(1)) &
|
26 |
+
(df['Low'] > df['Low'].shift(-1))
|
27 |
+
)
|
28 |
+
df['head_shoulder_pattern'] = np.nan
|
29 |
+
df.loc[mask_head_shoulder, 'head_shoulder_pattern'] = 'Head and Shoulder'
|
30 |
+
df.loc[mask_inv_head_shoulder, 'head_shoulder_pattern'] = 'Inverse Head and Shoulder'
|
31 |
+
return df
|
32 |
+
|
33 |
+
# Function to detect multiple tops and bottoms
|
34 |
+
def detect_multiple_tops_bottoms(df, window=3):
|
35 |
+
roll_window = window
|
36 |
+
df['high_roll_max'] = df['High'].rolling(window=roll_window).max()
|
37 |
+
df['low_roll_min'] = df['Low'].rolling(window=roll_window).min()
|
38 |
+
df['close_roll_max'] = df['Close'].rolling(window=roll_window).max()
|
39 |
+
df['close_roll_min'] = df['Close'].rolling(window=roll_window).min()
|
40 |
+
mask_top = (df['high_roll_max'] >= df['High'].shift(1)) & (df['close_roll_max'] < df['Close'].shift(1))
|
41 |
+
mask_bottom = (df['low_roll_min'] <= df['Low'].shift(1)) & (df['close_roll_min'] > df['Close'].shift(1))
|
42 |
+
df['multiple_top_bottom_pattern'] = np.nan
|
43 |
+
df.loc[mask_top, 'multiple_top_bottom_pattern'] = 'Multiple Top'
|
44 |
+
df.loc[mask_bottom, 'multiple_top_bottom_pattern'] = 'Multiple Bottom'
|
45 |
+
return df
|
46 |
+
|
47 |
+
# Function to calculate support and resistance levels
|
48 |
+
def calculate_support_resistance(df, window=3):
|
49 |
+
roll_window = window
|
50 |
+
std_dev = 2
|
51 |
+
df['high_roll_max'] = df['High'].rolling(window=roll_window).max()
|
52 |
+
df['low_roll_min'] = df['Low'].rolling(window=roll_window).min()
|
53 |
+
mean_high = df['High'].rolling(window=roll_window).mean()
|
54 |
+
std_high = df['High'].rolling(window=roll_window).std()
|
55 |
+
mean_low = df['Low'].rolling(window=roll_window).mean()
|
56 |
+
std_low = df['Low'].rolling(window=roll_window).std()
|
57 |
+
df['support'] = mean_low - std_dev * std_low
|
58 |
+
df['resistance'] = mean_high + std_dev * std_high
|
59 |
+
return df
|
60 |
+
|
61 |
+
# Function to detect triangle patterns
|
62 |
+
def detect_triangle_pattern(df, window=3):
|
63 |
+
roll_window = window
|
64 |
+
df['high_roll_max'] = df['High'].rolling(window=roll_window).max()
|
65 |
+
df['low_roll_min'] = df['Low'].rolling(window=roll_window).min()
|
66 |
+
mask_asc = (
|
67 |
+
(df['high_roll_max'] >= df['High'].shift(1)) &
|
68 |
+
(df['low_roll_min'] <= df['Low'].shift(1)) &
|
69 |
+
(df['Close'] > df['Close'].shift(1))
|
70 |
+
)
|
71 |
+
mask_desc = (
|
72 |
+
(df['high_roll_max'] <= df['High'].shift(1)) &
|
73 |
+
(df['low_roll_min'] >= df['Low'].shift(1)) &
|
74 |
+
(df['Close'] < df['Close'].shift(1))
|
75 |
+
)
|
76 |
+
df['triangle_pattern'] = np.nan
|
77 |
+
df.loc[mask_asc, 'triangle_pattern'] = 'Ascending Triangle'
|
78 |
+
df.loc[mask_desc, 'triangle_pattern'] = 'Descending Triangle'
|
79 |
+
return df
|
80 |
+
|
81 |
+
# Function to detect wedge patterns
|
82 |
+
def detect_wedge(df, window=3):
|
83 |
+
roll_window = window
|
84 |
+
df['high_roll_max'] = df['High'].rolling(window=roll_window).max()
|
85 |
+
df['low_roll_min'] = df['Low'].rolling(window=roll_window).min()
|
86 |
+
df['trend_high'] = df['High'].rolling(window=roll_window).apply(lambda x: 1 if (x[-1]-x[0]) > 0 else -1 if (x[-1]-x[0]) < 0 else 0)
|
87 |
+
df['trend_low'] = df['Low'].rolling(window=roll_window).apply(lambda x: 1 if (x[-1]-x[0]) > 0 else -1 if (x[-1]-x[0]) < 0 else 0)
|
88 |
+
mask_wedge_up = (
|
89 |
+
(df['high_roll_max'] >= df['High'].shift(1)) &
|
90 |
+
(df['low_roll_min'] <= df['Low'].shift(1)) &
|
91 |
+
(df['trend_high'] == 1) &
|
92 |
+
(df['trend_low'] == 1)
|
93 |
+
)
|
94 |
+
mask_wedge_down = (
|
95 |
+
(df['high_roll_max'] <= df['High'].shift(1)) &
|
96 |
+
(df['low_roll_min'] >= df['Low'].shift(1)) &
|
97 |
+
(df['trend_high'] == -1) &
|
98 |
+
(df['trend_low'] == -1)
|
99 |
+
)
|
100 |
+
df['wedge_pattern'] = np.nan
|
101 |
+
df.loc[mask_wedge_up, 'wedge_pattern'] = 'Wedge Up'
|
102 |
+
df.loc[mask_wedge_down, 'wedge_pattern'] = 'Wedge Down'
|
103 |
+
return df
|
104 |
|
105 |
+
# Function to detect channel patterns
|
106 |
+
def detect_channel(df, window=3):
|
107 |
+
roll_window = window
|
108 |
+
channel_range = 0.1
|
109 |
+
df['high_roll_max'] = df['High'].rolling(window=roll_window).max()
|
110 |
+
df['low_roll_min'] = df['Low'].rolling(window=roll_window).min()
|
111 |
+
df['trend_high'] = df['High'].rolling(window=roll_window).apply(lambda x: 1 if (x[-1]-x[0]) > 0 else -1 if (x[-1]-x[0]) < 0 else 0)
|
112 |
+
df['trend_low'] = df['Low'].rolling(window=roll_window).apply(lambda x: 1 if (x[-1]-x[0]) > 0 else -1 if (x[-1]-x[0]) <0 else 0)
|
113 |
+
mask_channel_up = (
|
114 |
+
(df['high_roll_max'] >= df['High'].shift(1)) &
|
115 |
+
(df['low_roll_min'] <= df['Low'].shift(1)) &
|
116 |
+
(df['high_roll_max'] - df['low_roll_min'] <= channel_range * (df['high_roll_max'] + df['low_roll_min'])/2) &
|
117 |
+
(df['trend_high'] == 1) &
|
118 |
+
(df['trend_low'] == 1)
|
119 |
+
)
|
120 |
+
mask_channel_down = (
|
121 |
+
(df['high_roll_max'] <= df['High'].shift(1)) &
|
122 |
+
(df['low_roll_min'] >= df['Low'].shift(1)) &
|
123 |
+
(df['high_roll_max'] - df['low_roll_min'] <= channel_range * (df['high_roll_max'] + df['low_roll_min'])/2) &
|
124 |
+
(df['trend_high'] == -1) &
|
125 |
+
(df['trend_low'] == -1)
|
126 |
+
)
|
127 |
+
df['channel_pattern'] = np.nan
|
128 |
+
df.loc[mask_channel_up, 'channel_pattern'] = 'Channel Up'
|
129 |
+
df.loc[mask_channel_down, 'channel_pattern'] = 'Channel Down'
|
130 |
+
return df
|
131 |
|
132 |
+
# Function to detect double top and bottom patterns
|
133 |
+
def detect_double_top_bottom(df, window=3, threshold=0.05):
|
134 |
+
roll_window = window
|
135 |
+
range_threshold = threshold
|
136 |
+
df['high_roll_max'] = df['High'].rolling(window=roll_window).max()
|
137 |
+
df['low_roll_min'] = df['Low'].rolling(window=roll_window).min()
|
138 |
+
mask_double_top = (
|
139 |
+
(df['high_roll_max'] >= df['High'].shift(1)) &
|
140 |
+
(df['high_roll_max'] >= df['High'].shift(-1)) &
|
141 |
+
(df['High'] < df['High'].shift(1)) &
|
142 |
+
(df['High'] < df['High'].shift(-1)) &
|
143 |
+
((df['High'].shift(1) - df['Low'].shift(1)) <= range_threshold * (df['High'].shift(1) + df['Low'].shift(1))/2) &
|
144 |
+
((df['High'].shift(-1) - df['Low'].shift(-1)) <= range_threshold * (df['High'].shift(-1) + df['Low'].shift(-1))/2)
|
145 |
+
)
|
146 |
+
mask_double_bottom = (
|
147 |
+
(df['low_roll_min'] <= df['Low'].shift(1)) &
|
148 |
+
(df['low_roll_min'] <= df['Low'].shift(-1)) &
|
149 |
+
(df['Low'] > df['Low'].shift(1)) &
|
150 |
+
(df['Low'] > df['Low'].shift(-1)) &
|
151 |
+
((df['High'].shift(1) - df['Low'].shift(1)) <= range_threshold * (df['High'].shift(1) + df['Low'].shift(1))/2) &
|
152 |
+
((df['High'].shift(-1) - df['Low'].shift(-1)) <= range_threshold * (df['High'].shift(-1) + df['Low'].shift(-1))/2)
|
153 |
+
)
|
154 |
+
df['double_pattern'] = np.nan
|
155 |
+
df.loc[mask_double_top, 'double_pattern'] = 'Double Top'
|
156 |
+
df.loc[mask_double_bottom, 'double_pattern'] = 'Double Bottom'
|
157 |
+
return df
|
158 |
+
|
159 |
+
# Function to detect trendlines
|
160 |
+
def detect_trendline(df, window=2):
|
161 |
+
roll_window = window
|
162 |
+
df['slope'] = np.nan
|
163 |
+
df['intercept'] = np.nan
|
164 |
+
|
165 |
+
for i in range(window, len(df)):
|
166 |
+
x = np.array(range(i-window, i))
|
167 |
+
y = df['Close'][i-window:i]
|
168 |
+
A = np.vstack([x, np.ones(len(x))]).T
|
169 |
+
m, c = np.linalg.lstsq(A, y, rcond=None)[0]
|
170 |
+
df.at[df.index[i], 'slope'] = m
|
171 |
+
df.at[df.index[i], 'intercept'] = c
|
172 |
+
|
173 |
+
mask_support = df['slope'] > 0
|
174 |
+
mask_resistance = df['slope'] < 0
|
175 |
+
df['support'] = np.nan
|
176 |
+
df['resistance'] = np.nan
|
177 |
+
df.loc[mask_support, 'support'] = df['Close'] * df['slope'] + df['intercept']
|
178 |
+
df.loc[mask_resistance, 'resistance'] = df['Close'] * df['slope'] + df['intercept']
|
179 |
+
|
180 |
+
return df
|
181 |
+
|
182 |
+
# Function to find pivots
|
183 |
+
def find_pivots(df):
|
184 |
+
high_diffs = df['High'].diff()
|
185 |
+
low_diffs = df['Low'].diff()
|
186 |
+
higher_high_mask = (high_diffs > 0) & (high_diffs.shift(-1) < 0)
|
187 |
+
lower_low_mask = (low_diffs < 0) & (low_diffs.shift(-1) > 0)
|
188 |
+
lower_high_mask = (high_diffs < 0) & (high_diffs.shift(-1) > 0)
|
189 |
+
higher_low_mask = (low_diffs > 0) & (low_diffs.shift(-1) < 0)
|
190 |
+
df['signal'] = ''
|
191 |
+
df.loc[higher_high_mask, 'signal'] = 'HH'
|
192 |
+
df.loc[lower_low_mask, 'signal'] = 'LL'
|
193 |
+
df.loc[lower_high_mask, 'signal'] = 'LH'
|
194 |
+
df.loc[higher_low_mask, 'signal'] = 'HL'
|
195 |
+
return df
|
196 |
+
|
197 |
+
# Streamlit App
|
198 |
+
def main():
|
199 |
+
st.title('Stock Pattern Detection App')
|
200 |
+
ticker = st.text_input('Enter Stock Ticker:', 'AAPL')
|
201 |
+
start_date = st.date_input('Start Date', pd.to_datetime('2020-01-01'))
|
202 |
+
end_date = st.date_input('End Date', pd.to_datetime('2022-01-01'))
|
203 |
|
204 |
+
if st.button('Detect Patterns'):
|
205 |
+
stock_data = fetch_stock_data(ticker, start_date, end_date)
|
206 |
+
stock_data = detect_head_shoulder(stock_data)
|
207 |
+
stock_data = detect_multiple_tops_bottoms(stock_data)
|
208 |
+
stock_data = calculate_support_resistance(stock_data)
|
209 |
+
stock_data = detect_triangle_pattern(stock_data)
|
210 |
+
stock_data = detect_wedge(stock_data)
|
211 |
+
stock_data = detect_channel(stock_data)
|
212 |
+
stock_data = detect_double_top_bottom(stock_data)
|
213 |
+
stock_data = detect_trendline(stock_data)
|
214 |
+
stock_data = find_pivots(stock_data)
|
215 |
|
216 |
+
st.write(stock_data)
|
|
|
|
|
217 |
|
218 |
if __name__ == "__main__":
|
219 |
main()
|