Spaces:
Runtime error
Runtime error
v1.1: 3 clases + fix yfinance + forzar re-entrenamiento
Browse files- app.py +1 -1
- download_data.py +2 -2
- regime_detector.py +3 -3
- regime_labeler.py +8 -38
- startup.sh +14 -26
app.py
CHANGED
|
@@ -140,7 +140,7 @@ async def get_regime(req: RegimeRequest):
|
|
| 140 |
proba = _regime_model.predict_proba(last_row)[0]
|
| 141 |
regime_id = int(np.argmax(proba))
|
| 142 |
|
| 143 |
-
REGIME_NAMES = {0: "TRENDING", 1: "RANGING", 2: "VOLATILE"
|
| 144 |
|
| 145 |
return RegimeResponse(
|
| 146 |
regime=REGIME_NAMES[regime_id],
|
|
|
|
| 140 |
proba = _regime_model.predict_proba(last_row)[0]
|
| 141 |
regime_id = int(np.argmax(proba))
|
| 142 |
|
| 143 |
+
REGIME_NAMES = {0: "TRENDING", 1: "RANGING", 2: "VOLATILE"}
|
| 144 |
|
| 145 |
return RegimeResponse(
|
| 146 |
regime=REGIME_NAMES[regime_id],
|
download_data.py
CHANGED
|
@@ -353,8 +353,8 @@ def download_macro_data(days: int) -> pd.DataFrame:
|
|
| 353 |
for name, ticker in tickers.items():
|
| 354 |
try:
|
| 355 |
logger.info("📥 Descargando %s (%s)...", name, ticker)
|
| 356 |
-
|
| 357 |
-
|
| 358 |
if not data.empty:
|
| 359 |
frames[name] = data["Close"].rename(f"macro_{name.lower()}")
|
| 360 |
except Exception as e:
|
|
|
|
| 353 |
for name, ticker in tickers.items():
|
| 354 |
try:
|
| 355 |
logger.info("📥 Descargando %s (%s)...", name, ticker)
|
| 356 |
+
tick = yf.Ticker(ticker)
|
| 357 |
+
data = tick.history(start=start_date, interval="1d")
|
| 358 |
if not data.empty:
|
| 359 |
frames[name] = data["Close"].rename(f"macro_{name.lower()}")
|
| 360 |
except Exception as e:
|
regime_detector.py
CHANGED
|
@@ -37,7 +37,7 @@ logger = logging.getLogger("AuroraBrain.Regime")
|
|
| 37 |
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
|
| 38 |
MODELS_DIR = os.path.join(os.path.dirname(__file__), "models")
|
| 39 |
|
| 40 |
-
REGIME_NAMES = {0: "TRENDING", 1: "RANGING", 2: "VOLATILE"
|
| 41 |
|
| 42 |
|
| 43 |
def select_features(df: pd.DataFrame, target_col: str = "regime",
|
|
@@ -116,7 +116,7 @@ def train_regime_model(df: pd.DataFrame, feature_cols: list[str],
|
|
| 116 |
reg_alpha=0.1,
|
| 117 |
reg_lambda=1.0,
|
| 118 |
objective="multi:softprob",
|
| 119 |
-
num_class=
|
| 120 |
eval_metric="mlogloss",
|
| 121 |
random_state=42,
|
| 122 |
n_jobs=-1,
|
|
@@ -159,7 +159,7 @@ def train_regime_model(df: pd.DataFrame, feature_cols: list[str],
|
|
| 159 |
cv_model = xgb.XGBClassifier(
|
| 160 |
n_estimators=200, max_depth=6, learning_rate=0.05,
|
| 161 |
subsample=0.8, colsample_bytree=0.8,
|
| 162 |
-
objective="multi:softprob", num_class=
|
| 163 |
random_state=42, n_jobs=-1,
|
| 164 |
)
|
| 165 |
cv_model.fit(X.iloc[train_idx], y.iloc[train_idx], verbose=False)
|
|
|
|
| 37 |
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
|
| 38 |
MODELS_DIR = os.path.join(os.path.dirname(__file__), "models")
|
| 39 |
|
| 40 |
+
REGIME_NAMES = {0: "TRENDING", 1: "RANGING", 2: "VOLATILE"}
|
| 41 |
|
| 42 |
|
| 43 |
def select_features(df: pd.DataFrame, target_col: str = "regime",
|
|
|
|
| 116 |
reg_alpha=0.1,
|
| 117 |
reg_lambda=1.0,
|
| 118 |
objective="multi:softprob",
|
| 119 |
+
num_class=3,
|
| 120 |
eval_metric="mlogloss",
|
| 121 |
random_state=42,
|
| 122 |
n_jobs=-1,
|
|
|
|
| 159 |
cv_model = xgb.XGBClassifier(
|
| 160 |
n_estimators=200, max_depth=6, learning_rate=0.05,
|
| 161 |
subsample=0.8, colsample_bytree=0.8,
|
| 162 |
+
objective="multi:softprob", num_class=3,
|
| 163 |
random_state=42, n_jobs=-1,
|
| 164 |
)
|
| 165 |
cv_model.fit(X.iloc[train_idx], y.iloc[train_idx], verbose=False)
|
regime_labeler.py
CHANGED
|
@@ -30,12 +30,11 @@ logger = logging.getLogger("AuroraBrain.Labeler")
|
|
| 30 |
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
|
| 31 |
|
| 32 |
# Régimen IDs
|
| 33 |
-
REGIME_TRENDING = 0
|
| 34 |
REGIME_RANGING = 1
|
| 35 |
REGIME_VOLATILE = 2
|
| 36 |
-
REGIME_BREAKOUT = 3
|
| 37 |
|
| 38 |
-
REGIME_NAMES = {0: "TRENDING", 1: "RANGING", 2: "VOLATILE"
|
| 39 |
|
| 40 |
|
| 41 |
def label_regimes(df: pd.DataFrame, horizon: int = 12,
|
|
@@ -68,7 +67,6 @@ def label_regimes(df: pd.DataFrame, horizon: int = 12,
|
|
| 68 |
logger.info("🏷️ Etiquetando regímenes (horizon=%d velas)...", horizon)
|
| 69 |
|
| 70 |
for i in range(n - horizon):
|
| 71 |
-
# Ventana de futuro
|
| 72 |
future_prices = c.iloc[i + 1: i + 1 + horizon]
|
| 73 |
future_volumes = v.iloc[i + 1: i + 1 + horizon]
|
| 74 |
|
|
@@ -79,59 +77,31 @@ def label_regimes(df: pd.DataFrame, horizon: int = 12,
|
|
| 79 |
if current_price <= 0:
|
| 80 |
continue
|
| 81 |
|
| 82 |
-
# Métricas de la ventana futura
|
| 83 |
max_price = future_prices.max()
|
| 84 |
min_price = future_prices.min()
|
| 85 |
end_price = future_prices.iloc[-1]
|
| 86 |
|
| 87 |
-
# Retorno neto
|
| 88 |
return_pct = (end_price - current_price) / current_price * 100
|
| 89 |
|
| 90 |
-
# Máximo drawdown en la ventana
|
| 91 |
running_max = future_prices.cummax()
|
| 92 |
drawdowns = (running_max - future_prices) / running_max * 100
|
| 93 |
max_drawdown = drawdowns.max()
|
| 94 |
|
| 95 |
-
# Máximo rally en la ventana
|
| 96 |
-
running_min = future_prices.cummin()
|
| 97 |
-
rallies = (future_prices - running_min) / running_min * 100
|
| 98 |
-
max_rally = rallies.max()
|
| 99 |
-
|
| 100 |
-
# Rango total
|
| 101 |
price_range = (max_price - min_price) / current_price * 100
|
| 102 |
|
| 103 |
-
#
|
| 104 |
-
|
| 105 |
-
avg_future_vol = future_volumes.mean()
|
| 106 |
-
vol_ratio = avg_future_vol / vol_sma if vol_sma > 0 else 1.0
|
| 107 |
-
|
| 108 |
-
# ── Clasificación (prioridad: VOLATILE > BREAKOUT > TRENDING > RANGING) ──
|
| 109 |
-
|
| 110 |
-
# VOLATILE: drawdown fuerte
|
| 111 |
if max_drawdown >= volatile_dd_threshold:
|
| 112 |
regimes[i] = REGIME_VOLATILE
|
| 113 |
continue
|
| 114 |
|
| 115 |
-
#
|
| 116 |
-
if
|
| 117 |
-
regimes[i] = REGIME_BREAKOUT
|
| 118 |
-
continue
|
| 119 |
-
|
| 120 |
-
# TRENDING: avance sostenido en una dirección
|
| 121 |
-
if abs(return_pct) >= trend_threshold:
|
| 122 |
regimes[i] = REGIME_TRENDING
|
| 123 |
continue
|
| 124 |
|
| 125 |
-
# RANGING:
|
| 126 |
-
|
| 127 |
-
regimes[i] = REGIME_RANGING
|
| 128 |
-
continue
|
| 129 |
-
|
| 130 |
-
# Default para movimientos moderados
|
| 131 |
-
if abs(return_pct) >= trend_threshold * 0.6:
|
| 132 |
-
regimes[i] = REGIME_TRENDING
|
| 133 |
-
else:
|
| 134 |
-
regimes[i] = REGIME_RANGING
|
| 135 |
|
| 136 |
# Últimas `horizon` velas no tienen etiqueta confiable
|
| 137 |
regimes[-horizon:] = -1 # Marcar como desconocido
|
|
|
|
| 30 |
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
|
| 31 |
|
| 32 |
# Régimen IDs
|
| 33 |
+
REGIME_TRENDING = 0 # Incluye BREAKOUT (movimiento fuerte en una dirección)
|
| 34 |
REGIME_RANGING = 1
|
| 35 |
REGIME_VOLATILE = 2
|
|
|
|
| 36 |
|
| 37 |
+
REGIME_NAMES = {0: "TRENDING", 1: "RANGING", 2: "VOLATILE"}
|
| 38 |
|
| 39 |
|
| 40 |
def label_regimes(df: pd.DataFrame, horizon: int = 12,
|
|
|
|
| 67 |
logger.info("🏷️ Etiquetando regímenes (horizon=%d velas)...", horizon)
|
| 68 |
|
| 69 |
for i in range(n - horizon):
|
|
|
|
| 70 |
future_prices = c.iloc[i + 1: i + 1 + horizon]
|
| 71 |
future_volumes = v.iloc[i + 1: i + 1 + horizon]
|
| 72 |
|
|
|
|
| 77 |
if current_price <= 0:
|
| 78 |
continue
|
| 79 |
|
|
|
|
| 80 |
max_price = future_prices.max()
|
| 81 |
min_price = future_prices.min()
|
| 82 |
end_price = future_prices.iloc[-1]
|
| 83 |
|
|
|
|
| 84 |
return_pct = (end_price - current_price) / current_price * 100
|
| 85 |
|
|
|
|
| 86 |
running_max = future_prices.cummax()
|
| 87 |
drawdowns = (running_max - future_prices) / running_max * 100
|
| 88 |
max_drawdown = drawdowns.max()
|
| 89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
price_range = (max_price - min_price) / current_price * 100
|
| 91 |
|
| 92 |
+
# ── Clasificación simplificada: 3 clases ──
|
| 93 |
+
# VOLATILE: drawdown fuerte (prioridad máxima)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
if max_drawdown >= volatile_dd_threshold:
|
| 95 |
regimes[i] = REGIME_VOLATILE
|
| 96 |
continue
|
| 97 |
|
| 98 |
+
# TRENDING: movimiento fuerte en cualquier dirección (incluye breakouts)
|
| 99 |
+
if abs(return_pct) >= trend_threshold or price_range >= breakout_threshold:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
regimes[i] = REGIME_TRENDING
|
| 101 |
continue
|
| 102 |
|
| 103 |
+
# RANGING: todo lo demás
|
| 104 |
+
regimes[i] = REGIME_RANGING
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
# Últimas `horizon` velas no tienen etiqueta confiable
|
| 107 |
regimes[-horizon:] = -1 # Marcar como desconocido
|
startup.sh
CHANGED
|
@@ -3,37 +3,25 @@ echo "========================================="
|
|
| 3 |
echo " 🧠 AURORA BRAIN — Starting Pipeline"
|
| 4 |
echo "========================================="
|
| 5 |
|
| 6 |
-
#
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
|
| 14 |
# Paso 2: Generar features
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
python feature_engine.py --all
|
| 18 |
-
else
|
| 19 |
-
echo "✅ Features ya generadas"
|
| 20 |
-
fi
|
| 21 |
|
| 22 |
-
# Paso 3: Etiquetar regímenes
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
python regime_labeler.py --all
|
| 26 |
-
else
|
| 27 |
-
echo "✅ Regímenes ya etiquetados"
|
| 28 |
-
fi
|
| 29 |
|
| 30 |
# Paso 4: Entrenar modelo
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
python regime_detector.py --symbol BTCUSDT
|
| 34 |
-
else
|
| 35 |
-
echo "✅ Modelo ya entrenado"
|
| 36 |
-
fi
|
| 37 |
|
| 38 |
echo "========================================="
|
| 39 |
echo " 🚀 Levantando API..."
|
|
|
|
| 3 |
echo " 🧠 AURORA BRAIN — Starting Pipeline"
|
| 4 |
echo "========================================="
|
| 5 |
|
| 6 |
+
# Limpiar datos anteriores para re-entrenar con mejoras
|
| 7 |
+
rm -rf data/*.parquet models/*.pkl models/*.json
|
| 8 |
+
echo "🗑️ Datos anteriores limpiados — re-entrenando desde cero"
|
| 9 |
+
|
| 10 |
+
# Paso 1: Descargar datos
|
| 11 |
+
echo "📥 Descargando datos históricos..."
|
| 12 |
+
python download_data.py --symbols BTCUSDT ETHUSDT SOLUSDT --days 1825
|
| 13 |
|
| 14 |
# Paso 2: Generar features
|
| 15 |
+
echo "🔧 Generando features..."
|
| 16 |
+
python feature_engine.py --all
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
+
# Paso 3: Etiquetar regímenes (ahora 3 clases: TRENDING/RANGING/VOLATILE)
|
| 19 |
+
echo "🏷️ Etiquetando regímenes..."
|
| 20 |
+
python regime_labeler.py --all
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
# Paso 4: Entrenar modelo
|
| 23 |
+
echo "🎯 Entrenando detector de régimen..."
|
| 24 |
+
python regime_detector.py --symbol BTCUSDT
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
echo "========================================="
|
| 27 |
echo " 🚀 Levantando API..."
|