Update app.py
Browse files
app.py
CHANGED
|
@@ -1,31 +1,25 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
import ccxt
|
| 3 |
import pandas as pd
|
| 4 |
import numpy as np
|
| 5 |
import xgboost as xgb
|
|
|
|
| 6 |
import gc
|
| 7 |
-
import
|
| 8 |
-
import
|
|
|
|
| 9 |
|
| 10 |
-
# ---
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
response = requests.get(url, timeout=5).json()
|
| 15 |
-
usdt_dom = response['data']['market_cap_percentage']['usdt']
|
| 16 |
-
btc_dom = response['data']['market_cap_percentage']['btc']
|
| 17 |
-
return usdt_dom, btc_dom
|
| 18 |
-
except Exception as e:
|
| 19 |
-
return 0.0, 0.0
|
| 20 |
|
| 21 |
-
# ---
|
| 22 |
-
|
| 23 |
-
def train_spot_model(symbol):
|
| 24 |
exchange = ccxt.bitget()
|
|
|
|
|
|
|
| 25 |
ohlcv = exchange.fetch_ohlcv(symbol, '1h', limit=2000)
|
| 26 |
df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
| 27 |
-
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
|
| 28 |
-
|
| 29 |
df['buy_vol_proxy'] = np.where(df['close'] > df['open'], df['volume'] * 0.7, df['volume'] * 0.3)
|
| 30 |
df['sell_vol_proxy'] = df['volume'] - df['buy_vol_proxy']
|
| 31 |
df['order_book_imbalance'] = (df['buy_vol_proxy'] - df['sell_vol_proxy']) / (df['volume'] + 1e-9)
|
|
@@ -34,8 +28,7 @@ def train_spot_model(symbol):
|
|
| 34 |
tp_pct, sl_pct, max_candles = 0.02, 0.01, 24
|
| 35 |
for i in range(len(df)):
|
| 36 |
if i + max_candles >= len(df):
|
| 37 |
-
targets.append(0)
|
| 38 |
-
continue
|
| 39 |
entry = df['close'].iloc[i]
|
| 40 |
tp, sl = entry * (1 + tp_pct), entry * (1 - sl_pct)
|
| 41 |
success = 0
|
|
@@ -55,141 +48,88 @@ def train_spot_model(symbol):
|
|
| 55 |
df = df.dropna()
|
| 56 |
|
| 57 |
features = ['open', 'high', 'low', 'close', 'volume', 'order_book_imbalance', 'SMA_10', 'SMA_50', 'ATR', 'Regime', 'returns']
|
| 58 |
-
X = df[features]
|
| 59 |
-
y = df['target']
|
| 60 |
|
| 61 |
-
|
| 62 |
-
model = xgb.XGBClassifier(n_estimators=300, max_depth=5, learning_rate=0.05, scale_pos_weight=scale_weight)
|
| 63 |
model.fit(X, y)
|
| 64 |
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
return model, features
|
| 69 |
-
|
| 70 |
-
# --- 2. Live Market Execution ---
|
| 71 |
-
def analyze_live_market(symbol, model, features):
|
| 72 |
-
exchange = ccxt.bitget()
|
| 73 |
-
|
| 74 |
-
ohlcv = exchange.fetch_ohlcv(symbol, '1h', limit=100)
|
| 75 |
-
df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
| 76 |
-
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
|
| 77 |
|
| 78 |
orderbook = exchange.fetch_order_book(symbol, limit=50)
|
| 79 |
bids_vol = sum([bid[1] for bid in orderbook['bids']])
|
| 80 |
asks_vol = sum([ask[1] for ask in orderbook['asks']])
|
| 81 |
live_imbalance = (bids_vol - asks_vol) / (bids_vol + asks_vol + 1e-9)
|
| 82 |
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
|
| 90 |
-
latest =
|
| 91 |
-
|
| 92 |
|
| 93 |
-
|
|
|
|
|
|
|
| 94 |
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
invalidation_level = limit_entry * 0.99
|
| 102 |
-
take_profit = limit_entry * 1.02
|
| 103 |
|
| 104 |
-
return
|
| 105 |
|
| 106 |
-
# --- 3.
|
| 107 |
-
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
'APT/USDT', 'FIL/USDT', 'XLM/USDT', 'STX/USDT', 'HBAR/USDT', 'ATOM/USDT',
|
| 115 |
-
'GRT/USDT', 'OP/USDT', 'LDO/USDT', 'INJ/USDT', 'TIA/USDT', 'SUI/USDT',
|
| 116 |
-
'AR/USDT', 'RENDER/USDT', 'FET/USDT', 'SEI/USDT', 'RUNE/USDT', 'FTM/USDT',
|
| 117 |
-
'PEPE/USDT', 'BONK/USDT', 'ORDI/USDT', 'PI/USDT', 'WIF/USDT', 'JUP/USDT',
|
| 118 |
-
'PYTH/USDT', 'ONDO/USDT', 'ARB/USDT'
|
| 119 |
-
]
|
| 120 |
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
-
|
| 124 |
-
with st.spinner("Fetching Macro Cash Flow Data..."):
|
| 125 |
-
usdt_dom, btc_dom = get_live_usdt_dominance()
|
| 126 |
-
|
| 127 |
-
with st.spinner(f"Training XGBoost on heavy {symbol} data..."):
|
| 128 |
-
model, features = train_spot_model(symbol)
|
| 129 |
-
|
| 130 |
-
with st.spinner("Fetching live order book & charting..."):
|
| 131 |
-
price, conf, entry, stop, tp, state, imbalance, chart_df = analyze_live_market(symbol, model, features)
|
| 132 |
-
|
| 133 |
-
# --- UI DISPLAY ---
|
| 134 |
-
col1, col2, col3, col4 = st.columns(4)
|
| 135 |
-
col1.metric("Current Price", f"${price:,.4f}")
|
| 136 |
-
col2.metric("AI Probability", f"{conf:.2%}")
|
| 137 |
-
col3.metric("Order Book", f"{imbalance:.2f}")
|
| 138 |
-
col4.metric("USDT Dominance", f"{usdt_dom:.2f}%", help="High = People holding cash (Bearish). Low = People buying crypto (Bullish).")
|
| 139 |
-
|
| 140 |
-
st.divider()
|
| 141 |
-
|
| 142 |
-
# --- LIVE INTERACTIVE CHART ---
|
| 143 |
-
st.markdown("### 📈 Live Price Action & Targets")
|
| 144 |
-
fig = go.Figure(data=[go.Candlestick(
|
| 145 |
-
x=chart_df['timestamp'],
|
| 146 |
-
open=chart_df['open'], high=chart_df['high'],
|
| 147 |
-
low=chart_df['low'], close=chart_df['close'],
|
| 148 |
-
increasing_line_color='#2ebd85', decreasing_line_color='#f6465d',
|
| 149 |
-
name='Price'
|
| 150 |
-
)])
|
| 151 |
-
|
| 152 |
-
# Draw AI Tactical Levels on the Chart
|
| 153 |
-
fig.add_hline(y=entry, line_dash="dash", line_color="#f0b90b", annotation_text="Limit Entry", annotation_position="top left")
|
| 154 |
-
fig.add_hline(y=tp, line_dash="solid", line_color="#2ebd85", annotation_text="Take Profit", annotation_position="top left")
|
| 155 |
-
fig.add_hline(y=stop, line_dash="solid", line_color="#f6465d", annotation_text="Stop Loss", annotation_position="bottom left")
|
| 156 |
-
|
| 157 |
-
fig.update_layout(
|
| 158 |
-
template='plotly_dark',
|
| 159 |
-
height=500,
|
| 160 |
-
margin=dict(l=0, r=0, t=30, b=0),
|
| 161 |
-
xaxis_rangeslider_visible=False,
|
| 162 |
-
paper_bgcolor='#0e1117',
|
| 163 |
-
plot_bgcolor='#0e1117'
|
| 164 |
-
)
|
| 165 |
-
st.plotly_chart(fig, use_container_width=True)
|
| 166 |
-
|
| 167 |
-
st.divider()
|
| 168 |
-
st.markdown(f"### 🛡️ Market Condition: {state}")
|
| 169 |
-
st.error(f"**DO NOT MARKET BUY.** Place a Limit Order at or below: **${entry:,.4f}**")
|
| 170 |
-
st.success(f"**Take Profit Target:** **${tp:,.4f}**")
|
| 171 |
-
st.warning(f"**Invalidation Level (Hard Stop Loss):** **${stop:,.4f}**")
|
| 172 |
-
|
| 173 |
-
st.divider()
|
| 174 |
-
st.markdown("### 🤖 AI Final Verdict")
|
| 175 |
-
|
| 176 |
-
is_trending = "Trending" in state
|
| 177 |
-
is_confident = conf >= 0.60
|
| 178 |
-
is_good_imbalance = 0.30 <= imbalance <= 0.65
|
| 179 |
-
is_manipulated = imbalance > 0.65
|
| 180 |
-
is_safe_macro = usdt_dom < 6.50
|
| 181 |
-
|
| 182 |
-
if is_trending and is_confident and is_good_imbalance and is_safe_macro:
|
| 183 |
-
st.success("✅ **PRIME SETUP DETECTED!** All metrics and macro cash flows align perfectly.")
|
| 184 |
-
else:
|
| 185 |
-
st.warning("⚠️ **SETUP REJECTED. Do not trade.** See failure reasons below:")
|
| 186 |
-
|
| 187 |
-
if not is_trending:
|
| 188 |
-
st.info("❌ Market is ranging/choppy.")
|
| 189 |
-
if not is_confident:
|
| 190 |
-
st.info(f"❌ AI Confidence is only {conf:.1%} (Requires 60.0%+).")
|
| 191 |
-
if imbalance < 0.30:
|
| 192 |
-
st.info(f"❌ Buy pressure is too weak. Imbalance is {imbalance:.2f}.")
|
| 193 |
-
if not is_safe_macro:
|
| 194 |
-
st.error(f"🚨 **MACRO KILL SWITCH ACTIVATED:** USDT Dominance is extremely high ({usdt_dom:.2f}%). Traders are fleeing to cash.")
|
| 195 |
-
|
|
|
|
|
|
|
| 1 |
import ccxt
|
| 2 |
import pandas as pd
|
| 3 |
import numpy as np
|
| 4 |
import xgboost as xgb
|
| 5 |
+
import time
|
| 6 |
import gc
|
| 7 |
+
import threading
|
| 8 |
+
import gradio as gr
|
| 9 |
+
from utils import get_live_usdt_dominance, send_email_alert
|
| 10 |
|
| 11 |
+
# --- 1. Your Credentials ---
|
| 12 |
+
SENDER_EMAIL = "your_email@gmail.com"
|
| 13 |
+
SENDER_PASSWORD = "paste_your_16_digit_app_password_here"
|
| 14 |
+
RECEIVER_EMAIL = "your_email@gmail.com"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
# --- 2. The Core AI Engine ---
|
| 17 |
+
def train_and_evaluate(symbol):
|
|
|
|
| 18 |
exchange = ccxt.bitget()
|
| 19 |
+
|
| 20 |
+
# Train Model (Historical Data)
|
| 21 |
ohlcv = exchange.fetch_ohlcv(symbol, '1h', limit=2000)
|
| 22 |
df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
|
|
|
|
|
|
| 23 |
df['buy_vol_proxy'] = np.where(df['close'] > df['open'], df['volume'] * 0.7, df['volume'] * 0.3)
|
| 24 |
df['sell_vol_proxy'] = df['volume'] - df['buy_vol_proxy']
|
| 25 |
df['order_book_imbalance'] = (df['buy_vol_proxy'] - df['sell_vol_proxy']) / (df['volume'] + 1e-9)
|
|
|
|
| 28 |
tp_pct, sl_pct, max_candles = 0.02, 0.01, 24
|
| 29 |
for i in range(len(df)):
|
| 30 |
if i + max_candles >= len(df):
|
| 31 |
+
targets.append(0); continue
|
|
|
|
| 32 |
entry = df['close'].iloc[i]
|
| 33 |
tp, sl = entry * (1 + tp_pct), entry * (1 - sl_pct)
|
| 34 |
success = 0
|
|
|
|
| 48 |
df = df.dropna()
|
| 49 |
|
| 50 |
features = ['open', 'high', 'low', 'close', 'volume', 'order_book_imbalance', 'SMA_10', 'SMA_50', 'ATR', 'Regime', 'returns']
|
| 51 |
+
X, y = df[features], df['target']
|
|
|
|
| 52 |
|
| 53 |
+
model = xgb.XGBClassifier(n_estimators=300, max_depth=5, learning_rate=0.05, scale_pos_weight=(y == 0).sum() / ((y == 1).sum() + 1e-9))
|
|
|
|
| 54 |
model.fit(X, y)
|
| 55 |
|
| 56 |
+
# Live Market Check
|
| 57 |
+
ohlcv_live = exchange.fetch_ohlcv(symbol, '1h', limit=60)
|
| 58 |
+
df_live = pd.DataFrame(ohlcv_live, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
orderbook = exchange.fetch_order_book(symbol, limit=50)
|
| 61 |
bids_vol = sum([bid[1] for bid in orderbook['bids']])
|
| 62 |
asks_vol = sum([ask[1] for ask in orderbook['asks']])
|
| 63 |
live_imbalance = (bids_vol - asks_vol) / (bids_vol + asks_vol + 1e-9)
|
| 64 |
|
| 65 |
+
df_live['order_book_imbalance'] = live_imbalance
|
| 66 |
+
df_live['SMA_10'] = df_live['close'].rolling(10).mean()
|
| 67 |
+
df_live['SMA_50'] = df_live['close'].rolling(50).mean()
|
| 68 |
+
df_live['ATR'] = df_live['high'].rolling(14).max() - df_live['low'].rolling(14).min()
|
| 69 |
+
df_live['Regime'] = df_live['close'].rolling(20).std() / (df_live['ATR'] + 1e-9)
|
| 70 |
+
df_live['returns'] = df_live['close'].pct_change()
|
| 71 |
|
| 72 |
+
latest = df_live.dropna().iloc[-1:]
|
| 73 |
+
conf = model.predict_proba(latest[features])[0][1]
|
| 74 |
|
| 75 |
+
price = latest['close'].iloc[0]
|
| 76 |
+
atr = latest['ATR'].iloc[0]
|
| 77 |
+
regime = latest['Regime'].iloc[0]
|
| 78 |
|
| 79 |
+
limit_entry = price - (atr * 0.5)
|
| 80 |
+
stop = limit_entry * 0.99
|
| 81 |
+
tp = limit_entry * 1.02
|
| 82 |
|
| 83 |
+
del df, X, y, df_live, model
|
| 84 |
+
gc.collect()
|
|
|
|
|
|
|
| 85 |
|
| 86 |
+
return price, conf, limit_entry, stop, tp, regime, live_imbalance
|
| 87 |
|
| 88 |
+
# --- 3. The Infinite Background Loop ---
|
| 89 |
+
def run_scanner():
|
| 90 |
+
TOP_PAIRS = ['BTC/USDT', 'ETH/USDT', 'SOL/USDT', 'BNB/USDT', 'XRP/USDT', 'DOGE/USDT', 'PEPE/USDT', 'BONK/USDT', 'PI/USDT', 'WIF/USDT']
|
| 91 |
+
|
| 92 |
+
print("Initializing scanner thread...")
|
| 93 |
+
send_email_alert(SENDER_EMAIL, SENDER_PASSWORD, RECEIVER_EMAIL, "SYSTEM", "🟢 AI Spot Sniper Bot Online. Scanning markets from Hugging Face...")
|
| 94 |
+
|
| 95 |
+
while True:
|
| 96 |
+
try:
|
| 97 |
+
usdt_dom = get_live_usdt_dominance()
|
| 98 |
+
if usdt_dom >= 6.50:
|
| 99 |
+
print(f"Macro Kill Switch Active (USDT.D: {usdt_dom:.2f}%). Sleeping 15 mins.")
|
| 100 |
+
time.sleep(900)
|
| 101 |
+
continue
|
| 102 |
+
|
| 103 |
+
for symbol in TOP_PAIRS:
|
| 104 |
+
price, conf, entry, stop, tp, regime, imbalance = train_and_evaluate(symbol)
|
| 105 |
+
|
| 106 |
+
if regime > 1.2 and conf >= 0.60 and 0.30 <= imbalance <= 0.65:
|
| 107 |
+
alert_msg = (
|
| 108 |
+
f"PRIME SETUP DETECTED: {symbol}\n\n"
|
| 109 |
+
f"Current Price: ${price:.4f}\n"
|
| 110 |
+
f"AI Confidence: {conf:.2%}\n"
|
| 111 |
+
f"Order Book Imbalance: {imbalance:.2f}\n\n"
|
| 112 |
+
f"LIMIT BUY: ${entry:.4f}\n"
|
| 113 |
+
f"TAKE PROFIT: ${tp:.4f}\n"
|
| 114 |
+
f"STOP LOSS: ${stop:.4f}"
|
| 115 |
+
)
|
| 116 |
+
send_email_alert(SENDER_EMAIL, SENDER_PASSWORD, RECEIVER_EMAIL, symbol, alert_msg)
|
| 117 |
+
|
| 118 |
+
time.sleep(2)
|
| 119 |
+
time.sleep(900)
|
| 120 |
+
|
| 121 |
+
except Exception as e:
|
| 122 |
+
print(f"Error in main loop: {e}")
|
| 123 |
+
time.sleep(60)
|
| 124 |
|
| 125 |
+
# --- 4. The Gradio Web Interface ---
|
| 126 |
+
thread = threading.Thread(target=run_scanner)
|
| 127 |
+
thread.daemon = True
|
| 128 |
+
thread.start()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
+
with gr.Blocks() as demo:
|
| 131 |
+
gr.Markdown("# 🟢 Email Spot Sniper is Live!")
|
| 132 |
+
gr.Markdown("Your AI is currently scanning the markets in the background. Emails will be sent automatically when a high-probability setup is found.")
|
| 133 |
+
gr.Markdown("**Note:** Ensure you are using a ping service (like UptimeRobot) to keep this Space from going to sleep.")
|
| 134 |
|
| 135 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|