Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,21 +1,346 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import yfinance as yf
|
3 |
+
import pandas as pd
|
4 |
+
import numpy as np
|
5 |
+
from datetime import datetime, timedelta
|
6 |
+
import feedparser
|
7 |
+
from textblob import TextBlob
|
8 |
+
from statsmodels.tsa.holtwinters import ExponentialSmoothing
|
9 |
+
|
10 |
+
# Function to fetch cryptocurrency data
|
11 |
+
def get_crypto_data(symbol, period="30d", interval="1h"):
|
12 |
+
crypto = yf.Ticker(f"{symbol}-USD")
|
13 |
+
data = crypto.history(period=period, interval=interval)
|
14 |
+
return data
|
15 |
+
|
16 |
+
# Function to calculate RSI
|
17 |
+
def calculate_rsi(data, period=14):
|
18 |
+
delta = data['Close'].diff()
|
19 |
+
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
|
20 |
+
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
|
21 |
+
rs = gain / loss
|
22 |
+
rsi = 100 - (100 / (1 + rs))
|
23 |
+
return rsi
|
24 |
+
|
25 |
+
# Function to calculate MACD
|
26 |
+
def calculate_macd(data, short_window=12, long_window=26, signal_window=9):
|
27 |
+
short_ema = data['Close'].ewm(span=short_window, adjust=False).mean()
|
28 |
+
long_ema = data['Close'].ewm(span=long_window, adjust=False).mean()
|
29 |
+
macd = short_ema - long_ema
|
30 |
+
signal = macd.ewm(span=signal_window, adjust=False).mean()
|
31 |
+
return macd, signal
|
32 |
+
|
33 |
+
# Function to calculate EMA
|
34 |
+
def calculate_ema(data, period=20):
|
35 |
+
return data['Close'].ewm(span=period, adjust=False).mean()
|
36 |
+
|
37 |
+
# Function to calculate ATR
|
38 |
+
def calculate_atr(data, period=14):
|
39 |
+
high_low = data['High'] - data['Low']
|
40 |
+
high_close = np.abs(data['High'] - data['Close'].shift())
|
41 |
+
low_close = np.abs(data['Low'] - data['Close'].shift())
|
42 |
+
true_range = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
|
43 |
+
atr = true_range.rolling(window=period).mean()
|
44 |
+
return atr
|
45 |
+
|
46 |
+
# Function to calculate Stochastic Oscillator
|
47 |
+
def calculate_stochastic(data, period=14):
|
48 |
+
high = data['High'].rolling(window=period).max()
|
49 |
+
low = data['Low'].rolling(window=period).min()
|
50 |
+
stoch_k = 100 * ((data['Close'] - low) / (high - low))
|
51 |
+
stoch_d = stoch_k.rolling(window=3).mean()
|
52 |
+
return stoch_k, stoch_d
|
53 |
+
|
54 |
+
# Function to calculate Bollinger Bands
|
55 |
+
def calculate_bollinger_bands(data, period=20, std_dev=2):
|
56 |
+
sma = data['Close'].rolling(window=period).mean()
|
57 |
+
std = data['Close'].rolling(window=period).std()
|
58 |
+
upper_band = sma + (std * std_dev)
|
59 |
+
lower_band = sma - (std * std_dev)
|
60 |
+
return upper_band, lower_band
|
61 |
+
|
62 |
+
# Function to calculate On-Balance Volume (OBV)
|
63 |
+
def calculate_obv(data):
|
64 |
+
obv = (np.sign(data['Close'].diff()) * data['Volume']).cumsum()
|
65 |
+
return obv
|
66 |
+
|
67 |
+
# Function to calculate Average Directional Index (ADX)
|
68 |
+
def calculate_adx(data, period=14):
|
69 |
+
high = data['High']
|
70 |
+
low = data['Low']
|
71 |
+
close = data['Close']
|
72 |
+
|
73 |
+
# Calculate +DM and -DM
|
74 |
+
plus_dm = high.diff()
|
75 |
+
minus_dm = -low.diff()
|
76 |
+
plus_dm[plus_dm < 0] = 0
|
77 |
+
minus_dm[minus_dm < 0] = 0
|
78 |
+
|
79 |
+
# Calculate True Range (TR)
|
80 |
+
tr = pd.concat([high - low, abs(high - close.shift()), abs(low - close.shift())], axis=1).max(axis=1)
|
81 |
+
|
82 |
+
# Calculate +DI and -DI
|
83 |
+
plus_di = 100 * (plus_dm.ewm(alpha=1/period).mean() / tr.ewm(alpha=1/period).mean())
|
84 |
+
minus_di = 100 * (minus_dm.ewm(alpha=1/period).mean() / tr.ewm(alpha=1/period).mean())
|
85 |
+
|
86 |
+
# Calculate ADX
|
87 |
+
dx = 100 * abs(plus_di - minus_di) / (plus_di + minus_di)
|
88 |
+
adx = dx.ewm(alpha=1/period).mean()
|
89 |
+
return adx
|
90 |
+
|
91 |
+
# Function to calculate Fibonacci Retracement levels
|
92 |
+
def calculate_fibonacci_levels(data):
|
93 |
+
high = data['High'].max()
|
94 |
+
low = data['Low'].min()
|
95 |
+
diff = high - low
|
96 |
+
return {
|
97 |
+
"23.6%": high - diff * 0.236,
|
98 |
+
"38.2%": high - diff * 0.382,
|
99 |
+
"50%": high - diff * 0.5,
|
100 |
+
"61.8%": high - diff * 0.618,
|
101 |
+
"78.6%": high - diff * 0.786,
|
102 |
+
}
|
103 |
+
|
104 |
+
# Function to calculate probabilities for the next 12 hours
|
105 |
+
def calculate_probabilities(data):
|
106 |
+
# Calculate indicators on the entire dataset
|
107 |
+
data['RSI'] = calculate_rsi(data)
|
108 |
+
data['MACD'], data['MACD_Signal'] = calculate_macd(data)
|
109 |
+
data['EMA_50'] = calculate_ema(data, period=50)
|
110 |
+
data['EMA_200'] = calculate_ema(data, period=200)
|
111 |
+
data['ATR'] = calculate_atr(data)
|
112 |
+
data['Stoch_K'], data['Stoch_D'] = calculate_stochastic(data)
|
113 |
+
data['Upper_Band'], data['Lower_Band'] = calculate_bollinger_bands(data)
|
114 |
+
data['OBV'] = calculate_obv(data)
|
115 |
+
data['ADX'] = calculate_adx(data)
|
116 |
+
|
117 |
+
# Use the most recent values for predictions
|
118 |
+
recent_data = data.iloc[-1]
|
119 |
+
|
120 |
+
# Calculate probabilities based on recent data
|
121 |
+
probabilities = {
|
122 |
+
"RSI": {"Pump": 0, "Dump": 0},
|
123 |
+
"MACD": {"Pump": 0, "Dump": 0},
|
124 |
+
"EMA": {"Pump": 0, "Dump": 0},
|
125 |
+
"ATR": {"Pump": 0, "Dump": 0},
|
126 |
+
"Stochastic": {"Pump": 0, "Dump": 0},
|
127 |
+
"Bollinger Bands": {"Pump": 0, "Dump": 0},
|
128 |
+
"OBV": {"Pump": 0, "Dump": 0},
|
129 |
+
"ADX": {"Pump": 0, "Dump": 0},
|
130 |
+
}
|
131 |
+
|
132 |
+
# RSI
|
133 |
+
rsi = recent_data['RSI']
|
134 |
+
if rsi < 25:
|
135 |
+
probabilities["RSI"]["Pump"] = 90 # Strong Pump
|
136 |
+
elif 25 <= rsi < 30:
|
137 |
+
probabilities["RSI"]["Pump"] = 60 # Moderate Pump
|
138 |
+
elif 70 < rsi <= 75:
|
139 |
+
probabilities["RSI"]["Dump"] = 60 # Moderate Dump
|
140 |
+
elif rsi > 75:
|
141 |
+
probabilities["RSI"]["Dump"] = 90 # Strong Dump
|
142 |
+
|
143 |
+
# MACD
|
144 |
+
macd = recent_data['MACD']
|
145 |
+
macd_signal = recent_data['MACD_Signal']
|
146 |
+
if macd > macd_signal and macd > 0:
|
147 |
+
probabilities["MACD"]["Pump"] = 90 # Strong Pump
|
148 |
+
elif macd > macd_signal and macd <= 0:
|
149 |
+
probabilities["MACD"]["Pump"] = 60 # Moderate Pump
|
150 |
+
elif macd < macd_signal and macd >= 0:
|
151 |
+
probabilities["MACD"]["Dump"] = 60 # Moderate Dump
|
152 |
+
elif macd < macd_signal and macd < 0:
|
153 |
+
probabilities["MACD"]["Dump"] = 90 # Strong Dump
|
154 |
+
|
155 |
+
# EMA
|
156 |
+
ema_short = recent_data['EMA_50']
|
157 |
+
ema_long = recent_data['EMA_200']
|
158 |
+
close = recent_data['Close']
|
159 |
+
if ema_short > ema_long and close > ema_short:
|
160 |
+
probabilities["EMA"]["Pump"] = 90 # Strong Pump
|
161 |
+
elif ema_short > ema_long and close <= ema_short:
|
162 |
+
probabilities["EMA"]["Pump"] = 60 # Moderate Pump
|
163 |
+
elif ema_short < ema_long and close >= ema_short:
|
164 |
+
probabilities["EMA"]["Dump"] = 60 # Moderate Dump
|
165 |
+
elif ema_short < ema_long and close < ema_short:
|
166 |
+
probabilities["EMA"]["Dump"] = 90 # Strong Dump
|
167 |
+
|
168 |
+
# ATR
|
169 |
+
atr = recent_data['ATR']
|
170 |
+
if atr > 100:
|
171 |
+
probabilities["ATR"]["Pump"] = 90 # Strong Pump
|
172 |
+
elif 50 < atr <= 100:
|
173 |
+
probabilities["ATR"]["Pump"] = 60 # Moderate Pump
|
174 |
+
elif -100 <= atr < -50:
|
175 |
+
probabilities["ATR"]["Dump"] = 60 # Moderate Dump
|
176 |
+
elif atr < -100:
|
177 |
+
probabilities["ATR"]["Dump"] = 90 # Strong Dump
|
178 |
+
|
179 |
+
# Stochastic Oscillator
|
180 |
+
stoch_k = recent_data['Stoch_K']
|
181 |
+
stoch_d = recent_data['Stoch_D']
|
182 |
+
if stoch_k < 20 and stoch_d < 20:
|
183 |
+
probabilities["Stochastic"]["Pump"] = 90 # Strong Pump
|
184 |
+
elif 20 <= stoch_k < 30 and 20 <= stoch_d < 30:
|
185 |
+
probabilities["Stochastic"]["Pump"] = 60 # Moderate Pump
|
186 |
+
elif 70 < stoch_k <= 80 and 70 < stoch_d <= 80:
|
187 |
+
probabilities["Stochastic"]["Dump"] = 60 # Moderate Dump
|
188 |
+
elif stoch_k > 80 and stoch_d > 80:
|
189 |
+
probabilities["Stochastic"]["Dump"] = 90 # Strong Dump
|
190 |
+
|
191 |
+
# Bollinger Bands
|
192 |
+
close = recent_data['Close']
|
193 |
+
upper_band = recent_data['Upper_Band']
|
194 |
+
lower_band = recent_data['Lower_Band']
|
195 |
+
if close <= lower_band:
|
196 |
+
probabilities["Bollinger Bands"]["Pump"] = 90 # Strong Pump
|
197 |
+
elif lower_band < close <= lower_band * 1.05:
|
198 |
+
probabilities["Bollinger Bands"]["Pump"] = 60 # Moderate Pump
|
199 |
+
elif upper_band * 0.95 <= close < upper_band:
|
200 |
+
probabilities["Bollinger Bands"]["Dump"] = 60 # Moderate Dump
|
201 |
+
elif close >= upper_band:
|
202 |
+
probabilities["Bollinger Bands"]["Dump"] = 90 # Strong Dump
|
203 |
+
|
204 |
+
# OBV
|
205 |
+
obv = recent_data['OBV']
|
206 |
+
if obv > 100000:
|
207 |
+
probabilities["OBV"]["Pump"] = 90 # Strong Pump
|
208 |
+
elif 50000 < obv <= 100000:
|
209 |
+
probabilities["OBV"]["Pump"] = 60 # Moderate Pump
|
210 |
+
elif -100000 <= obv < -50000:
|
211 |
+
probabilities["OBV"]["Dump"] = 60 # Moderate Dump
|
212 |
+
elif obv < -100000:
|
213 |
+
probabilities["OBV"]["Dump"] = 90 # Strong Dump
|
214 |
+
|
215 |
+
# ADX
|
216 |
+
adx = recent_data['ADX']
|
217 |
+
if adx > 25:
|
218 |
+
probabilities["ADX"]["Pump"] = 90 # Strong Pump
|
219 |
+
elif 20 < adx <= 25:
|
220 |
+
probabilities["ADX"]["Pump"] = 60 # Moderate Pump
|
221 |
+
elif 15 < adx <= 20:
|
222 |
+
probabilities["ADX"]["Dump"] = 60 # Moderate Dump
|
223 |
+
elif adx <= 15:
|
224 |
+
probabilities["ADX"]["Dump"] = 90 # Strong Dump
|
225 |
+
|
226 |
+
return probabilities, recent_data
|
227 |
+
|
228 |
+
# Function to predict future prices using Exponential Smoothing
|
229 |
+
def predict_price(data, days=7):
|
230 |
+
try:
|
231 |
+
# Prepare data for Exponential Smoothing
|
232 |
+
df = data[['Close']]
|
233 |
+
|
234 |
+
# Train the model
|
235 |
+
model = ExponentialSmoothing(df, trend="add", seasonal="add", seasonal_periods=7)
|
236 |
+
fit = model.fit()
|
237 |
+
|
238 |
+
# Make future predictions
|
239 |
+
forecast = fit.forecast(steps=days)
|
240 |
+
|
241 |
+
# Format predictions with dates
|
242 |
+
last_date = data.index[-1]
|
243 |
+
dates = pd.date_range(start=last_date + timedelta(days=1), periods=days)
|
244 |
+
forecast_df = pd.DataFrame({"Date": dates, "Price": forecast})
|
245 |
+
forecast_df["Date"] = forecast_df["Date"].dt.strftime("%B %d") # Format as "Month Day"
|
246 |
+
forecast_df["Price"] = forecast_df["Price"].round(2)
|
247 |
+
|
248 |
+
return forecast_df
|
249 |
+
except Exception as e:
|
250 |
+
return f"Error predicting prices: {e}"
|
251 |
+
|
252 |
+
# Function to fetch news from top 5 crypto news sites and perform sentiment analysis
|
253 |
+
def fetch_crypto_news(symbol):
|
254 |
+
try:
|
255 |
+
# List of RSS feeds for top 5 crypto news sites
|
256 |
+
rss_feeds = [
|
257 |
+
"https://coindesk.com/feed/",
|
258 |
+
"https://cointelegraph.com/rss",
|
259 |
+
"https://cryptoslate.com/feed/",
|
260 |
+
"https://www.newsbtc.com/feed/",
|
261 |
+
"https://news.bitcoin.com/feed/"
|
262 |
+
]
|
263 |
+
|
264 |
+
news_items = []
|
265 |
+
for feed_url in rss_feeds:
|
266 |
+
feed = feedparser.parse(feed_url)
|
267 |
+
for entry in feed.entries[:5]: # Limit to 5 articles per site
|
268 |
+
if symbol.lower() in entry.title.lower() or symbol.lower() in entry.summary.lower():
|
269 |
+
# Perform sentiment analysis on the article title
|
270 |
+
analysis = TextBlob(entry.title)
|
271 |
+
sentiment = "Bullish" if analysis.sentiment.polarity > 0 else "Bearish" if analysis.sentiment.polarity < 0 else "Neutral"
|
272 |
+
|
273 |
+
# Format the date as "Month Day"
|
274 |
+
published_date = datetime.strptime(entry.published, "%a, %d %b %Y %H:%M:%S %z").strftime("%B %d")
|
275 |
+
|
276 |
+
news_items.append({
|
277 |
+
"title": entry.title,
|
278 |
+
"link": entry.link,
|
279 |
+
"sentiment": sentiment,
|
280 |
+
"published": published_date,
|
281 |
+
})
|
282 |
+
return news_items[:5] # Return top 5 articles
|
283 |
+
except Exception as e:
|
284 |
+
return f"Error fetching crypto news: {e}"
|
285 |
+
|
286 |
+
# Gradio Interface
|
287 |
+
def crypto_app(symbol):
|
288 |
+
if symbol:
|
289 |
+
# Fetch data
|
290 |
+
data = get_crypto_data(symbol)
|
291 |
+
if data.empty:
|
292 |
+
return f"No data found for {symbol}. Please check the symbol and try again."
|
293 |
+
else:
|
294 |
+
# Ensure the DataFrame has enough rows
|
295 |
+
if len(data) < 20:
|
296 |
+
return f"Not enough data to calculate indicators. Only {len(data)} rows available. Please try a longer period."
|
297 |
+
else:
|
298 |
+
# Calculate probabilities for the next 12 hours
|
299 |
+
probabilities, recent_data = calculate_probabilities(data)
|
300 |
+
|
301 |
+
# Predict future prices
|
302 |
+
price_predictions = predict_price(data)
|
303 |
+
|
304 |
+
# Fetch crypto news and sentiment
|
305 |
+
news_items = fetch_crypto_news(symbol)
|
306 |
+
|
307 |
+
# Calculate Fibonacci Retracement levels
|
308 |
+
fib_levels = calculate_fibonacci_levels(data)
|
309 |
+
|
310 |
+
# Prepare output
|
311 |
+
output = f"**{symbol} Pump/Dump Probabilities (Next 12 Hours):**\n"
|
312 |
+
for indicator, values in probabilities.items():
|
313 |
+
output += f"- **{indicator}**: Pump: {values['Pump']:.2f}%, Dump: {values['Dump']:.2f}%\n"
|
314 |
+
|
315 |
+
output += "\n**Price Predictions (Next 7 Days):**\n"
|
316 |
+
if isinstance(price_predictions, pd.DataFrame):
|
317 |
+
output += price_predictions.to_string(index=False)
|
318 |
+
else:
|
319 |
+
output += price_predictions
|
320 |
+
|
321 |
+
output += "\n\n**Fibonacci Retracement Levels:**\n"
|
322 |
+
for level, price in fib_levels.items():
|
323 |
+
output += f"- **{level}**: ${price:.2f}\n"
|
324 |
+
|
325 |
+
output += "\n**Latest Crypto News Sentiment:**\n"
|
326 |
+
if isinstance(news_items, list):
|
327 |
+
for news in news_items:
|
328 |
+
output += f"- **{news['title']}** ({news['sentiment']}) - {news['published']}\n"
|
329 |
+
output += f" [Read more]({news['link']})\n"
|
330 |
+
else:
|
331 |
+
output += news_items
|
332 |
+
|
333 |
+
return output
|
334 |
+
else:
|
335 |
+
return "Please enter a cryptocurrency symbol."
|
336 |
+
|
337 |
+
# Gradio Interface
|
338 |
+
iface = gr.Interface(
|
339 |
+
fn=crypto_app,
|
340 |
+
inputs=gr.Textbox(placeholder="Enter cryptocurrency symbol (e.g., ETH, BTC)"),
|
341 |
+
outputs="text",
|
342 |
+
title="Crypto Information Finder and Pump/Dump Predictor ππ",
|
343 |
+
description="This app provides technical indicator-based predictions, price forecasts, and sentiment analysis for any cryptocurrency.",
|
344 |
+
)
|
345 |
+
|
346 |
+
iface.launch()
|