more quiet logging
This commit is contained in:
parent
1a1c410922
commit
dc532f8795
@ -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 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'
|
& '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'
|
||||||
|
|
||||||
|
229
main.py
229
main.py
@ -48,6 +48,22 @@ logging.basicConfig(
|
|||||||
)
|
)
|
||||||
logger = logging.getLogger("trading_bot")
|
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 environment variables
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
MEXC_API_KEY = os.getenv('MEXC_API_KEY')
|
MEXC_API_KEY = os.getenv('MEXC_API_KEY')
|
||||||
@ -287,8 +303,15 @@ class PricePredictionModel(nn.Module):
|
|||||||
return total_loss / epochs
|
return total_loss / epochs
|
||||||
|
|
||||||
class TradingEnvironment:
|
class TradingEnvironment:
|
||||||
|
"""Trading environment for reinforcement learning with enhanced features"""
|
||||||
def __init__(self, initial_balance=INITIAL_BALANCE, window_size=30, demo=True):
|
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.initial_balance = initial_balance
|
||||||
self.balance = initial_balance
|
self.balance = initial_balance
|
||||||
self.window_size = window_size
|
self.window_size = window_size
|
||||||
@ -310,6 +333,13 @@ class TradingEnvironment:
|
|||||||
self.current_step = 0
|
self.current_step = 0
|
||||||
self.current_price = 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
|
# For tracking signals for visualization
|
||||||
self.trade_signals = []
|
self.trade_signals = []
|
||||||
|
|
||||||
@ -473,6 +503,9 @@ class TradingEnvironment:
|
|||||||
}
|
}
|
||||||
return next_state, 0, done, info
|
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
|
# Store current price before taking action
|
||||||
self.current_price = self.data[self.current_step]['close']
|
self.current_price = self.data[self.current_step]['close']
|
||||||
|
|
||||||
@ -985,8 +1018,9 @@ class TradingEnvironment:
|
|||||||
self.position = 'long'
|
self.position = 'long'
|
||||||
self.entry_price = self.current_price
|
self.entry_price = self.current_price
|
||||||
self.position_size = self.calculate_position_size()
|
self.position_size = self.calculate_position_size()
|
||||||
self.stop_loss = self.entry_price * (1 - STOP_LOSS_PERCENT/100)
|
# Use the adjusted risk parameters
|
||||||
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 (bottom)
|
# Check if this is an optimal buy point (bottom)
|
||||||
current_idx = len(self.features['price']) - 1
|
current_idx = len(self.features['price']) - 1
|
||||||
@ -1059,8 +1093,8 @@ class TradingEnvironment:
|
|||||||
self.entry_price = self.current_price
|
self.entry_price = self.current_price
|
||||||
self.entry_index = len(self.features['price']) - 1
|
self.entry_index = len(self.features['price']) - 1
|
||||||
self.position_size = self.calculate_position_size()
|
self.position_size = self.calculate_position_size()
|
||||||
self.stop_loss = self.entry_price * (1 - STOP_LOSS_PERCENT/100)
|
self.stop_loss = self.entry_price * (1 - self.stop_loss_pct/100)
|
||||||
self.take_profit = self.entry_price * (1 + TAKE_PROFIT_PERCENT/100)
|
self.take_profit = self.entry_price * (1 + self.take_profit_pct/100)
|
||||||
|
|
||||||
# Check if this is an optimal buy point
|
# Check if this is an optimal buy point
|
||||||
if hasattr(self, 'optimal_bottoms') and self.entry_index in self.optimal_bottoms:
|
if hasattr(self, 'optimal_bottoms') and self.entry_index in self.optimal_bottoms:
|
||||||
@ -1074,8 +1108,9 @@ class TradingEnvironment:
|
|||||||
self.position = 'short'
|
self.position = 'short'
|
||||||
self.entry_price = self.current_price
|
self.entry_price = self.current_price
|
||||||
self.position_size = self.calculate_position_size()
|
self.position_size = self.calculate_position_size()
|
||||||
self.stop_loss = self.entry_price * (1 + STOP_LOSS_PERCENT/100)
|
# Use the adjusted risk parameters
|
||||||
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 (top)
|
# Check if this is an optimal sell point (top)
|
||||||
current_idx = len(self.features['price']) - 1
|
current_idx = len(self.features['price']) - 1
|
||||||
@ -1138,8 +1173,8 @@ class TradingEnvironment:
|
|||||||
self.position = 'short'
|
self.position = 'short'
|
||||||
self.entry_price = self.current_price
|
self.entry_price = self.current_price
|
||||||
self.position_size = self.calculate_position_size()
|
self.position_size = self.calculate_position_size()
|
||||||
self.stop_loss = self.entry_price * (1 + STOP_LOSS_PERCENT/100)
|
self.stop_loss = self.entry_price * (1 + self.stop_loss_pct/100)
|
||||||
self.take_profit = self.entry_price * (1 - TAKE_PROFIT_PERCENT/100)
|
self.take_profit = self.entry_price * (1 - self.take_profit_pct/100)
|
||||||
|
|
||||||
# Check if this is an optimal sell point
|
# Check if this is an optimal sell point
|
||||||
current_idx = len(self.features['price']) - 1
|
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")
|
logger.info(f"Identified {len(bottoms)} optimal buy points and {len(tops)} optimal sell points")
|
||||||
|
|
||||||
def calculate_position_size(self):
|
def calculate_position_size(self):
|
||||||
"""Calculate position size based on current balance and risk parameters"""
|
"""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 with leverage
|
Returns:
|
||||||
position_size = self.balance * (risk_percent / 100) * MAX_LEVERAGE
|
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
|
# In futures trading, adjust for leverage
|
||||||
safety_factor = 0.8
|
if hasattr(self, 'leverage') and self.leverage > 1:
|
||||||
position_size *= safety_factor
|
risk_amount = min(risk_amount * self.leverage, self.balance * 10) # Limit max risk
|
||||||
|
|
||||||
# Ensure minimum position size
|
return risk_amount
|
||||||
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
|
|
||||||
|
|
||||||
def calculate_fees(self, position_size):
|
def calculate_fees(self, position_size):
|
||||||
"""Calculate trading fees for a given position size"""
|
"""Calculate trading fees for a given position size"""
|
||||||
@ -1639,6 +1667,105 @@ class TradingEnvironment:
|
|||||||
logger.error(f"Trade execution failed: {e}")
|
logger.error(f"Trade execution failed: {e}")
|
||||||
return None
|
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
|
# Ensure GPU usage if available
|
||||||
def get_device():
|
def get_device():
|
||||||
"""Get the best available device (CUDA GPU or CPU)"""
|
"""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
|
trades_count = 0
|
||||||
step_counter = 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:
|
try:
|
||||||
logger.info("Starting WebSocket tick processing...")
|
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}")
|
logger.warning(f"Invalid tick data received: {tick}")
|
||||||
continue
|
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
|
# Convert timestamp to datetime
|
||||||
tick_time = datetime.datetime.fromtimestamp(timestamp / 1000)
|
tick_time = datetime.datetime.fromtimestamp(timestamp / 1000)
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ from datetime import datetime, timedelta
|
|||||||
|
|
||||||
# Configure logging with more detailed format
|
# Configure logging with more detailed format
|
||||||
logging.basicConfig(
|
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',
|
format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s',
|
||||||
handlers=[
|
handlers=[
|
||||||
logging.StreamHandler(),
|
logging.StreamHandler(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user