redradios commited on
Commit
edeff07
·
1 Parent(s): b6e771c

v1.1: 3 clases + fix yfinance + forzar re-entrenamiento

Browse files
Files changed (5) hide show
  1. app.py +1 -1
  2. download_data.py +2 -2
  3. regime_detector.py +3 -3
  4. regime_labeler.py +8 -38
  5. 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", 3: "BREAKOUT"}
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
- data = yf.download(ticker, start=start_date, interval="1d",
357
- progress=False, auto_adjust=True)
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", 3: "BREAKOUT"}
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=4,
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=4,
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", 3: "BREAKOUT"}
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
- # Volumen promedio futuro vs histórico
104
- vol_sma = df["f_vol_sma_20"].iloc[i] if "f_vol_sma_20" in df.columns else v.iloc[max(0, i-20):i].mean()
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
- # BREAKOUT: movimiento grande + volumen alto
116
- if price_range >= breakout_threshold and vol_ratio >= breakout_vol_mult:
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: precio se quedó en un rango estrecho
126
- if price_range <= range_threshold:
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
- # Paso 1: Descargar datos (si no existen)
7
- if [ ! -f "data/klines_BTCUSDT_4h.parquet" ]; then
8
- echo "📥 Descargando datos históricos..."
9
- python download_data.py --symbols BTCUSDT ETHUSDT SOLUSDT --days 1825
10
- else
11
- echo "✅ Datos ya descargados"
12
- fi
13
 
14
  # Paso 2: Generar features
15
- if [ ! -f "data/features_BTCUSDT_4h.parquet" ]; then
16
- echo "🔧 Generando features..."
17
- python feature_engine.py --all
18
- else
19
- echo "✅ Features ya generadas"
20
- fi
21
 
22
- # Paso 3: Etiquetar regímenes
23
- if [ ! -f "data/labeled_BTCUSDT_4h.parquet" ]; then
24
- echo "🏷️ Etiquetando regímenes..."
25
- python regime_labeler.py --all
26
- else
27
- echo "✅ Regímenes ya etiquetados"
28
- fi
29
 
30
  # Paso 4: Entrenar modelo
31
- if [ ! -f "models/regime_model.pkl" ]; then
32
- echo "🎯 Entrenando detector de régimen..."
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..."