Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -4,8 +4,8 @@ import yfinance as yf
|
|
4 |
from datetime import datetime, timedelta
|
5 |
import requests
|
6 |
from bs4 import BeautifulSoup
|
7 |
-
from concurrent.futures import ThreadPoolExecutor, as_completed
|
8 |
from pattern_finder import score_downward_trend, score_candle, calculate_risk_reward
|
|
|
9 |
import urllib3
|
10 |
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
11 |
|
@@ -29,7 +29,7 @@ def load_sp500_tickers():
|
|
29 |
def load_data(ticker):
|
30 |
"""Load stock data using yfinance."""
|
31 |
end_date = datetime.today()
|
32 |
-
start_date = end_date - timedelta(days=365)
|
33 |
data = yf.download(ticker, start=start_date, end=end_date)
|
34 |
return data
|
35 |
|
@@ -53,13 +53,6 @@ def average_downtrend(data, method, window=4):
|
|
53 |
return avg_diff if avg_diff < 0 else 0.0
|
54 |
|
55 |
|
56 |
-
def score_candle(candle, prev_candle, trend_strength):
|
57 |
-
"""Score a single candle based on its characteristics and previous candle."""
|
58 |
-
# [Keep the existing scoring logic]
|
59 |
-
# ...
|
60 |
-
return score
|
61 |
-
|
62 |
-
|
63 |
def score_today_candle(data, window=4):
|
64 |
"""Score today's candle based on the downtrend from the past 'window' days."""
|
65 |
if len(data) < window + 1:
|
@@ -70,6 +63,7 @@ def score_today_candle(data, window=4):
|
|
70 |
|
71 |
close_price = today_candle['Close']
|
72 |
previous_data = data.iloc[-(window+1):-1]
|
|
|
73 |
down_High = average_downtrend(previous_data, method="High", window=window) + average_downtrend(previous_data, method="High", window=7) / 2
|
74 |
down_Close = average_downtrend(previous_data, method="Close", window=window) + average_downtrend(previous_data, method="Close", window=7) / 2
|
75 |
avg_downtrend = (down_High + down_Close) / 2
|
@@ -82,45 +76,50 @@ def score_today_candle(data, window=4):
|
|
82 |
sma_20 = calculate_sma(data, window=20).iloc[-1]
|
83 |
ema_10 = calculate_ema(data, window=10).iloc[-1]
|
84 |
|
85 |
-
if (close_price < ema_10) or (close_price < sma_20) or
|
86 |
return -1
|
87 |
-
|
88 |
return score_candle(today_candle, prev_candle, abs(avg_downtrend))
|
89 |
|
90 |
|
91 |
-
def scan_ticker(ticker):
|
92 |
-
"""Load data for a ticker and calculate its score."""
|
93 |
-
data = load_data(ticker)
|
94 |
-
if not data.empty:
|
95 |
-
score = score_today_candle(data)
|
96 |
-
if score > 0:
|
97 |
-
return ticker, score
|
98 |
-
return None
|
99 |
-
|
100 |
-
|
101 |
def scan_sp500(top_n=25, progress=gr.Progress()):
|
102 |
tickers = load_sp500_tickers()
|
|
|
103 |
tickers.append("QQQ")
|
104 |
|
105 |
-
scores = []
|
106 |
with ThreadPoolExecutor(max_workers=10) as executor:
|
107 |
-
futures = {executor.submit(
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
scores = sorted(scores, key=lambda x: x[1], reverse=True)
|
115 |
return scores[:top_n]
|
116 |
|
117 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
def gradio_scan_sp500(top_n, progress=gr.Progress()):
|
119 |
-
progress(0, desc="
|
120 |
tickers = load_sp500_tickers()
|
121 |
tickers.append("QQQ")
|
122 |
|
123 |
-
progress(0.
|
124 |
results = scan_sp500(top_n, progress)
|
125 |
|
126 |
last_data = load_data(results[0][0])
|
@@ -129,11 +128,12 @@ def gradio_scan_sp500(top_n, progress=gr.Progress()):
|
|
129 |
date_created = next_market_day.strftime("%Y-%m-%d")
|
130 |
|
131 |
output = f"Scan Results for Market Open on: {date_created}\n\n"
|
132 |
-
output += "Top {} stocks based on pattern finder score:\n\n"
|
133 |
for ticker, score in results:
|
134 |
-
output += "{}: Total Score = {:.2f}\n"
|
135 |
return output
|
136 |
|
|
|
137 |
iface = gr.Interface(
|
138 |
fn=gradio_scan_sp500,
|
139 |
inputs=gr.Slider(minimum=1, maximum=100, step=1, label="Number of top stocks to display", value=25),
|
|
|
4 |
from datetime import datetime, timedelta
|
5 |
import requests
|
6 |
from bs4 import BeautifulSoup
|
|
|
7 |
from pattern_finder import score_downward_trend, score_candle, calculate_risk_reward
|
8 |
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
9 |
import urllib3
|
10 |
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
11 |
|
|
|
29 |
def load_data(ticker):
|
30 |
"""Load stock data using yfinance."""
|
31 |
end_date = datetime.today()
|
32 |
+
start_date = end_date - timedelta(days=365)
|
33 |
data = yf.download(ticker, start=start_date, end=end_date)
|
34 |
return data
|
35 |
|
|
|
53 |
return avg_diff if avg_diff < 0 else 0.0
|
54 |
|
55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
def score_today_candle(data, window=4):
|
57 |
"""Score today's candle based on the downtrend from the past 'window' days."""
|
58 |
if len(data) < window + 1:
|
|
|
63 |
|
64 |
close_price = today_candle['Close']
|
65 |
previous_data = data.iloc[-(window+1):-1]
|
66 |
+
|
67 |
down_High = average_downtrend(previous_data, method="High", window=window) + average_downtrend(previous_data, method="High", window=7) / 2
|
68 |
down_Close = average_downtrend(previous_data, method="Close", window=window) + average_downtrend(previous_data, method="Close", window=7) / 2
|
69 |
avg_downtrend = (down_High + down_Close) / 2
|
|
|
76 |
sma_20 = calculate_sma(data, window=20).iloc[-1]
|
77 |
ema_10 = calculate_ema(data, window=10).iloc[-1]
|
78 |
|
79 |
+
if (close_price < ema_10) or (close_price < sma_20) or (close_price < sma_50) or (close_price < sma_200):
|
80 |
return -1
|
81 |
+
|
82 |
return score_candle(today_candle, prev_candle, abs(avg_downtrend))
|
83 |
|
84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
def scan_sp500(top_n=25, progress=gr.Progress()):
|
86 |
tickers = load_sp500_tickers()
|
87 |
+
scores = []
|
88 |
tickers.append("QQQ")
|
89 |
|
|
|
90 |
with ThreadPoolExecutor(max_workers=10) as executor:
|
91 |
+
futures = {executor.submit(load_data, ticker): ticker for ticker in tickers}
|
92 |
+
total_tickers = len(futures)
|
93 |
+
|
94 |
+
for i, future in enumerate(as_completed(futures)):
|
95 |
+
ticker = futures[future]
|
96 |
+
data = future.result()
|
97 |
+
|
98 |
+
if not data.empty:
|
99 |
+
score = score_today_candle(data)
|
100 |
+
if score > 0:
|
101 |
+
scores.append((ticker, score))
|
102 |
+
|
103 |
+
# Update progress after each ticker is processed
|
104 |
+
progress(i / total_tickers, desc=f"Processing {ticker}")
|
105 |
|
106 |
scores = sorted(scores, key=lambda x: x[1], reverse=True)
|
107 |
return scores[:top_n]
|
108 |
|
109 |
|
110 |
+
def next_business_day(date):
|
111 |
+
next_day = date + timedelta(days=1)
|
112 |
+
while next_day.weekday() >= 5: # 5 = Saturday, 6 = Sunday
|
113 |
+
next_day += timedelta(days=1)
|
114 |
+
return next_day
|
115 |
+
|
116 |
+
|
117 |
def gradio_scan_sp500(top_n, progress=gr.Progress()):
|
118 |
+
progress(0, desc="Loading S&P 500 Tickers")
|
119 |
tickers = load_sp500_tickers()
|
120 |
tickers.append("QQQ")
|
121 |
|
122 |
+
progress(0.2, desc="Running Stock Scanning")
|
123 |
results = scan_sp500(top_n, progress)
|
124 |
|
125 |
last_data = load_data(results[0][0])
|
|
|
128 |
date_created = next_market_day.strftime("%Y-%m-%d")
|
129 |
|
130 |
output = f"Scan Results for Market Open on: {date_created}\n\n"
|
131 |
+
output += f"Top {top_n} stocks based on pattern finder score:\n\n"
|
132 |
for ticker, score in results:
|
133 |
+
output += f"{ticker}: Total Score = {score:.2f}\n"
|
134 |
return output
|
135 |
|
136 |
+
|
137 |
iface = gr.Interface(
|
138 |
fn=gradio_scan_sp500,
|
139 |
inputs=gr.Slider(minimum=1, maximum=100, step=1, label="Number of top stocks to display", value=25),
|