Update app.py
Browse files
app.py
CHANGED
|
@@ -5,12 +5,13 @@ import traceback
|
|
| 5 |
from datetime import datetime
|
| 6 |
|
| 7 |
import gradio as gr
|
| 8 |
-
import yfinance as yf
|
| 9 |
import pandas as pd
|
| 10 |
import numpy as np
|
| 11 |
import joblib
|
| 12 |
import requests
|
| 13 |
|
|
|
|
|
|
|
| 14 |
from sklearn.preprocessing import MinMaxScaler
|
| 15 |
from sklearn.model_selection import TimeSeriesSplit
|
| 16 |
|
|
@@ -24,14 +25,14 @@ from ta.trend import ADXIndicator
|
|
| 24 |
# ============================================================
|
| 25 |
|
| 26 |
SYMBOLS = [
|
| 27 |
-
"
|
| 28 |
-
"
|
| 29 |
-
"
|
| 30 |
-
"
|
| 31 |
-
"
|
| 32 |
-
"
|
| 33 |
-
"
|
| 34 |
-
"
|
| 35 |
]
|
| 36 |
|
| 37 |
WINDOW = 20
|
|
@@ -47,6 +48,12 @@ os.makedirs(LOG_DIR, exist_ok=True)
|
|
| 47 |
BOT_TOKEN = "YOUR_BOT_TOKEN"
|
| 48 |
CHAT_ID = "YOUR_CHAT_ID"
|
| 49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
# ============================================================
|
| 52 |
# LOGGING
|
|
@@ -57,6 +64,13 @@ logging.basicConfig(level=logging.INFO)
|
|
| 57 |
logger = logging.getLogger("crypto_ai")
|
| 58 |
|
| 59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
# ============================================================
|
| 61 |
# PATHS
|
| 62 |
# ============================================================
|
|
@@ -116,50 +130,21 @@ def send_telegram(message):
|
|
| 116 |
|
| 117 |
|
| 118 |
# ============================================================
|
| 119 |
-
#
|
| 120 |
# ============================================================
|
| 121 |
|
| 122 |
-
def clean_columns(df):
|
| 123 |
-
|
| 124 |
-
if isinstance(df.columns, pd.MultiIndex):
|
| 125 |
-
|
| 126 |
-
df.columns = (
|
| 127 |
-
df.columns.get_level_values(0)
|
| 128 |
-
)
|
| 129 |
-
|
| 130 |
-
df.columns = [
|
| 131 |
-
str(c).lower()
|
| 132 |
-
for c in df.columns
|
| 133 |
-
]
|
| 134 |
-
|
| 135 |
-
return df
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
# ============================================================
|
| 139 |
-
# DOWNLOAD DATA
|
| 140 |
-
# ============================================================
|
| 141 |
-
|
| 142 |
-
# ============================================================
|
| 143 |
-
# DOWNLOAD DATA WITH CACHE + RETRY
|
| 144 |
-
# ============================================================
|
| 145 |
-
|
| 146 |
-
LAST_DOWNLOAD = {}
|
| 147 |
-
|
| 148 |
-
CACHE_SECONDS = 300
|
| 149 |
-
|
| 150 |
-
|
| 151 |
def safe_download(symbol):
|
| 152 |
|
| 153 |
try:
|
| 154 |
|
| 155 |
-
# ============================================
|
| 156 |
-
# CACHE
|
| 157 |
-
# ============================================
|
| 158 |
-
|
| 159 |
now = time.time()
|
| 160 |
|
| 161 |
csv_file = data_path(symbol)
|
| 162 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
if os.path.exists(csv_file):
|
| 164 |
|
| 165 |
last_time = LAST_DOWNLOAD.get(symbol, 0)
|
|
@@ -178,24 +163,37 @@ def safe_download(symbol):
|
|
| 178 |
# DOWNLOAD
|
| 179 |
# ============================================
|
| 180 |
|
| 181 |
-
|
| 182 |
-
symbol,
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
progress=False,
|
| 186 |
-
auto_adjust=True,
|
| 187 |
-
threads=False
|
| 188 |
)
|
| 189 |
|
| 190 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
|
| 192 |
-
|
| 193 |
|
| 194 |
-
|
| 195 |
|
| 196 |
-
|
| 197 |
|
| 198 |
-
|
| 199 |
|
| 200 |
except Exception as e:
|
| 201 |
|
|
@@ -204,7 +202,7 @@ def safe_download(symbol):
|
|
| 204 |
)
|
| 205 |
|
| 206 |
# ============================================
|
| 207 |
-
# FALLBACK
|
| 208 |
# ============================================
|
| 209 |
|
| 210 |
try:
|
|
@@ -212,7 +210,7 @@ def safe_download(symbol):
|
|
| 212 |
if os.path.exists(csv_file):
|
| 213 |
|
| 214 |
logger.info(
|
| 215 |
-
f"Using
|
| 216 |
)
|
| 217 |
|
| 218 |
df = pd.read_csv(
|
|
@@ -539,6 +537,8 @@ def train_all():
|
|
| 539 |
|
| 540 |
run_train(symbol)
|
| 541 |
|
|
|
|
|
|
|
| 542 |
except Exception as e:
|
| 543 |
|
| 544 |
logger.error(e)
|
|
@@ -585,11 +585,11 @@ def trade_levels(df, signal):
|
|
| 585 |
sl = close_price
|
| 586 |
|
| 587 |
return {
|
| 588 |
-
"entry": close_price,
|
| 589 |
-
"tp1": tp1,
|
| 590 |
-
"tp2": tp2,
|
| 591 |
-
"tp3": tp3,
|
| 592 |
-
"sl": sl
|
| 593 |
}
|
| 594 |
|
| 595 |
|
|
@@ -597,8 +597,6 @@ def trade_levels(df, signal):
|
|
| 597 |
# PREDICT
|
| 598 |
# ============================================================
|
| 599 |
|
| 600 |
-
LAST_SIGNALS = {}
|
| 601 |
-
|
| 602 |
def run_predict(symbol):
|
| 603 |
|
| 604 |
if not os.path.exists(
|
|
@@ -682,18 +680,12 @@ def run_predict(symbol):
|
|
| 682 |
|
| 683 |
signal = "NO TRADE"
|
| 684 |
|
| 685 |
-
if regime =
|
| 686 |
-
|
| 687 |
-
signal = "NO TRADE"
|
| 688 |
-
|
| 689 |
-
else:
|
| 690 |
|
| 691 |
if pred == 1 and score >= 60:
|
| 692 |
-
|
| 693 |
signal = "BUY"
|
| 694 |
|
| 695 |
elif pred == 0 and score >= 60:
|
| 696 |
-
|
| 697 |
signal = "SELL"
|
| 698 |
|
| 699 |
levels = trade_levels(
|
|
@@ -716,6 +708,12 @@ PAIR: {symbol}
|
|
| 716 |
SIGNAL: {signal}
|
| 717 |
|
| 718 |
CONFIDENCE: {confidence:.2f}%
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 719 |
"""
|
| 720 |
)
|
| 721 |
|
|
@@ -724,11 +722,11 @@ CONFIDENCE: {confidence:.2f}%
|
|
| 724 |
"Signal": signal,
|
| 725 |
"Confidence": round(confidence, 2),
|
| 726 |
"Score": score,
|
| 727 |
-
"Entry":
|
| 728 |
-
"TP1":
|
| 729 |
-
"TP2":
|
| 730 |
-
"TP3":
|
| 731 |
-
"SL":
|
| 732 |
"RSI": round(last["rsi"], 2),
|
| 733 |
"ADX": round(last["adx"], 2),
|
| 734 |
"Regime": regime
|
|
@@ -739,14 +737,14 @@ CONFIDENCE: {confidence:.2f}%
|
|
| 739 |
# REALTIME SCAN
|
| 740 |
# ============================================================
|
| 741 |
|
| 742 |
-
LAST_RETRAIN = 0
|
| 743 |
-
|
| 744 |
def realtime_scan():
|
| 745 |
|
| 746 |
global LAST_RETRAIN
|
| 747 |
|
| 748 |
now = time.time()
|
| 749 |
|
|
|
|
|
|
|
| 750 |
if now - LAST_RETRAIN > 21600:
|
| 751 |
|
| 752 |
logger.info(
|
|
@@ -761,9 +759,10 @@ def realtime_scan():
|
|
| 761 |
|
| 762 |
for symbol in SYMBOLS:
|
| 763 |
|
| 764 |
-
time.sleep(1)
|
| 765 |
try:
|
| 766 |
|
|
|
|
|
|
|
| 767 |
result = run_predict(symbol)
|
| 768 |
|
| 769 |
rows.append(result)
|
|
@@ -802,7 +801,7 @@ with gr.Blocks(
|
|
| 802 |
interactive=False
|
| 803 |
)
|
| 804 |
|
| 805 |
-
timer = gr.Timer(
|
| 806 |
|
| 807 |
timer.tick(
|
| 808 |
fn=realtime_scan,
|
|
|
|
| 5 |
from datetime import datetime
|
| 6 |
|
| 7 |
import gradio as gr
|
|
|
|
| 8 |
import pandas as pd
|
| 9 |
import numpy as np
|
| 10 |
import joblib
|
| 11 |
import requests
|
| 12 |
|
| 13 |
+
from binance.client import Client
|
| 14 |
+
|
| 15 |
from sklearn.preprocessing import MinMaxScaler
|
| 16 |
from sklearn.model_selection import TimeSeriesSplit
|
| 17 |
|
|
|
|
| 25 |
# ============================================================
|
| 26 |
|
| 27 |
SYMBOLS = [
|
| 28 |
+
"BTCUSDT",
|
| 29 |
+
"ETHUSDT",
|
| 30 |
+
"SOLUSDT",
|
| 31 |
+
"BNBUSDT",
|
| 32 |
+
"XRPUSDT",
|
| 33 |
+
"DOGEUSDT",
|
| 34 |
+
"AVAXUSDT",
|
| 35 |
+
"LINKUSDT",
|
| 36 |
]
|
| 37 |
|
| 38 |
WINDOW = 20
|
|
|
|
| 48 |
BOT_TOKEN = "YOUR_BOT_TOKEN"
|
| 49 |
CHAT_ID = "YOUR_CHAT_ID"
|
| 50 |
|
| 51 |
+
CACHE_SECONDS = 60
|
| 52 |
+
|
| 53 |
+
LAST_DOWNLOAD = {}
|
| 54 |
+
LAST_SIGNALS = {}
|
| 55 |
+
LAST_RETRAIN = 0
|
| 56 |
+
|
| 57 |
|
| 58 |
# ============================================================
|
| 59 |
# LOGGING
|
|
|
|
| 64 |
logger = logging.getLogger("crypto_ai")
|
| 65 |
|
| 66 |
|
| 67 |
+
# ============================================================
|
| 68 |
+
# BINANCE CLIENT
|
| 69 |
+
# ============================================================
|
| 70 |
+
|
| 71 |
+
client = Client()
|
| 72 |
+
|
| 73 |
+
|
| 74 |
# ============================================================
|
| 75 |
# PATHS
|
| 76 |
# ============================================================
|
|
|
|
| 130 |
|
| 131 |
|
| 132 |
# ============================================================
|
| 133 |
+
# DOWNLOAD BINANCE DATA
|
| 134 |
# ============================================================
|
| 135 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
def safe_download(symbol):
|
| 137 |
|
| 138 |
try:
|
| 139 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
now = time.time()
|
| 141 |
|
| 142 |
csv_file = data_path(symbol)
|
| 143 |
|
| 144 |
+
# ============================================
|
| 145 |
+
# CACHE
|
| 146 |
+
# ============================================
|
| 147 |
+
|
| 148 |
if os.path.exists(csv_file):
|
| 149 |
|
| 150 |
last_time = LAST_DOWNLOAD.get(symbol, 0)
|
|
|
|
| 163 |
# DOWNLOAD
|
| 164 |
# ============================================
|
| 165 |
|
| 166 |
+
klines = client.get_klines(
|
| 167 |
+
symbol=symbol,
|
| 168 |
+
interval=Client.KLINE_INTERVAL_1HOUR,
|
| 169 |
+
limit=1000
|
|
|
|
|
|
|
|
|
|
| 170 |
)
|
| 171 |
|
| 172 |
+
rows = []
|
| 173 |
+
|
| 174 |
+
for k in klines:
|
| 175 |
+
|
| 176 |
+
rows.append({
|
| 177 |
+
"time": pd.to_datetime(
|
| 178 |
+
k[0],
|
| 179 |
+
unit="ms"
|
| 180 |
+
),
|
| 181 |
+
"open": float(k[1]),
|
| 182 |
+
"high": float(k[2]),
|
| 183 |
+
"low": float(k[3]),
|
| 184 |
+
"close": float(k[4]),
|
| 185 |
+
"volume": float(k[5]),
|
| 186 |
+
})
|
| 187 |
+
|
| 188 |
+
df = pd.DataFrame(rows)
|
| 189 |
|
| 190 |
+
df = df.set_index("time")
|
| 191 |
|
| 192 |
+
df.to_csv(csv_file)
|
| 193 |
|
| 194 |
+
LAST_DOWNLOAD[symbol] = now
|
| 195 |
|
| 196 |
+
return df, True
|
| 197 |
|
| 198 |
except Exception as e:
|
| 199 |
|
|
|
|
| 202 |
)
|
| 203 |
|
| 204 |
# ============================================
|
| 205 |
+
# FALLBACK CACHE
|
| 206 |
# ============================================
|
| 207 |
|
| 208 |
try:
|
|
|
|
| 210 |
if os.path.exists(csv_file):
|
| 211 |
|
| 212 |
logger.info(
|
| 213 |
+
f"Using cache: {symbol}"
|
| 214 |
)
|
| 215 |
|
| 216 |
df = pd.read_csv(
|
|
|
|
| 537 |
|
| 538 |
run_train(symbol)
|
| 539 |
|
| 540 |
+
time.sleep(0.5)
|
| 541 |
+
|
| 542 |
except Exception as e:
|
| 543 |
|
| 544 |
logger.error(e)
|
|
|
|
| 585 |
sl = close_price
|
| 586 |
|
| 587 |
return {
|
| 588 |
+
"entry": round(close_price, 2),
|
| 589 |
+
"tp1": round(tp1, 2),
|
| 590 |
+
"tp2": round(tp2, 2),
|
| 591 |
+
"tp3": round(tp3, 2),
|
| 592 |
+
"sl": round(sl, 2)
|
| 593 |
}
|
| 594 |
|
| 595 |
|
|
|
|
| 597 |
# PREDICT
|
| 598 |
# ============================================================
|
| 599 |
|
|
|
|
|
|
|
| 600 |
def run_predict(symbol):
|
| 601 |
|
| 602 |
if not os.path.exists(
|
|
|
|
| 680 |
|
| 681 |
signal = "NO TRADE"
|
| 682 |
|
| 683 |
+
if regime != "SIDEWAYS":
|
|
|
|
|
|
|
|
|
|
|
|
|
| 684 |
|
| 685 |
if pred == 1 and score >= 60:
|
|
|
|
| 686 |
signal = "BUY"
|
| 687 |
|
| 688 |
elif pred == 0 and score >= 60:
|
|
|
|
| 689 |
signal = "SELL"
|
| 690 |
|
| 691 |
levels = trade_levels(
|
|
|
|
| 708 |
SIGNAL: {signal}
|
| 709 |
|
| 710 |
CONFIDENCE: {confidence:.2f}%
|
| 711 |
+
|
| 712 |
+
ENTRY: {levels['entry']}
|
| 713 |
+
TP1: {levels['tp1']}
|
| 714 |
+
TP2: {levels['tp2']}
|
| 715 |
+
TP3: {levels['tp3']}
|
| 716 |
+
SL: {levels['sl']}
|
| 717 |
"""
|
| 718 |
)
|
| 719 |
|
|
|
|
| 722 |
"Signal": signal,
|
| 723 |
"Confidence": round(confidence, 2),
|
| 724 |
"Score": score,
|
| 725 |
+
"Entry": levels["entry"],
|
| 726 |
+
"TP1": levels["tp1"],
|
| 727 |
+
"TP2": levels["tp2"],
|
| 728 |
+
"TP3": levels["tp3"],
|
| 729 |
+
"SL": levels["sl"],
|
| 730 |
"RSI": round(last["rsi"], 2),
|
| 731 |
"ADX": round(last["adx"], 2),
|
| 732 |
"Regime": regime
|
|
|
|
| 737 |
# REALTIME SCAN
|
| 738 |
# ============================================================
|
| 739 |
|
|
|
|
|
|
|
| 740 |
def realtime_scan():
|
| 741 |
|
| 742 |
global LAST_RETRAIN
|
| 743 |
|
| 744 |
now = time.time()
|
| 745 |
|
| 746 |
+
# AUTO RETRAIN EVERY 6 HOURS
|
| 747 |
+
|
| 748 |
if now - LAST_RETRAIN > 21600:
|
| 749 |
|
| 750 |
logger.info(
|
|
|
|
| 759 |
|
| 760 |
for symbol in SYMBOLS:
|
| 761 |
|
|
|
|
| 762 |
try:
|
| 763 |
|
| 764 |
+
time.sleep(0.3)
|
| 765 |
+
|
| 766 |
result = run_predict(symbol)
|
| 767 |
|
| 768 |
rows.append(result)
|
|
|
|
| 801 |
interactive=False
|
| 802 |
)
|
| 803 |
|
| 804 |
+
timer = gr.Timer(30)
|
| 805 |
|
| 806 |
timer.tick(
|
| 807 |
fn=realtime_scan,
|