Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
| 1 |
# ============================================
|
| 2 |
-
# 👑 TOMIRIS SPACE 19
|
| 3 |
# ============================================
|
| 4 |
import os, time, threading, warnings, json, asyncio
|
| 5 |
from typing import Dict, Any, Optional, List, Tuple
|
| 6 |
import numpy as np, pandas as pd
|
| 7 |
import requests
|
| 8 |
-
from datetime import datetime
|
| 9 |
from collections import deque
|
| 10 |
from fastapi import FastAPI, Query
|
| 11 |
warnings.filterwarnings('ignore')
|
|
@@ -49,7 +49,6 @@ if HAS_FIREBASE:
|
|
| 49 |
SPACE_URLS: Dict[str, str] = {
|
| 50 |
"space_17_hub": os.getenv("SPACE17_URL", "https://tomiris-ai-name5-5.hf.space"),
|
| 51 |
"space_18_arbiter": os.getenv("SPACE18_URL", "https://tomiris-ai-name6-6.hf.space"),
|
| 52 |
-
"space_31_perf": "https://nuxotetotmailsvoboden-tomiris-perf.hf.space"
|
| 53 |
}
|
| 54 |
|
| 55 |
HUB_URL = SPACE_URLS["space_17_hub"]
|
|
@@ -64,6 +63,8 @@ TWELVE_KEYS: List[str] = [
|
|
| 64 |
SYMBOL: str = "SOL/USD"
|
| 65 |
MT5_SYMBOL: str = "SOLUSD"
|
| 66 |
TIMEFRAMES: List[str] = ["15min", "1h", "4h"]
|
|
|
|
|
|
|
| 67 |
|
| 68 |
try:
|
| 69 |
with open("best_config.json", "r") as f:
|
|
@@ -111,7 +112,7 @@ COMPONENT_PERF: Dict[str, Dict[str, float]] = {
|
|
| 111 |
YAHOO_INTERVAL_MAP: Dict[str, str] = {"15min": "15m", "1h": "60m", "4h": "4h"}
|
| 112 |
|
| 113 |
# ================= ЗАГРУЗКА МОДЕЛЕЙ =================
|
| 114 |
-
print(f"🔥 SPACE 19
|
| 115 |
MODELS: Dict[str, Optional[Any]] = {"xgb_daily": None, "xgb_4h": None, "lgb": None}
|
| 116 |
|
| 117 |
if HAS_JOBLIB:
|
|
@@ -143,7 +144,7 @@ def get_next_twelve_key() -> str:
|
|
| 143 |
return key
|
| 144 |
|
| 145 |
session = requests.Session()
|
| 146 |
-
session.headers.update({"User-Agent": "Tomiris-Space19-
|
| 147 |
|
| 148 |
def safe_float(value: Any, default: float = 0.0) -> float:
|
| 149 |
try:
|
|
@@ -203,6 +204,20 @@ def hurst_exponent(series: pd.Series, lags: int = 20) -> float:
|
|
| 203 |
def fetch_google_trends_index(keyword: str) -> float:
|
| 204 |
return 50.0
|
| 205 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
# ================= DATA HUB =================
|
| 207 |
def get_mt5_price_from_hub() -> Dict[str, Any]:
|
| 208 |
global HUB_CACHE
|
|
@@ -405,20 +420,6 @@ def fetch_space_signal(name: str, url: str, endpoint: str = "/consilium") -> Dic
|
|
| 405 |
breaker_fail(name)
|
| 406 |
return {"active": False, "reason": str(e)[:50]}
|
| 407 |
|
| 408 |
-
# ================= ОТПРАВКА В HUB =================
|
| 409 |
-
def send_signal_to_hub(symbol: str, direction: str, confidence: float):
|
| 410 |
-
try:
|
| 411 |
-
session.post(f"{HUB_URL}/signal", json={
|
| 412 |
-
"space": "space_19_sol_master",
|
| 413 |
-
"symbol": symbol,
|
| 414 |
-
"direction": direction,
|
| 415 |
-
"confidence": confidence,
|
| 416 |
-
"raw": json.dumps({"source": "space_19_sol_master"})
|
| 417 |
-
}, timeout=5)
|
| 418 |
-
print(f"📤 {symbol}: {direction} conf={confidence:.3f} отправлен в Hub")
|
| 419 |
-
except Exception as e:
|
| 420 |
-
print(f"Ошибка отправки в Hub: {e}")
|
| 421 |
-
|
| 422 |
# ================= ПОСТРОЕНИЕ ПРИЗНАКОВ =================
|
| 423 |
def build_features_from_mt5(mt5_features: Dict[str, Any]) -> Dict[str, Any]:
|
| 424 |
features: Dict[str, Any] = {}
|
|
@@ -628,7 +629,7 @@ def get_sol_signal() -> Optional[Dict[str, Any]]:
|
|
| 628 |
"signal": {"direction": "WAIT", "confidence": 0.0},
|
| 629 |
"reason": "stress_test_black_swan"
|
| 630 |
}
|
| 631 |
-
send_signal_to_hub(
|
| 632 |
return result
|
| 633 |
|
| 634 |
regime = detect_market_regime(model_features)
|
|
@@ -799,17 +800,80 @@ def get_sol_signal() -> Optional[Dict[str, Any]]:
|
|
| 799 |
"risk": {"sl": sl, "tp": tp, "rr": round(tp_dist/(sl_dist+1e-10), 2) if direction != "WAIT" else 0},
|
| 800 |
"meta": {
|
| 801 |
"latency_ms": latency,
|
| 802 |
-
"model_version": "
|
| 803 |
"features_used": len(model_features) if model_features else 0,
|
| 804 |
"dynamic_weights": comp_weights
|
| 805 |
}
|
| 806 |
}
|
| 807 |
|
| 808 |
-
|
| 809 |
-
send_signal_to_hub(SYMBOL, result["signal"]["direction"], result["signal"]["confidence"])
|
| 810 |
print(f"🥉 SOL/USD: {direction} | conf={confidence:.3f} | ensemble={xgb_prob:.3f} | models={models_used} | regime={regime} | latency={latency}ms")
|
| 811 |
return result
|
| 812 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 813 |
# ================= KEEP-ALIVE =================
|
| 814 |
def keep_alive():
|
| 815 |
while True:
|
|
@@ -818,21 +882,24 @@ def keep_alive():
|
|
| 818 |
requests.get("http://localhost:7860/health", timeout=5)
|
| 819 |
except:
|
| 820 |
pass
|
|
|
|
| 821 |
threading.Thread(target=keep_alive, daemon=True).start()
|
|
|
|
|
|
|
| 822 |
|
| 823 |
# ================= FASTAPI =================
|
| 824 |
-
app = FastAPI(title="TOMIRIS SOL/USD MASTER
|
| 825 |
|
| 826 |
@app.get("/health")
|
| 827 |
async def health():
|
| 828 |
models_loaded = sum(1 for m in ["xgb_daily","xgb_4h","lgb"] if MODELS.get(m) is not None)
|
| 829 |
return {
|
| 830 |
-
"space": "Space 19
|
| 831 |
"status": "operational",
|
| 832 |
"symbol": SYMBOL,
|
| 833 |
"models_loaded": models_loaded,
|
| 834 |
-
"
|
| 835 |
-
"
|
| 836 |
}
|
| 837 |
|
| 838 |
@app.get("/consilium")
|
|
@@ -889,6 +956,6 @@ async def explain():
|
|
| 889 |
}
|
| 890 |
return {"error": "No prediction yet"}
|
| 891 |
|
| 892 |
-
print("🚀 SPACE 19
|
| 893 |
-
print("🥉 Ансамбль daily+4h + Meta SOL Score + Dynamic Weights +
|
| 894 |
print("✅ Готов к бою!")
|
|
|
|
| 1 |
# ============================================
|
| 2 |
+
# 👑 TOMIRIS SPACE 19 v9.0 — SOL/USD MASTER (Hub‑Connected + Auto‑Retrain)
|
| 3 |
# ============================================
|
| 4 |
import os, time, threading, warnings, json, asyncio
|
| 5 |
from typing import Dict, Any, Optional, List, Tuple
|
| 6 |
import numpy as np, pandas as pd
|
| 7 |
import requests
|
| 8 |
+
from datetime import datetime, timedelta
|
| 9 |
from collections import deque
|
| 10 |
from fastapi import FastAPI, Query
|
| 11 |
warnings.filterwarnings('ignore')
|
|
|
|
| 49 |
SPACE_URLS: Dict[str, str] = {
|
| 50 |
"space_17_hub": os.getenv("SPACE17_URL", "https://tomiris-ai-name5-5.hf.space"),
|
| 51 |
"space_18_arbiter": os.getenv("SPACE18_URL", "https://tomiris-ai-name6-6.hf.space"),
|
|
|
|
| 52 |
}
|
| 53 |
|
| 54 |
HUB_URL = SPACE_URLS["space_17_hub"]
|
|
|
|
| 63 |
SYMBOL: str = "SOL/USD"
|
| 64 |
MT5_SYMBOL: str = "SOLUSD"
|
| 65 |
TIMEFRAMES: List[str] = ["15min", "1h", "4h"]
|
| 66 |
+
AUTO_REPORT_INTERVAL = 300 # секунд между авто‑отправками
|
| 67 |
+
RETRAIN_INTERVAL = 30 * 86400 # 30 дней
|
| 68 |
|
| 69 |
try:
|
| 70 |
with open("best_config.json", "r") as f:
|
|
|
|
| 112 |
YAHOO_INTERVAL_MAP: Dict[str, str] = {"15min": "15m", "1h": "60m", "4h": "4h"}
|
| 113 |
|
| 114 |
# ================= ЗАГРУЗКА МОДЕЛЕЙ =================
|
| 115 |
+
print(f"🔥 SPACE 19 v9.0: Загрузка моделей для {SYMBOL}...")
|
| 116 |
MODELS: Dict[str, Optional[Any]] = {"xgb_daily": None, "xgb_4h": None, "lgb": None}
|
| 117 |
|
| 118 |
if HAS_JOBLIB:
|
|
|
|
| 144 |
return key
|
| 145 |
|
| 146 |
session = requests.Session()
|
| 147 |
+
session.headers.update({"User-Agent": "Tomiris-Space19-v9.0"})
|
| 148 |
|
| 149 |
def safe_float(value: Any, default: float = 0.0) -> float:
|
| 150 |
try:
|
|
|
|
| 204 |
def fetch_google_trends_index(keyword: str) -> float:
|
| 205 |
return 50.0
|
| 206 |
|
| 207 |
+
# ================= ОТПРАВКА В HUB =================
|
| 208 |
+
def send_signal_to_hub(direction, confidence):
|
| 209 |
+
try:
|
| 210 |
+
session.post(f"{HUB_URL}/signal", json={
|
| 211 |
+
"space": "space_19_sol_master",
|
| 212 |
+
"symbol": SYMBOL,
|
| 213 |
+
"direction": direction,
|
| 214 |
+
"confidence": confidence,
|
| 215 |
+
"raw": json.dumps({"source": "space_19_sol_master"})
|
| 216 |
+
}, timeout=5)
|
| 217 |
+
print(f"📤 {SYMBOL}: {direction} conf={confidence:.3f} отправлен в Hub")
|
| 218 |
+
except Exception as e:
|
| 219 |
+
print(f"Ошибка отправки в Hub: {e}")
|
| 220 |
+
|
| 221 |
# ================= DATA HUB =================
|
| 222 |
def get_mt5_price_from_hub() -> Dict[str, Any]:
|
| 223 |
global HUB_CACHE
|
|
|
|
| 420 |
breaker_fail(name)
|
| 421 |
return {"active": False, "reason": str(e)[:50]}
|
| 422 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
# ================= ПОСТРОЕНИЕ ПРИЗНАКОВ =================
|
| 424 |
def build_features_from_mt5(mt5_features: Dict[str, Any]) -> Dict[str, Any]:
|
| 425 |
features: Dict[str, Any] = {}
|
|
|
|
| 629 |
"signal": {"direction": "WAIT", "confidence": 0.0},
|
| 630 |
"reason": "stress_test_black_swan"
|
| 631 |
}
|
| 632 |
+
send_signal_to_hub("WAIT", 0.0)
|
| 633 |
return result
|
| 634 |
|
| 635 |
regime = detect_market_regime(model_features)
|
|
|
|
| 800 |
"risk": {"sl": sl, "tp": tp, "rr": round(tp_dist/(sl_dist+1e-10), 2) if direction != "WAIT" else 0},
|
| 801 |
"meta": {
|
| 802 |
"latency_ms": latency,
|
| 803 |
+
"model_version": "v9.0_auto_retrain",
|
| 804 |
"features_used": len(model_features) if model_features else 0,
|
| 805 |
"dynamic_weights": comp_weights
|
| 806 |
}
|
| 807 |
}
|
| 808 |
|
| 809 |
+
send_signal_to_hub(result["signal"]["direction"], result["signal"]["confidence"])
|
|
|
|
| 810 |
print(f"🥉 SOL/USD: {direction} | conf={confidence:.3f} | ensemble={xgb_prob:.3f} | models={models_used} | regime={regime} | latency={latency}ms")
|
| 811 |
return result
|
| 812 |
|
| 813 |
+
# ================= АВТО-ОТПРАВКА =================
|
| 814 |
+
def auto_report():
|
| 815 |
+
while True:
|
| 816 |
+
time.sleep(AUTO_REPORT_INTERVAL)
|
| 817 |
+
try:
|
| 818 |
+
get_sol_signal()
|
| 819 |
+
except Exception as e:
|
| 820 |
+
print(f"Ошибка авто-отправки: {e}")
|
| 821 |
+
|
| 822 |
+
# ================= АВТО-ДООБУЧЕНИЕ =================
|
| 823 |
+
def retrain_models():
|
| 824 |
+
print("🔄 Запуск дообучения моделей SOL...")
|
| 825 |
+
try:
|
| 826 |
+
# Используем дневные данные за последний год через Yahoo (или Hub)
|
| 827 |
+
df = None
|
| 828 |
+
if HAS_YFINANCE:
|
| 829 |
+
yf_data = yf.download("SOL-USD", period="1y", interval="1d", progress=False)
|
| 830 |
+
if not yf_data.empty:
|
| 831 |
+
df = pd.DataFrame({
|
| 832 |
+
'close': yf_data['Close'].values.flatten(),
|
| 833 |
+
'high': yf_data['High'].values.flatten(),
|
| 834 |
+
'low': yf_data['Low'].values.flatten(),
|
| 835 |
+
'open': yf_data['Open'].values.flatten(),
|
| 836 |
+
'volume': yf_data['Volume'].values.flatten()
|
| 837 |
+
}).dropna()
|
| 838 |
+
if df is None or len(df) < 200:
|
| 839 |
+
print("Недостаточно данных для дообучения")
|
| 840 |
+
return
|
| 841 |
+
|
| 842 |
+
features_list = []
|
| 843 |
+
targets = []
|
| 844 |
+
for i in range(100, len(df)-24):
|
| 845 |
+
sub_df = df.iloc[:i+1]
|
| 846 |
+
feats = build_sol_features(sub_df)
|
| 847 |
+
if not feats:
|
| 848 |
+
continue
|
| 849 |
+
features_list.append(feats)
|
| 850 |
+
target = 1 if df["close"].iloc[i+24] > df["close"].iloc[i] else 0
|
| 851 |
+
targets.append(target)
|
| 852 |
+
|
| 853 |
+
if not features_list:
|
| 854 |
+
return
|
| 855 |
+
# Приводим к единому размеру (pad до 200)
|
| 856 |
+
X = np.array([list(f.values())[:200] + [0.0]*(200 - len(f)) for f in features_list])
|
| 857 |
+
y = np.array(targets)
|
| 858 |
+
|
| 859 |
+
for model_name in ["xgb_daily", "xgb_4h"]:
|
| 860 |
+
model = MODELS.get(model_name)
|
| 861 |
+
if model and hasattr(model, 'fit'):
|
| 862 |
+
model.fit(X, y)
|
| 863 |
+
joblib.dump(model, f"{model_name}_retrained.joblib")
|
| 864 |
+
print(f"✅ {model_name} дообучена")
|
| 865 |
+
if MODELS.get("lgb") and hasattr(MODELS["lgb"], 'fit'):
|
| 866 |
+
MODELS["lgb"].fit(X, y)
|
| 867 |
+
joblib.dump(MODELS["lgb"], "lgb_retrained.joblib")
|
| 868 |
+
print("✅ LightGBM дообучена")
|
| 869 |
+
except Exception as e:
|
| 870 |
+
print(f"Ошибка дообучения: {e}")
|
| 871 |
+
|
| 872 |
+
def auto_retrain():
|
| 873 |
+
while True:
|
| 874 |
+
time.sleep(RETRAIN_INTERVAL)
|
| 875 |
+
retrain_models()
|
| 876 |
+
|
| 877 |
# ================= KEEP-ALIVE =================
|
| 878 |
def keep_alive():
|
| 879 |
while True:
|
|
|
|
| 882 |
requests.get("http://localhost:7860/health", timeout=5)
|
| 883 |
except:
|
| 884 |
pass
|
| 885 |
+
|
| 886 |
threading.Thread(target=keep_alive, daemon=True).start()
|
| 887 |
+
threading.Thread(target=auto_report, daemon=True).start()
|
| 888 |
+
threading.Thread(target=auto_retrain, daemon=True).start()
|
| 889 |
|
| 890 |
# ================= FASTAPI =================
|
| 891 |
+
app = FastAPI(title="TOMIRIS SOL/USD MASTER v9.0 Auto-Retrain")
|
| 892 |
|
| 893 |
@app.get("/health")
|
| 894 |
async def health():
|
| 895 |
models_loaded = sum(1 for m in ["xgb_daily","xgb_4h","lgb"] if MODELS.get(m) is not None)
|
| 896 |
return {
|
| 897 |
+
"space": "Space 19 v9.0 Auto-Retrain",
|
| 898 |
"status": "operational",
|
| 899 |
"symbol": SYMBOL,
|
| 900 |
"models_loaded": models_loaded,
|
| 901 |
+
"hub_connected": True,
|
| 902 |
+
"auto_retrain": True
|
| 903 |
}
|
| 904 |
|
| 905 |
@app.get("/consilium")
|
|
|
|
| 956 |
}
|
| 957 |
return {"error": "No prediction yet"}
|
| 958 |
|
| 959 |
+
print("🚀 SPACE 19 v9.0 — SOL/USD MASTER (Hub-Connected + Auto-Retrain) ЗАПУЩЕН!")
|
| 960 |
+
print("🥉 Ансамбль daily+4h + Meta SOL Score + Dynamic Weights + Auto-Retrain")
|
| 961 |
print("✅ Готов к бою!")
|