Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import numpy as np
|
| 4 |
+
import ccxt
|
| 5 |
+
import matplotlib.pyplot as plt
|
| 6 |
+
from ta.momentum import RSIIndicator
|
| 7 |
+
from ta.trend import EMAIndicator, MACD, ADXIndicator
|
| 8 |
+
from ta.volatility import BollingerBands
|
| 9 |
+
from xgboost import XGBClassifier
|
| 10 |
+
from sklearn.model_selection import TimeSeriesSplit
|
| 11 |
+
from sklearn.metrics import classification_report
|
| 12 |
+
|
| 13 |
+
# --- PAGE SETUP ---
|
| 14 |
+
st.set_page_config(page_title="AI Crypto Signal Detector",
|
| 15 |
+
page_icon="💹",
|
| 16 |
+
layout="wide")
|
| 17 |
+
|
| 18 |
+
st.markdown("""
|
| 19 |
+
<style>
|
| 20 |
+
.reportview-container {
|
| 21 |
+
background: #0e1117;
|
| 22 |
+
color: #fafafa;
|
| 23 |
+
}
|
| 24 |
+
h1,h2,h3 { color: #0df2c8; }
|
| 25 |
+
.sidebar .sidebar-content {
|
| 26 |
+
background: #111;
|
| 27 |
+
}
|
| 28 |
+
div.stButton > button {
|
| 29 |
+
color: white;
|
| 30 |
+
background-color: #00b894;
|
| 31 |
+
border-radius: 10px;
|
| 32 |
+
height: 3em;
|
| 33 |
+
width: 100%;
|
| 34 |
+
}
|
| 35 |
+
</style>
|
| 36 |
+
""", unsafe_allow_html=True)
|
| 37 |
+
|
| 38 |
+
# --- HEADER ---
|
| 39 |
+
st.title("💹 AI-Powered Crypto Signal Detector")
|
| 40 |
+
st.markdown("Predict **LONG / SHORT / HOLD** signals using EMA, RSI, MACD and XGBoost.")
|
| 41 |
+
|
| 42 |
+
# --- USER INPUTS ---
|
| 43 |
+
col1, col2, col3 = st.columns(3)
|
| 44 |
+
with col1:
|
| 45 |
+
symbol = st.selectbox("Select Crypto Pair", ["BTC/USDT", "ETH/USDT", "BNB/USDT", "SOL/USDT"])
|
| 46 |
+
with col2:
|
| 47 |
+
timeframe = st.selectbox("Timeframe", ["1h", "4h", "1d"])
|
| 48 |
+
with col3:
|
| 49 |
+
limit = st.slider("Number of Candles", 500, 5000, 2000, step=500)
|
| 50 |
+
|
| 51 |
+
st.markdown("---")
|
| 52 |
+
|
| 53 |
+
# --- FETCH & RUN ---
|
| 54 |
+
if st.button("🚀 Run Model"):
|
| 55 |
+
st.info("Fetching data and training model … please wait ≈ 30 sec")
|
| 56 |
+
exchange = ccxt.binance()
|
| 57 |
+
bars = exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=limit)
|
| 58 |
+
df = pd.DataFrame(bars, columns=['ts','open','high','low','close','vol'])
|
| 59 |
+
df['ts'] = pd.to_datetime(df['ts'], unit='ms')
|
| 60 |
+
df = df.set_index('ts')
|
| 61 |
+
|
| 62 |
+
# --- FEATURES ---
|
| 63 |
+
df['ret1'] = df['close'].pct_change()
|
| 64 |
+
df['ema12'] = EMAIndicator(df['close'], window=12).ema_indicator()
|
| 65 |
+
df['ema26'] = EMAIndicator(df['close'], window=26).ema_indicator()
|
| 66 |
+
df['ema_diff'] = df['ema12'] - df['ema26']
|
| 67 |
+
df['rsi14'] = RSIIndicator(df['close'], window=14).rsi()
|
| 68 |
+
bb = BollingerBands(df['close'])
|
| 69 |
+
df['bb_high'] = bb.bollinger_hband()
|
| 70 |
+
df['bb_low'] = bb.bollinger_lband()
|
| 71 |
+
macd = MACD(df['close'])
|
| 72 |
+
df['macd'] = macd.macd()
|
| 73 |
+
df['macd_signal'] = macd.macd_signal()
|
| 74 |
+
adx = ADXIndicator(df['high'], df['low'], df['close'])
|
| 75 |
+
df['adx'] = adx.adx()
|
| 76 |
+
df = df.dropna()
|
| 77 |
+
|
| 78 |
+
# --- TARGET ---
|
| 79 |
+
fwd_horizon = 3
|
| 80 |
+
future_ret = df['close'].shift(-fwd_horizon) / df['close'] - 1
|
| 81 |
+
thr = 0.005
|
| 82 |
+
df['target'] = np.where(future_ret > thr, 1, np.where(future_ret < -thr, -1, 0))
|
| 83 |
+
df = df.dropna()
|
| 84 |
+
|
| 85 |
+
# --- MODEL DATA ---
|
| 86 |
+
features = ['ema_diff','rsi14','bb_high','bb_low','macd','macd_signal','adx','ret1']
|
| 87 |
+
X = df[features]
|
| 88 |
+
y = df['target'].map({-1:0,0:1,1:2}) # map for XGBoost
|
| 89 |
+
|
| 90 |
+
tscv = TimeSeriesSplit(n_splits=5)
|
| 91 |
+
train_idx, test_idx = list(tscv.split(X))[-1]
|
| 92 |
+
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
|
| 93 |
+
y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
|
| 94 |
+
|
| 95 |
+
# --- MODEL TRAIN ---
|
| 96 |
+
clf = XGBClassifier(n_estimators=300, learning_rate=0.05, max_depth=6,
|
| 97 |
+
subsample=0.8, colsample_bytree=0.8, random_state=42)
|
| 98 |
+
clf.fit(X_train, y_train)
|
| 99 |
+
|
| 100 |
+
# --- PREDICT ---
|
| 101 |
+
y_pred = clf.predict(X_test)
|
| 102 |
+
df_test = df.iloc[test_idx].copy()
|
| 103 |
+
df_test['pred'] = pd.Series(y_pred).map({0:-1,1:0,2:1})
|
| 104 |
+
|
| 105 |
+
# --- REPORT ---
|
| 106 |
+
y_true = pd.Series(y_test).map({0:-1,1:0,2:1})
|
| 107 |
+
report = classification_report(y_true, df_test['pred'], output_dict=True)
|
| 108 |
+
st.subheader("📊 Model Performance")
|
| 109 |
+
st.write(pd.DataFrame(report).transpose().style.background_gradient(cmap='Blues'))
|
| 110 |
+
|
| 111 |
+
# --- PLOT SIGNALS ---
|
| 112 |
+
st.subheader("📈 Signals on Price Chart")
|
| 113 |
+
fig, ax = plt.subplots(figsize=(12,5))
|
| 114 |
+
ax.plot(df_test.index, df_test['close'], label='Close Price', color='cyan')
|
| 115 |
+
ax.scatter(df_test.index[df_test['pred']==1], df_test['close'][df_test['pred']==1],
|
| 116 |
+
color='lime', label='LONG', marker='^', s=80)
|
| 117 |
+
ax.scatter(df_test.index[df_test['pred']==-1], df_test['close'][df_test['pred']==-1],
|
| 118 |
+
color='red', label='SHORT', marker='v', s=80)
|
| 119 |
+
ax.set_facecolor("#0e1117")
|
| 120 |
+
ax.legend()
|
| 121 |
+
st.pyplot(fig)
|
| 122 |
+
|
| 123 |
+
# --- SUMMARY ---
|
| 124 |
+
st.success("✅ Model completed! Scroll up for results and chart.")
|
| 125 |
+
st.markdown(f"**Dataset size:** {len(df)} rows | **Features:** {len(features)} | **Symbol:** {symbol}")
|