Remove deprecated training scripts and Systemd service files
- Deleted `install_cron.sh`, `setup_schedule.sh`, and `train_daily.sh` as part of the transition to a new scheduling mechanism. - Removed associated Systemd service and timer files for daily model training. - Updated `live_regime_strategy.py` and `main.py` to reflect changes in model training and scheduling logic. - Adjusted `regime_strategy.py` to align with new target calculation methods and updated optimal parameters. - Enhanced `regime_detection.py` to incorporate path-dependent labeling for target calculations.
This commit is contained in:
@@ -6,6 +6,7 @@ Uses a pre-trained ML model or trains on historical data.
|
||||
"""
|
||||
import logging
|
||||
import pickle
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
@@ -39,8 +40,9 @@ class LiveRegimeStrategy:
|
||||
self.paths = path_config
|
||||
self.model: Optional[RandomForestClassifier] = None
|
||||
self.feature_cols: Optional[list] = None
|
||||
self.horizon: int = 102 # Default horizon
|
||||
self.horizon: int = 54 # Default horizon
|
||||
self._last_model_load_time: float = 0.0
|
||||
self._last_train_time: float = 0.0
|
||||
self._load_or_train_model()
|
||||
|
||||
def reload_model_if_changed(self) -> None:
|
||||
@@ -72,6 +74,13 @@ class LiveRegimeStrategy:
|
||||
logger.info(f"Loaded model from {self.paths.model_path} (horizon={self.horizon})")
|
||||
else:
|
||||
logger.info(f"Loaded model from {self.paths.model_path} (default horizon={self.horizon})")
|
||||
|
||||
# Load timestamp if available
|
||||
if 'timestamp' in saved:
|
||||
self._last_train_time = saved['timestamp']
|
||||
else:
|
||||
self._last_train_time = self._last_model_load_time
|
||||
|
||||
return
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not load model: {e}")
|
||||
@@ -88,12 +97,20 @@ class LiveRegimeStrategy:
|
||||
pickle.dump({
|
||||
'model': self.model,
|
||||
'feature_cols': self.feature_cols,
|
||||
'metrics': {'horizon': self.horizon} # Save horizon
|
||||
'metrics': {'horizon': self.horizon}, # Save horizon
|
||||
'timestamp': time.time()
|
||||
}, f)
|
||||
logger.info(f"Saved model to {self.paths.model_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"Could not save model: {e}")
|
||||
|
||||
def check_retrain(self, features: pd.DataFrame) -> None:
|
||||
"""Check if model needs retraining (older than 24h)."""
|
||||
if time.time() - self._last_train_time > 24 * 3600:
|
||||
logger.info("Model is older than 24h. Retraining...")
|
||||
self.train_model(features)
|
||||
self._last_train_time = time.time()
|
||||
|
||||
def train_model(self, features: pd.DataFrame) -> None:
|
||||
"""
|
||||
Train the Random Forest model on historical data.
|
||||
@@ -106,18 +123,61 @@ class LiveRegimeStrategy:
|
||||
z_thresh = self.config.z_entry_threshold
|
||||
horizon = self.horizon
|
||||
profit_target = 0.005 # 0.5% profit threshold
|
||||
stop_loss_pct = self.config.stop_loss_pct
|
||||
|
||||
# Define targets
|
||||
future_min = features['spread'].rolling(window=horizon).min().shift(-horizon)
|
||||
future_max = features['spread'].rolling(window=horizon).max().shift(-horizon)
|
||||
# Calculate targets path-dependently
|
||||
spread = features['spread'].values
|
||||
z_score = features['z_score'].values
|
||||
n = len(spread)
|
||||
|
||||
target_short = features['spread'] * (1 - profit_target)
|
||||
target_long = features['spread'] * (1 + profit_target)
|
||||
targets = np.zeros(n, dtype=int)
|
||||
|
||||
success_short = (features['z_score'] > z_thresh) & (future_min < target_short)
|
||||
success_long = (features['z_score'] < -z_thresh) & (future_max > target_long)
|
||||
candidates = np.where((z_score > z_thresh) | (z_score < -z_thresh))[0]
|
||||
|
||||
targets = np.select([success_short, success_long], [1, 1], default=0)
|
||||
for i in candidates:
|
||||
if i + horizon >= n:
|
||||
continue
|
||||
|
||||
entry_price = spread[i]
|
||||
future_prices = spread[i+1 : i+1+horizon]
|
||||
|
||||
if z_score[i] > z_thresh: # Short
|
||||
target_price = entry_price * (1 - profit_target)
|
||||
stop_price = entry_price * (1 + stop_loss_pct)
|
||||
|
||||
hit_tp = future_prices <= target_price
|
||||
hit_sl = future_prices >= stop_price
|
||||
|
||||
if not np.any(hit_tp):
|
||||
targets[i] = 0
|
||||
elif not np.any(hit_sl):
|
||||
targets[i] = 1
|
||||
else:
|
||||
first_tp_idx = np.argmax(hit_tp)
|
||||
first_sl_idx = np.argmax(hit_sl)
|
||||
if first_tp_idx < first_sl_idx:
|
||||
targets[i] = 1
|
||||
else:
|
||||
targets[i] = 0
|
||||
|
||||
else: # Long
|
||||
target_price = entry_price * (1 + profit_target)
|
||||
stop_price = entry_price * (1 - stop_loss_pct)
|
||||
|
||||
hit_tp = future_prices >= target_price
|
||||
hit_sl = future_prices <= stop_price
|
||||
|
||||
if not np.any(hit_tp):
|
||||
targets[i] = 0
|
||||
elif not np.any(hit_sl):
|
||||
targets[i] = 1
|
||||
else:
|
||||
first_tp_idx = np.argmax(hit_tp)
|
||||
first_sl_idx = np.argmax(hit_sl)
|
||||
if first_tp_idx < first_sl_idx:
|
||||
targets[i] = 1
|
||||
else:
|
||||
targets[i] = 0
|
||||
|
||||
# Exclude non-feature columns
|
||||
exclude = ['spread', 'btc_close', 'eth_close', 'eth_volume']
|
||||
@@ -127,8 +187,10 @@ class LiveRegimeStrategy:
|
||||
X = features[self.feature_cols].fillna(0)
|
||||
X = X.replace([np.inf, -np.inf], 0)
|
||||
|
||||
# Remove rows with invalid targets
|
||||
valid_mask = ~np.isnan(targets) & future_min.notna().values & future_max.notna().values
|
||||
# Use rows where we had enough data to look ahead
|
||||
valid_mask = np.zeros(n, dtype=bool)
|
||||
valid_mask[:n-horizon] = True
|
||||
|
||||
X_clean = X[valid_mask]
|
||||
y_clean = targets[valid_mask]
|
||||
|
||||
@@ -152,7 +214,8 @@ class LiveRegimeStrategy:
|
||||
def generate_signal(
|
||||
self,
|
||||
features: pd.DataFrame,
|
||||
current_funding: dict
|
||||
current_funding: dict,
|
||||
position_side: Optional[str] = None
|
||||
) -> dict:
|
||||
"""
|
||||
Generate trading signal from latest features.
|
||||
@@ -160,10 +223,14 @@ class LiveRegimeStrategy:
|
||||
Args:
|
||||
features: DataFrame with calculated features
|
||||
current_funding: Dictionary with funding rate data
|
||||
position_side: Current position side ('long', 'short', or None)
|
||||
|
||||
Returns:
|
||||
Signal dictionary with action, side, confidence, etc.
|
||||
"""
|
||||
# Check if retraining is needed
|
||||
self.check_retrain(features)
|
||||
|
||||
if self.model is None:
|
||||
# Train model if not available
|
||||
if len(features) >= 200:
|
||||
@@ -233,12 +300,17 @@ class LiveRegimeStrategy:
|
||||
signal['action'] = 'hold'
|
||||
signal['reason'] = f'funding_filter_blocked_short (funding={btc_funding:.4f})'
|
||||
|
||||
# Check for exit conditions (mean reversion complete)
|
||||
if signal['action'] == 'hold':
|
||||
# Z-score crossed back through 0
|
||||
if abs(z_score) < 0.3:
|
||||
signal['action'] = 'check_exit'
|
||||
signal['reason'] = f'z_score_reverted_to_mean ({z_score:.2f})'
|
||||
# Check for exit conditions (Overshoot Logic)
|
||||
if signal['action'] == 'hold' and position_side:
|
||||
# Overshoot Logic
|
||||
# If Long, exit if Z > 0.5 (Reverted past 0 to +0.5)
|
||||
if position_side == 'long' and z_score > 0.5:
|
||||
signal['action'] = 'check_exit'
|
||||
signal['reason'] = f'overshoot_exit_long (z={z_score:.2f} > 0.5)'
|
||||
# If Short, exit if Z < -0.5 (Reverted past 0 to -0.5)
|
||||
elif position_side == 'short' and z_score < -0.5:
|
||||
signal['action'] = 'check_exit'
|
||||
signal['reason'] = f'overshoot_exit_short (z={z_score:.2f} < -0.5)'
|
||||
|
||||
logger.info(
|
||||
f"Signal: {signal['action']} {signal['side'] or ''} "
|
||||
|
||||
@@ -206,11 +206,16 @@ class LiveTradingBot:
|
||||
# 3. Sync with exchange positions
|
||||
self.position_manager.sync_with_exchange()
|
||||
|
||||
# Get current position side for signal generation
|
||||
symbol = self.trading_config.eth_symbol
|
||||
position = self.position_manager.get_position_for_symbol(symbol)
|
||||
position_side = position.side if position else None
|
||||
|
||||
# 4. Get current funding rates
|
||||
funding = self.data_feed.get_current_funding_rates()
|
||||
|
||||
# 5. Generate trading signal
|
||||
sig = self.strategy.generate_signal(features, funding)
|
||||
sig = self.strategy.generate_signal(features, funding, position_side=position_side)
|
||||
|
||||
# 6. Update shared state with strategy info
|
||||
self._update_strategy_state(sig, funding)
|
||||
|
||||
Reference in New Issue
Block a user