diff --git a/_notes.md b/_notes.md index 572368e..575ebb7 100644 --- a/_notes.md +++ b/_notes.md @@ -2,6 +2,11 @@ https://github.com/mexcdevelop/mexc-api-sdk/blob/main/README.md#test-new-order python mexc_tick_visualizer.py --symbol BTC/USDT --interval 1.0 --candle 60 +python main.py --mode live --symbol ETH/USDT --timeframe 1m --use-websocket + +python main.py --mode live --symbol BTC/USDT --timeframe 1m --use-websocket --dashboard +# http://localhost:8060 + & 'C:\Users\popov\miniforge3\python.exe' 'c:\Users\popov\.cursor\extensions\ms-python.debugpy-2024.6.0-win32-x64\bundled\libs\debugpy\adapter/../..\debugpy\launcher' '51766' '--' 'main.py' '--mode' 'live' '--demo' 'false' '--symbol' 'ETH/USDT' '--timeframe' '1m' '--leverage' '50' diff --git a/main.py b/main.py index 56cbbc9..d173aa1 100644 --- a/main.py +++ b/main.py @@ -48,6 +48,22 @@ logging.basicConfig( ) logger = logging.getLogger("trading_bot") +# Look for WebSocket specific logger +websocket_logger = logging.getLogger('websocket') # or similar name +websocket_logger.setLevel(logging.INFO) # Change this from DEBUG to INFO + +# Add this somewhere after the logger is defined +class WebSocketFilter(logging.Filter): + def filter(self, record): + # Filter out DEBUG messages from WebSocket-related modules + if record.levelno == logging.DEBUG and ('websocket' in record.name or + 'protocol' in record.name or + 'realtime' in record.name): + return False + return True + +logger.addFilter(WebSocketFilter()) + # Load environment variables load_dotenv() MEXC_API_KEY = os.getenv('MEXC_API_KEY') @@ -287,8 +303,15 @@ class PricePredictionModel(nn.Module): return total_loss / epochs class TradingEnvironment: + """Trading environment for reinforcement learning with enhanced features""" def __init__(self, initial_balance=INITIAL_BALANCE, window_size=30, demo=True): - """Initialize the trading environment""" + """Initialize trading environment + + Args: + initial_balance: Starting account balance + window_size: Number of candles in the state window + demo: Whether to run in demo mode + """ self.initial_balance = initial_balance self.balance = initial_balance self.window_size = window_size @@ -310,6 +333,13 @@ class TradingEnvironment: self.current_step = 0 self.current_price = 0 + # Risk management parameters (adjusted for more aggressive trading) + self.stop_loss_pct = STOP_LOSS_PERCENT * 0.8 # Tighter stop loss (80% of original) + self.take_profit_pct = TAKE_PROFIT_PERCENT * 1.5 # Higher take profit (150% of original) + self.trailing_stop_activated = False + self.trailing_stop_distance = 0 + self.max_position_size_pct = 0.8 # Use up to 80% of balance for position size + # For tracking signals for visualization self.trade_signals = [] @@ -473,6 +503,9 @@ class TradingEnvironment: } return next_state, 0, done, info + # Adapt trading parameters to current market conditions + self.adapt_trading_parameters_to_market() + # Store current price before taking action self.current_price = self.data[self.current_step]['close'] @@ -985,8 +1018,9 @@ class TradingEnvironment: self.position = 'long' self.entry_price = self.current_price self.position_size = self.calculate_position_size() - self.stop_loss = self.entry_price * (1 - STOP_LOSS_PERCENT/100) - self.take_profit = self.entry_price * (1 + TAKE_PROFIT_PERCENT/100) + # Use the adjusted risk parameters + self.stop_loss = self.entry_price * (1 - self.stop_loss_pct/100) + self.take_profit = self.entry_price * (1 + self.take_profit_pct/100) # Check if this is an optimal buy point (bottom) current_idx = len(self.features['price']) - 1 @@ -1059,8 +1093,8 @@ class TradingEnvironment: self.entry_price = self.current_price self.entry_index = len(self.features['price']) - 1 self.position_size = self.calculate_position_size() - self.stop_loss = self.entry_price * (1 - STOP_LOSS_PERCENT/100) - self.take_profit = self.entry_price * (1 + TAKE_PROFIT_PERCENT/100) + self.stop_loss = self.entry_price * (1 - self.stop_loss_pct/100) + self.take_profit = self.entry_price * (1 + self.take_profit_pct/100) # Check if this is an optimal buy point if hasattr(self, 'optimal_bottoms') and self.entry_index in self.optimal_bottoms: @@ -1074,8 +1108,9 @@ class TradingEnvironment: self.position = 'short' self.entry_price = self.current_price self.position_size = self.calculate_position_size() - self.stop_loss = self.entry_price * (1 + STOP_LOSS_PERCENT/100) - self.take_profit = self.entry_price * (1 - TAKE_PROFIT_PERCENT/100) + # Use the adjusted risk parameters + self.stop_loss = self.entry_price * (1 + self.stop_loss_pct/100) + self.take_profit = self.entry_price * (1 - self.take_profit_pct/100) # Check if this is an optimal sell point (top) current_idx = len(self.features['price']) - 1 @@ -1138,8 +1173,8 @@ class TradingEnvironment: self.position = 'short' self.entry_price = self.current_price self.position_size = self.calculate_position_size() - self.stop_loss = self.entry_price * (1 + STOP_LOSS_PERCENT/100) - self.take_profit = self.entry_price * (1 - TAKE_PROFIT_PERCENT/100) + self.stop_loss = self.entry_price * (1 + self.stop_loss_pct/100) + self.take_profit = self.entry_price * (1 - self.take_profit_pct/100) # Check if this is an optimal sell point current_idx = len(self.features['price']) - 1 @@ -1387,26 +1422,19 @@ class TradingEnvironment: logger.info(f"Identified {len(bottoms)} optimal buy points and {len(tops)} optimal sell points") def calculate_position_size(self): - """Calculate position size based on current balance and risk parameters""" - # Use a fixed percentage of balance for each trade - risk_percent = 5.0 # Risk 5% of balance per trade + """Calculate position size based on current balance and risk parameters - # Calculate position size with leverage - position_size = self.balance * (risk_percent / 100) * MAX_LEVERAGE + Returns: + float: Position size in quote currency + """ + # More aggressive position sizing + risk_amount = self.balance * (self.max_position_size_pct * random.uniform(0.7, 1.0)) - # Apply a safety factor to avoid liquidation - safety_factor = 0.8 - position_size *= safety_factor + # In futures trading, adjust for leverage + if hasattr(self, 'leverage') and self.leverage > 1: + risk_amount = min(risk_amount * self.leverage, self.balance * 10) # Limit max risk - # Ensure minimum position size - min_position = 10.0 # Minimum position size in USD - position_size = max(position_size, min(min_position, self.balance * 0.5)) - - # Ensure position size doesn't exceed balance * leverage - max_position = self.balance * MAX_LEVERAGE - position_size = min(position_size, max_position) - - return position_size + return risk_amount def calculate_fees(self, position_size): """Calculate trading fees for a given position size""" @@ -1639,6 +1667,105 @@ class TradingEnvironment: logger.error(f"Trade execution failed: {e}") return None + def is_volatile_market(self): + """Detect if the market is currently in a volatile state with significant price movements + + Returns: + bool: True if market is volatile, False otherwise + """ + if len(self.features['price']) < 20: + return False + + # Calculate recent price volatility + recent_prices = self.features['price'][-20:] + returns = np.diff(recent_prices) / recent_prices[:-1] + volatility = np.std(returns) * 100 # Convert to percentage + + # Calculate volume increase + recent_volumes = self.features['volume'][-10:] + avg_volume_prev = np.mean(self.features['volume'][-20:-10]) + avg_volume_recent = np.mean(recent_volumes) + volume_increase = avg_volume_recent / avg_volume_prev if avg_volume_prev > 0 else 1.0 + + # Calculate ATR if available + atr_high = False + if len(self.features['atr']) > 5: + recent_atr = self.features['atr'][-1] + avg_atr = np.mean(self.features['atr'][-20:-1]) + atr_ratio = recent_atr / avg_atr if avg_atr > 0 else 1.0 + atr_high = atr_ratio > 1.5 + + # Check if price moved significantly in either direction recently + price_range_percent = (max(recent_prices) - min(recent_prices)) / min(recent_prices) * 100 + + # Market is volatile if any of these conditions are met + volatile = ( + volatility > 0.5 or # High standard deviation of returns + volume_increase > 1.8 or # Volume spike + price_range_percent > 1.5 or # Large price range + atr_high # High ATR relative to average + ) + + if volatile: + logger.info(f"Volatile market detected - Volatility: {volatility:.2f}%, Volume increase: {volume_increase:.2f}x, Price range: {price_range_percent:.2f}%") + + return volatile + + def adapt_trading_parameters_to_market(self): + """Dynamically adjust trading parameters based on market conditions + + Returns: + None + """ + # Check market conditions + is_volatile = self.is_volatile_market() + is_trending_up = self.is_uptrend() + is_trending_down = self.is_downtrend() + + # Base parameters + base_stop_loss = STOP_LOSS_PERCENT + base_take_profit = TAKE_PROFIT_PERCENT + base_position_size = 0.5 # 50% of max + + # Adjust based on market conditions + if is_volatile: + # In volatile markets, use tighter stops but higher take profits + self.stop_loss_pct = base_stop_loss * 0.7 # Tighter stop + self.take_profit_pct = base_take_profit * 1.8 # Higher target + self.max_position_size_pct = base_position_size * 1.3 # More aggressive sizing + + elif is_trending_up: + # In uptrends, use looser stops for longs, tighter for shorts + if self.position == 'long' or self.position == 'flat': + self.stop_loss_pct = base_stop_loss * 0.9 + self.take_profit_pct = base_take_profit * 1.6 + self.max_position_size_pct = base_position_size * 1.2 + else: + # More conservative for shorts in uptrend + self.stop_loss_pct = base_stop_loss * 0.7 + self.take_profit_pct = base_take_profit * 1.2 + self.max_position_size_pct = base_position_size * 0.8 + + elif is_trending_down: + # In downtrends, use looser stops for shorts, tighter for longs + if self.position == 'short' or self.position == 'flat': + self.stop_loss_pct = base_stop_loss * 0.9 + self.take_profit_pct = base_take_profit * 1.6 + self.max_position_size_pct = base_position_size * 1.2 + else: + # More conservative for longs in downtrend + self.stop_loss_pct = base_stop_loss * 0.7 + self.take_profit_pct = base_take_profit * 1.2 + self.max_position_size_pct = base_position_size * 0.8 + else: + # In sideways/uncertain markets, be more balanced + self.stop_loss_pct = base_stop_loss * 0.8 + self.take_profit_pct = base_take_profit * 1.3 + self.max_position_size_pct = base_position_size + + # Log the adaptation + logger.debug(f"Adapted trading parameters - Stop loss: {self.stop_loss_pct:.2f}%, Take profit: {self.take_profit_pct:.2f}%, Max position size: {self.max_position_size_pct*100:.1f}%") + # Ensure GPU usage if available def get_device(): """Get the best available device (CUDA GPU or CPU)""" @@ -2787,6 +2914,12 @@ async def process_websocket_ticks(websocket, env, agent=None, demo=True, timefra trades_count = 0 step_counter = 0 + # For tracking sudden price movements + last_prices = [] + price_movement_threshold = 0.5 # 0.5% movement threshold + volume_spike_threshold = 2.0 # 2x average volume + recent_volumes = [] + try: logger.info("Starting WebSocket tick processing...") @@ -2808,6 +2941,50 @@ async def process_websocket_ticks(websocket, env, agent=None, demo=True, timefra logger.warning(f"Invalid tick data received: {tick}") continue + # Track price movement for significant changes + last_prices.append(price) + if len(last_prices) > 20: + last_prices.pop(0) + + # Track volumes for volume spikes + recent_volumes.append(volume) + if len(recent_volumes) > 20: + recent_volumes.pop(0) + + # Check for significant price movement + if len(last_prices) >= 5: + price_change_pct = abs(price - last_prices[0]) / last_prices[0] * 100 + avg_volume = np.mean(recent_volumes[:-1]) if len(recent_volumes) > 1 else volume + volume_ratio = volume / avg_volume if avg_volume > 0 else 1.0 + + # Log significant movements + if price_change_pct > price_movement_threshold: + logger.info(f"Significant price movement detected: {price_change_pct:.2f}% change") + + if volume_ratio > volume_spike_threshold: + logger.info(f"Volume spike detected: {volume_ratio:.2f}x average volume") + + # Force more frequent trading decisions on significant movements + if (price_change_pct > price_movement_threshold or volume_ratio > volume_spike_threshold) and agent is not None and current_candle is not None: + # Create a temporary candle with current data + temp_candle = current_candle.copy() + temp_candle['close'] = price # Update with latest price + + # Add to environment temporarily + env.add_data(temp_candle) + + # Get action + state = env.get_state() + # Force exploitation (no exploration) during significant movements + action = agent.select_action(state, training=False) + + # Execute action in environment + next_state, reward, done, info = env.step(action) + + # Log trading activity + action_name = "HOLD" if action == 0 else "BUY" if action == 1 else "SELL" if action == 2 else "CLOSE" + logger.info(f"Significant movement action: {action_name}, Price: ${price:.2f}, Balance: ${env.balance:.2f}") + # Convert timestamp to datetime tick_time = datetime.datetime.fromtimestamp(timestamp / 1000) diff --git a/realtime.py b/realtime.py index cf63db4..60ee7eb 100644 --- a/realtime.py +++ b/realtime.py @@ -20,7 +20,7 @@ from datetime import datetime, timedelta # Configure logging with more detailed format logging.basicConfig( - level=logging.DEBUG, # Changed to DEBUG for more detailed logs + level=logging.INFO, # Changed to DEBUG for more detailed logs format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s', handlers=[ logging.StreamHandler(),