From 0c445435d0f4aba9364ec322fd35c5d6ac3d4147 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Sat, 24 May 2025 01:15:16 +0300 Subject: [PATCH] working charts again!!! --- .../data_interface.cpython-312.pyc | Bin 29057 -> 29070 bytes dataprovider_realtime.py | 296 +++++++++++++++--- realtime_chart.log | 121 +++++++ test_chart_data.py | 153 +++++++++ train_rl_with_realtime.py | 7 +- 5 files changed, 539 insertions(+), 38 deletions(-) create mode 100644 test_chart_data.py diff --git a/NN/utils/__pycache__/data_interface.cpython-312.pyc b/NN/utils/__pycache__/data_interface.cpython-312.pyc index d70ac63b844e0650bdcbcf4450ea649057a85cbb..5c90249fd6808be2c4f360cdf3eba2cb335acd4b 100644 GIT binary patch delta 49 zcmZpC%-Hvsk@qw&FBbz4%nVn~aNWpTQ@|&hl30>hP?TSmnUY!*Ke?;mGNZ-hxI$$B DjZqNe delta 36 qcmeBs%-Hytk@qw&FBbz46z=?*Zn2TKrht)S^7MksjM|g43zY%n2@G%m diff --git a/dataprovider_realtime.py b/dataprovider_realtime.py index 60fc471..311f494 100644 --- a/dataprovider_realtime.py +++ b/dataprovider_realtime.py @@ -1017,6 +1017,7 @@ class TickStorage: self.current_candle = {tf: None for tf in self.timeframes} self.last_candle_timestamp = {tf: None for tf in self.timeframes} self.cache_dir = os.path.join(os.getcwd(), "cache", symbol.replace("/", "")) + self.cache_path = os.path.join(self.cache_dir, f"{symbol.replace('/', '')}_ticks.json") # Add missing cache_path self.use_timescaledb = use_timescaledb self.max_ticks = 10000 # Maximum number of ticks to store in memory @@ -1024,9 +1025,13 @@ class TickStorage: os.makedirs(self.cache_dir, exist_ok=True) logger.info(f"Creating new tick storage for {symbol} with timeframes {self.timeframes}") + logger.info(f"Cache directory: {self.cache_dir}") + logger.info(f"Cache file: {self.cache_path}") if use_timescaledb: print(f"TickStorage: TimescaleDB integration is ENABLED for {symbol}") + else: + logger.info(f"TickStorage: TimescaleDB integration is DISABLED for {symbol}") def _save_to_cache(self): """Save ticks to a cache file""" @@ -1282,40 +1287,57 @@ class TickStorage: def load_historical_data(self, symbol, limit=1000): """Load historical data for all timeframes""" + logger.info(f"Starting historical data load for {symbol} with limit {limit}") + # Clear existing data self.ticks = [] self.candles = {tf: [] for tf in self.timeframes} self.current_candle = {tf: None for tf in self.timeframes} # Try to load ticks from cache first + logger.info("Attempting to load from cache...") cache_loaded = self._load_from_cache() + if cache_loaded: + logger.info("Successfully loaded data from cache") + else: + logger.info("No valid cache data found") # Check if we have TimescaleDB enabled - if self.use_timescaledb: + if self.use_timescaledb and timescaledb_handler and timescaledb_handler.enabled: logger.info("Attempting to fetch historical data from TimescaleDB") loaded_from_db = False # Load candles for each timeframe from TimescaleDB for tf in self.timeframes: - candles = timescaledb_handler.fetch_candles(symbol, tf, limit) - if candles: - self.candles[tf] = candles - loaded_from_db = True - logger.info(f"Loaded {len(candles)} {tf} candles from TimescaleDB") + try: + candles = timescaledb_handler.fetch_candles(symbol, tf, limit) + if candles: + self.candles[tf] = candles + loaded_from_db = True + logger.info(f"Loaded {len(candles)} {tf} candles from TimescaleDB") + else: + logger.info(f"No {tf} candles found in TimescaleDB") + except Exception as e: + logger.error(f"Error loading {tf} candles from TimescaleDB: {str(e)}") if loaded_from_db: logger.info("Successfully loaded historical data from TimescaleDB") return True + else: + logger.info("TimescaleDB not available or disabled") # If no TimescaleDB data and no cache, we need to get from Binance API if not cache_loaded: + logger.info("Loading data from Binance API...") # Create a BinanceHistoricalData instance historical_data = BinanceHistoricalData() # Load data for each timeframe + success_count = 0 for tf in self.timeframes: if tf != "1s": # Skip 1s since we'll generate it from ticks try: + logger.info(f"Fetching {tf} candles for {symbol}...") df = historical_data.get_historical_candles(symbol, self._timeframe_to_seconds(tf), limit) if df is not None and not df.empty: logger.info(f"Loaded {len(df)} {tf} candles from Binance API") @@ -1334,15 +1356,23 @@ class TickStorage: candles.append(candle) # Also save to TimescaleDB if enabled - if self.use_timescaledb: + if self.use_timescaledb and timescaledb_handler and timescaledb_handler.enabled: timescaledb_handler.upsert_candle(symbol, tf, candle) self.candles[tf] = candles + success_count += 1 + else: + logger.warning(f"No data returned for {tf} candles") except Exception as e: logger.error(f"Error loading {tf} candles: {str(e)}") + import traceback + logger.error(traceback.format_exc()) + + logger.info(f"Successfully loaded {success_count} timeframes from Binance API") # For 1s, load from API if possible or compute from first available timeframe if "1s" in self.timeframes: + logger.info("Loading 1s candles...") # Try to get 1s data from Binance try: df_1s = historical_data.get_historical_candles(symbol, 1, 300) # Only need recent 1s data @@ -1363,7 +1393,7 @@ class TickStorage: candles_1s.append(candle) # Also save to TimescaleDB if enabled - if self.use_timescaledb: + if self.use_timescaledb and timescaledb_handler and timescaledb_handler.enabled: timescaledb_handler.upsert_candle(symbol, "1s", candle) self.candles["1s"] = candles_1s @@ -1372,6 +1402,7 @@ class TickStorage: # If 1s data not available or failed to load, approximate from 1m data if not self.candles.get("1s"): + logger.info("1s data not available, trying to approximate from 1m data...") # If 1s data not available, we can approximate from 1m data if "1m" in self.timeframes and self.candles["1m"]: # For demonstration, just use the 1m candles as placeholders for 1s @@ -1381,6 +1412,7 @@ class TickStorage: # Take the most recent 5 minutes of 1m candles recent_1m = self.candles["1m"][-5:] if self.candles["1m"] else [] + logger.info(f"Creating 1s approximations from {len(recent_1m)} 1m candles") for candle_1m in recent_1m: # Create 60 1s candles for each 1m candle ts_base = candle_1m["timestamp"].timestamp() @@ -1397,17 +1429,23 @@ class TickStorage: self.candles["1s"].append(candle_1s) # Also save to TimescaleDB if enabled - if self.use_timescaledb: + if self.use_timescaledb and timescaledb_handler and timescaledb_handler.enabled: timescaledb_handler.upsert_candle(symbol, "1s", candle_1s) + + logger.info(f"Created {len(self.candles['1s'])} approximated 1s candles") + else: + logger.warning("No 1m data available to approximate 1s candles from") # Set the last candle of each timeframe as the current candle for tf in self.timeframes: if self.candles[tf]: self.current_candle[tf] = self.candles[tf][-1].copy() self.last_candle_timestamp[tf] = self.current_candle[tf]["timestamp"] + logger.debug(f"Set current candle for {tf}: {self.current_candle[tf]['timestamp']}") # If we loaded ticks from cache, rebuild candles if cache_loaded: + logger.info("Rebuilding candles from cached ticks...") # Clear candles self.candles = {tf: [] for tf in self.timeframes} self.current_candle = {tf: None for tf in self.timeframes} @@ -1419,8 +1457,17 @@ class TickStorage: self._update_1s_candle(tick) else: self._update_candles_for_timeframe(tf, tick) + + logger.info("Finished rebuilding candles from ticks") - return cache_loaded or (self.candles["1m"] if "1m" in self.candles else False) + # Log final results + for tf in self.timeframes: + count = len(self.candles[tf]) + logger.info(f"Final {tf} candle count: {count}") + + has_data = cache_loaded or any(self.candles[tf] for tf in self.timeframes) + logger.info(f"Historical data loading completed. Has data: {has_data}") + return has_data def _try_cache_ticks(self): """Try to save ticks to cache periodically""" @@ -1561,6 +1608,11 @@ class RealTimeChart: self.trades_per_minute = 0 self.trades_per_hour = 0 + # Initialize trade rate tracking variables + self.trade_times = [] # Store timestamps of recent trades for rate calculation + self.last_trade_rate_calculation = datetime.now() + self.trade_rate = {"per_second": 0, "per_minute": 0, "per_hour": 0} + # Initialize interactive components self.app = app @@ -1583,6 +1635,23 @@ class RealTimeChart: timeframes=["1s", "1m", "5m", "15m", "1h", "4h", "1d"], use_timescaledb=use_timescaledb ) + + # Load historical data immediately for cold start + logger.info(f"Loading historical data for {symbol} during chart initialization") + try: + data_loaded = self.tick_storage.load_historical_data(symbol) + if data_loaded: + logger.info(f"Successfully loaded historical data for {symbol}") + # Log what we have + for tf in ["1s", "1m", "5m", "15m", "1h"]: + candle_count = len(self.tick_storage.candles.get(tf, [])) + logger.info(f" {tf}: {candle_count} candles") + else: + logger.warning(f"Failed to load historical data for {symbol}") + except Exception as e: + logger.error(f"Error loading historical data during initialization: {str(e)}") + import traceback + logger.error(traceback.format_exc()) else: self.tick_storage = tick_storage @@ -1592,7 +1661,7 @@ class RealTimeChart: self.app.layout = self._create_layout() # Register callbacks - self._register_callbacks() + self._setup_callbacks() # Log initialization if self.enable_logging: @@ -1653,13 +1722,10 @@ class RealTimeChart: # Store for the selected timeframe dcc.Store(id='interval-store', data={'interval': 1}), - # Chart content container that will be updated by callbacks - html.Div(id='chart-content', children=[ - # Initial content - dcc.Graph(id='live-chart', style={"height": "600px"}), - dcc.Graph(id='secondary-charts', style={"height": "500px"}), - html.Div(id='positions-list') - ]) + # Chart content (without wrapper div to avoid callback issues) + dcc.Graph(id='live-chart', style={"height": "600px"}), + dcc.Graph(id='secondary-charts', style={"height": "500px"}), + html.Div(id='positions-list') ]) def _create_chart_and_controls(self): @@ -2016,26 +2082,20 @@ class RealTimeChart: try: # Get candles from tick storage interval_key = self._get_interval_key(interval_seconds) - df = self.tick_storage.get_candles(interval_key) + candles_list = self.tick_storage.get_candles(interval_key) - if df is None or df.empty: - logger.warning(f"No candle data available for {interval_key}") - return [] # Return empty list if no data + if not candles_list: + logger.warning(f"No candle data available for {interval_key} - trying to load historical data") + # Try to load historical data if we don't have any + self.tick_storage.load_historical_data(self.symbol) + candles_list = self.tick_storage.get_candles(interval_key) - # Convert dataframe to list of dictionaries - candles = [] - for idx, row in df.iterrows(): - candle = { - 'timestamp': idx, - 'open': row['open'], - 'high': row['high'], - 'low': row['low'], - 'close': row['close'], - 'volume': row['volume'] - } - candles.append(candle) + if not candles_list: + logger.error(f"Still no candle data available for {interval_key} after loading historical data") + return [] - return candles + logger.info(f"Retrieved {len(candles_list)} candles for {interval_key}") + return candles_list except Exception as e: logger.error(f"Error getting candles: {str(e)}") @@ -2197,7 +2257,11 @@ class RealTimeChart: def run(self, host='localhost', port=8050): """Run the Dash app on the specified host and port""" try: - logger.info(f"Starting Dash app for {self.symbol} on {host}:{port}") + logger.info("="*60) + logger.info(f"๐Ÿš€ STARTING WEB UI FOR {self.symbol}") + logger.info(f"๐Ÿ“ฑ Web interface available at: http://{host}:{port}/") + logger.info(f"๐ŸŒ Open this URL in your browser to view the trading chart") + logger.info("="*60) self.app.run(debug=False, use_reloader=False, host=host, port=port) except Exception as e: logger.error(f"Error running Dash app: {str(e)}") @@ -2253,6 +2317,166 @@ class RealTimeChart: # Placeholder for adding NN signals if needed in the future pass + def _update_main_chart(self, interval_seconds): + """Update the main chart for the specified interval""" + try: + # Convert interval seconds to timeframe key + interval_key = self._get_interval_key(interval_seconds) + + # Get candles for this timeframe + if interval_key not in self.tick_storage.candles or not self.tick_storage.candles[interval_key]: + logger.warning(f"No candle data available for {interval_key}") + # Return empty figure with a message + fig = go.Figure() + fig.add_annotation( + text=f"No data available for {interval_key}", + xref="paper", yref="paper", + x=0.5, y=0.5, showarrow=False, + font=dict(size=20, color="white") + ) + fig.update_layout( + template="plotly_dark", + height=600, + title=f"{self.symbol} - {interval_key} Chart", + xaxis_title="Time", + yaxis_title="Price ($)" + ) + return fig + + # Get candles (limit to last 500 for performance) + candles = self.tick_storage.candles[interval_key][-500:] + + if not candles: + # Return empty figure if no candles + fig = go.Figure() + fig.add_annotation( + text=f"No candles available for {interval_key}", + xref="paper", yref="paper", + x=0.5, y=0.5, showarrow=False, + font=dict(size=20, color="white") + ) + fig.update_layout( + template="plotly_dark", + height=600, + title=f"{self.symbol} - {interval_key} Chart" + ) + return fig + + # Extract OHLC values + timestamps = [candle['timestamp'] for candle in candles] + opens = [candle['open'] for candle in candles] + highs = [candle['high'] for candle in candles] + lows = [candle['low'] for candle in candles] + closes = [candle['close'] for candle in candles] + volumes = [candle['volume'] for candle in candles] + + # Create the main figure + fig = go.Figure() + + # Add candlestick trace + fig.add_trace(go.Candlestick( + x=timestamps, + open=opens, + high=highs, + low=lows, + close=closes, + name=f"{self.symbol}", + increasing_line_color='rgba(0, 200, 0, 0.8)', + decreasing_line_color='rgba(255, 0, 0, 0.8)', + increasing_fillcolor='rgba(0, 200, 0, 0.3)', + decreasing_fillcolor='rgba(255, 0, 0, 0.3)' + )) + + # Add trade markers if we have positions + if hasattr(self, 'positions') and self.positions: + # Get recent positions (last 50 to avoid clutter) + recent_positions = self.positions[-50:] if len(self.positions) > 50 else self.positions + + for position in recent_positions: + # Add entry marker + fig.add_trace(go.Scatter( + x=[position.entry_timestamp], + y=[position.entry_price], + mode='markers', + marker=dict( + symbol='triangle-up' if position.action == 'BUY' else 'triangle-down', + size=12, + color='green' if position.action == 'BUY' else 'red', + line=dict(width=2, color='white') + ), + name=f"{position.action} Entry", + hovertemplate=f"{position.action} Entry
" + + f"Price: ${position.entry_price:.2f}
" + + f"Time: {position.entry_timestamp}
" + + f"ID: {position.trade_id}", + showlegend=False + )) + + # Add exit marker if position is closed + if not position.is_open and position.exit_price and position.exit_timestamp: + fig.add_trace(go.Scatter( + x=[position.exit_timestamp], + y=[position.exit_price], + mode='markers', + marker=dict( + symbol='triangle-down' if position.action == 'BUY' else 'triangle-up', + size=12, + color='blue', + line=dict(width=2, color='white') + ), + name=f"{position.action} Exit", + hovertemplate=f"{position.action} Exit
" + + f"Price: ${position.exit_price:.2f}
" + + f"Time: {position.exit_timestamp}
" + + f"PnL: ${position.pnl:.2f}
" + + f"ID: {position.trade_id}", + showlegend=False + )) + + # Update layout + fig.update_layout( + template="plotly_dark", + height=600, + title=f"{self.symbol} - {interval_key} Chart (Live Trading)", + xaxis_title="Time", + yaxis_title="Price ($)", + showlegend=True, + margin=dict(l=0, r=0, t=40, b=0), + xaxis_rangeslider_visible=False, + hovermode='x unified' + ) + + # Format Y-axis with appropriate decimal places + fig.update_yaxes(tickformat=".2f") + + # Format X-axis + fig.update_xaxes( + rangeslider_visible=False, + type='date' + ) + + return fig + + except Exception as e: + logger.error(f"Error updating main chart: {str(e)}") + import traceback + logger.error(traceback.format_exc()) + + # Return error figure + fig = go.Figure() + fig.add_annotation( + text=f"Error loading chart: {str(e)}", + xref="paper", yref="paper", + x=0.5, y=0.5, showarrow=False, + font=dict(size=16, color="red") + ) + fig.update_layout( + template="plotly_dark", + height=600, + title=f"{self.symbol} Chart - Error" + ) + return fig + class BinanceWebSocket: """Binance WebSocket implementation for real-time tick data""" def __init__(self, symbol: str): diff --git a/realtime_chart.log b/realtime_chart.log index 3f26b0e..48ae714 100644 --- a/realtime_chart.log +++ b/realtime_chart.log @@ -696857,3 +696857,124 @@ ade'} 2025-03-19 00:09:56,964 - INFO - [_internal.py:97] - 127.0.0.1 - - [19/Mar/2025 00:09:56] "POST /_dash-update-component HTTP/1.1" 200 - 2025-03-19 00:09:57,070 - DEBUG - [realtime.py:233] - Retrieved 965 ticks from time range 1742335497069 to None 2025-03-19 00:09:57,071 - DEBUG - [protocol.py:1177] - < PING '1742335799715' [text, 13 bytes] +2025-05-24 01:14:38,311 - INFO - [dataprovider_realtime.py:978] - Detected local timezone: Europe/Kiev (Europe/Kiev) +2025-05-24 01:14:38,654 - INFO - [test_chart_data.py:111] - ============================================================ +2025-05-24 01:14:38,655 - INFO - [test_chart_data.py:122] - ---------------------------------------- +2025-05-24 01:14:38,656 - INFO - [test_chart_data.py:21] - Testing Binance historical data fetch... +2025-05-24 01:14:38,671 - INFO - [dataprovider_realtime.py:337] - Loaded 1000 candles from cache: F:\projects\gogo2\cache\ETHUSDT_1m_candles.csv +2025-05-24 01:14:39,101 - INFO - [dataprovider_realtime.py:348] - Saved 100 candles to cache: F:\projects\gogo2\cache\ETHUSDT_1m_candles.csv +2025-05-24 01:14:39,101 - INFO - [dataprovider_realtime.py:305] - Fetched 100 candles for ETH/USDT (1m) +2025-05-24 01:14:39,104 - INFO - [test_chart_data.py:31] - Latest price: $2556.22 +2025-05-24 01:14:39,104 - INFO - [test_chart_data.py:32] - Date range: 2025-05-23 20:35:00 to 2025-05-23 22:14:00 +2025-05-24 01:14:39,106 - INFO - [test_chart_data.py:122] - ---------------------------------------- +2025-05-24 01:14:39,106 - INFO - [test_chart_data.py:44] - Testing TickStorage data loading... +2025-05-24 01:14:39,107 - INFO - [dataprovider_realtime.py:1027] - Creating new tick storage for ETH/USDT with timeframes ['1s', '1m', '5m', '1h'] +2025-05-24 01:14:39,107 - INFO - [dataprovider_realtime.py:1028] - Cache directory: F:\projects\gogo2\cache\ETHUSDT +2025-05-24 01:14:39,107 - INFO - [dataprovider_realtime.py:1029] - Cache file: F:\projects\gogo2\cache\ETHUSDT\ETHUSDT_ticks.json +2025-05-24 01:14:39,107 - INFO - [dataprovider_realtime.py:1034] - TickStorage: TimescaleDB integration is DISABLED for ETH/USDT +2025-05-24 01:14:39,108 - INFO - [dataprovider_realtime.py:1290] - Starting historical data load for ETH/USDT with limit 100 +2025-05-24 01:14:39,108 - INFO - [dataprovider_realtime.py:1298] - Attempting to load from cache... +2025-05-24 01:14:39,115 - ERROR - [dataprovider_realtime.py:1064] - Error loading ticks from cache: Expecting value: line 1 column 52 (char 51) +2025-05-24 01:14:39,115 - INFO - [dataprovider_realtime.py:1303] - No valid cache data found +2025-05-24 01:14:39,116 - INFO - [dataprovider_realtime.py:1327] - TimescaleDB not available or disabled +2025-05-24 01:14:39,116 - INFO - [dataprovider_realtime.py:1331] - Loading data from Binance API... +2025-05-24 01:14:39,116 - INFO - [dataprovider_realtime.py:1340] - Fetching 1m candles for ETH/USDT... +2025-05-24 01:14:39,131 - INFO - [dataprovider_realtime.py:337] - Loaded 100 candles from cache: F:\projects\gogo2\cache\ETHUSDT_1m_candles.csv +2025-05-24 01:14:39,583 - INFO - [dataprovider_realtime.py:348] - Saved 100 candles to cache: F:\projects\gogo2\cache\ETHUSDT_1m_candles.csv +2025-05-24 01:14:39,583 - INFO - [dataprovider_realtime.py:305] - Fetched 100 candles for ETH/USDT (1m) +2025-05-24 01:14:39,583 - INFO - [dataprovider_realtime.py:1343] - Loaded 100 1m candles from Binance API +2025-05-24 01:14:39,589 - INFO - [dataprovider_realtime.py:1340] - Fetching 5m candles for ETH/USDT... +2025-05-24 01:14:39,601 - INFO - [dataprovider_realtime.py:337] - Loaded 1000 candles from cache: F:\projects\gogo2\cache\ETHUSDT_5m_candles.csv +2025-05-24 01:14:39,602 - INFO - [dataprovider_realtime.py:265] - Using cached historical data for ETH/USDT (5m) +2025-05-24 01:14:39,602 - INFO - [dataprovider_realtime.py:1343] - Loaded 1000 5m candles from Binance API +2025-05-24 01:14:39,642 - INFO - [dataprovider_realtime.py:1340] - Fetching 1h candles for ETH/USDT... +2025-05-24 01:14:39,656 - INFO - [dataprovider_realtime.py:337] - Loaded 1000 candles from cache: F:\projects\gogo2\cache\ETHUSDT_1h_candles.csv +2025-05-24 01:14:39,656 - INFO - [dataprovider_realtime.py:265] - Using cached historical data for ETH/USDT (1h) +2025-05-24 01:14:39,656 - INFO - [dataprovider_realtime.py:1343] - Loaded 1000 1h candles from Binance API +2025-05-24 01:14:39,683 - INFO - [dataprovider_realtime.py:1371] - Successfully loaded 3 timeframes from Binance API +2025-05-24 01:14:39,684 - INFO - [dataprovider_realtime.py:1375] - Loading 1s candles... +2025-05-24 01:14:39,684 - INFO - [dataprovider_realtime.py:333] - Using recent 1s cache (age: 1.0 minutes) +2025-05-24 01:14:39,687 - INFO - [dataprovider_realtime.py:337] - Loaded 300 candles from cache: F:\projects\gogo2\cache\ETHUSDT_1s_candles.csv +2025-05-24 01:14:39,687 - INFO - [dataprovider_realtime.py:265] - Using cached historical data for ETH/USDT (1s) +2025-05-24 01:14:39,687 - INFO - [dataprovider_realtime.py:1380] - Loaded 300 recent 1s candles from Binance API +2025-05-24 01:14:39,695 - INFO - [dataprovider_realtime.py:1466] - Final 1s candle count: 300 +2025-05-24 01:14:39,695 - INFO - [dataprovider_realtime.py:1466] - Final 1m candle count: 100 +2025-05-24 01:14:39,695 - INFO - [dataprovider_realtime.py:1466] - Final 5m candle count: 1000 +2025-05-24 01:14:39,695 - INFO - [dataprovider_realtime.py:1466] - Final 1h candle count: 1000 +2025-05-24 01:14:39,696 - INFO - [dataprovider_realtime.py:1469] - Historical data loading completed. Has data: True +2025-05-24 01:14:39,697 - INFO - [test_chart_data.py:59] - 1s: 300 candles +2025-05-24 01:14:39,697 - INFO - [test_chart_data.py:63] - Latest 1s: 2025-05-23 22:13:36 - $2549.36 +2025-05-24 01:14:39,697 - INFO - [test_chart_data.py:59] - 1m: 100 candles +2025-05-24 01:14:39,697 - INFO - [test_chart_data.py:63] - Latest 1m: 2025-05-23 22:14:00 - $2556.22 +2025-05-24 01:14:39,699 - INFO - [test_chart_data.py:59] - 5m: 1000 candles +2025-05-24 01:14:39,699 - INFO - [test_chart_data.py:63] - Latest 5m: 2025-05-23 22:10:00 - $2549.69 +2025-05-24 01:14:39,699 - INFO - [test_chart_data.py:59] - 1h: 1000 candles +2025-05-24 01:14:39,699 - INFO - [test_chart_data.py:63] - Latest 1h: 2025-05-23 22:00:00 - $2549.84 +2025-05-24 01:14:39,700 - INFO - [test_chart_data.py:122] - ---------------------------------------- +2025-05-24 01:14:39,700 - INFO - [test_chart_data.py:78] - Testing RealTimeChart initialization... +2025-05-24 01:14:39,701 - INFO - [dataprovider_realtime.py:1027] - Creating new tick storage for ETH/USDT with timeframes ['1s', '1m', '5m', '15m', '1h', '4h', '1d'] +2025-05-24 01:14:39,701 - INFO - [dataprovider_realtime.py:1028] - Cache directory: F:\projects\gogo2\cache\ETHUSDT +2025-05-24 01:14:39,701 - INFO - [dataprovider_realtime.py:1029] - Cache file: F:\projects\gogo2\cache\ETHUSDT\ETHUSDT_ticks.json +2025-05-24 01:14:39,701 - INFO - [dataprovider_realtime.py:1640] - Loading historical data for ETH/USDT during chart initialization +2025-05-24 01:14:39,702 - INFO - [dataprovider_realtime.py:1290] - Starting historical data load for ETH/USDT with limit 1000 +2025-05-24 01:14:39,702 - INFO - [dataprovider_realtime.py:1298] - Attempting to load from cache... +2025-05-24 01:14:39,710 - ERROR - [dataprovider_realtime.py:1064] - Error loading ticks from cache: Expecting value: line 1 column 53 (char 52) +2025-05-24 01:14:39,710 - INFO - [dataprovider_realtime.py:1303] - No valid cache data found +2025-05-24 01:14:39,710 - INFO - [dataprovider_realtime.py:1327] - TimescaleDB not available or disabled +2025-05-24 01:14:39,711 - INFO - [dataprovider_realtime.py:1331] - Loading data from Binance API... +2025-05-24 01:14:39,711 - INFO - [dataprovider_realtime.py:1340] - Fetching 1m candles for ETH/USDT... +2025-05-24 01:14:39,721 - INFO - [dataprovider_realtime.py:337] - Loaded 100 candles from cache: F:\projects\gogo2\cache\ETHUSDT_1m_candles.csv +2025-05-24 01:14:40,243 - INFO - [dataprovider_realtime.py:348] - Saved 1000 candles to cache: F:\projects\gogo2\cache\ETHUSDT_1m_candles.csv +2025-05-24 01:14:40,244 - INFO - [dataprovider_realtime.py:305] - Fetched 1000 candles for ETH/USDT (1m) +2025-05-24 01:14:40,244 - INFO - [dataprovider_realtime.py:1343] - Loaded 1000 1m candles from Binance API +2025-05-24 01:14:40,271 - INFO - [dataprovider_realtime.py:1340] - Fetching 5m candles for ETH/USDT... +2025-05-24 01:14:40,274 - INFO - [dataprovider_realtime.py:337] - Loaded 1000 candles from cache: F:\projects\gogo2\cache\ETHUSDT_5m_candles.csv +2025-05-24 01:14:40,275 - INFO - [dataprovider_realtime.py:265] - Using cached historical data for ETH/USDT (5m) +2025-05-24 01:14:40,275 - INFO - [dataprovider_realtime.py:1343] - Loaded 1000 5m candles from Binance API +2025-05-24 01:14:40,300 - INFO - [dataprovider_realtime.py:1340] - Fetching 15m candles for ETH/USDT... +2025-05-24 01:14:40,321 - INFO - [dataprovider_realtime.py:337] - Loaded 1000 candles from cache: F:\projects\gogo2\cache\ETHUSDT_15m_candles.csv +2025-05-24 01:14:40,322 - INFO - [dataprovider_realtime.py:265] - Using cached historical data for ETH/USDT (15m) +2025-05-24 01:14:40,322 - INFO - [dataprovider_realtime.py:1343] - Loaded 1000 15m candles from Binance API +2025-05-24 01:14:40,348 - INFO - [dataprovider_realtime.py:1340] - Fetching 1h candles for ETH/USDT... +2025-05-24 01:14:40,353 - INFO - [dataprovider_realtime.py:337] - Loaded 1000 candles from cache: F:\projects\gogo2\cache\ETHUSDT_1h_candles.csv +2025-05-24 01:14:40,353 - INFO - [dataprovider_realtime.py:265] - Using cached historical data for ETH/USDT (1h) +2025-05-24 01:14:40,354 - INFO - [dataprovider_realtime.py:1343] - Loaded 1000 1h candles from Binance API +2025-05-24 01:14:40,379 - INFO - [dataprovider_realtime.py:1340] - Fetching 4h candles for ETH/USDT... +2025-05-24 01:14:40,383 - INFO - [dataprovider_realtime.py:337] - Loaded 1000 candles from cache: F:\projects\gogo2\cache\ETHUSDT_4h_candles.csv +2025-05-24 01:14:40,384 - INFO - [dataprovider_realtime.py:265] - Using cached historical data for ETH/USDT (4h) +2025-05-24 01:14:40,384 - INFO - [dataprovider_realtime.py:1343] - Loaded 1000 4h candles from Binance API +2025-05-24 01:14:40,409 - INFO - [dataprovider_realtime.py:1340] - Fetching 1d candles for ETH/USDT... +2025-05-24 01:14:40,412 - INFO - [dataprovider_realtime.py:337] - Loaded 1000 candles from cache: F:\projects\gogo2\cache\ETHUSDT_1d_candles.csv +2025-05-24 01:14:40,412 - INFO - [dataprovider_realtime.py:265] - Using cached historical data for ETH/USDT (1d) +2025-05-24 01:14:40,412 - INFO - [dataprovider_realtime.py:1343] - Loaded 1000 1d candles from Binance API +2025-05-24 01:14:40,439 - INFO - [dataprovider_realtime.py:1371] - Successfully loaded 6 timeframes from Binance API +2025-05-24 01:14:40,439 - INFO - [dataprovider_realtime.py:1375] - Loading 1s candles... +2025-05-24 01:14:40,439 - INFO - [dataprovider_realtime.py:333] - Using recent 1s cache (age: 1.1 minutes) +2025-05-24 01:14:40,442 - INFO - [dataprovider_realtime.py:337] - Loaded 300 candles from cache: F:\projects\gogo2\cache\ETHUSDT_1s_candles.csv +2025-05-24 01:14:40,442 - INFO - [dataprovider_realtime.py:265] - Using cached historical data for ETH/USDT (1s) +2025-05-24 01:14:40,443 - INFO - [dataprovider_realtime.py:1380] - Loaded 300 recent 1s candles from Binance API +2025-05-24 01:14:40,450 - INFO - [dataprovider_realtime.py:1466] - Final 1s candle count: 300 +2025-05-24 01:14:40,450 - INFO - [dataprovider_realtime.py:1466] - Final 1m candle count: 1000 +2025-05-24 01:14:40,451 - INFO - [dataprovider_realtime.py:1466] - Final 5m candle count: 1000 +2025-05-24 01:14:40,451 - INFO - [dataprovider_realtime.py:1466] - Final 15m candle count: 1000 +2025-05-24 01:14:40,451 - INFO - [dataprovider_realtime.py:1466] - Final 1h candle count: 1000 +2025-05-24 01:14:40,451 - INFO - [dataprovider_realtime.py:1466] - Final 4h candle count: 1000 +2025-05-24 01:14:40,451 - INFO - [dataprovider_realtime.py:1466] - Final 1d candle count: 1000 +2025-05-24 01:14:40,451 - INFO - [dataprovider_realtime.py:1469] - Historical data loading completed. Has data: True +2025-05-24 01:14:40,451 - INFO - [dataprovider_realtime.py:1644] - Successfully loaded historical data for ETH/USDT +2025-05-24 01:14:40,451 - INFO - [dataprovider_realtime.py:1648] - 1s: 300 candles +2025-05-24 01:14:40,451 - INFO - [dataprovider_realtime.py:1648] - 1m: 1000 candles +2025-05-24 01:14:40,452 - INFO - [dataprovider_realtime.py:1648] - 5m: 1000 candles +2025-05-24 01:14:40,452 - INFO - [dataprovider_realtime.py:1648] - 15m: 1000 candles +2025-05-24 01:14:40,452 - INFO - [dataprovider_realtime.py:1648] - 1h: 1000 candles +2025-05-24 01:14:40,452 - INFO - [dataprovider_realtime.py:1668] - RealTimeChart initialized: ETH/USDT (1m) +2025-05-24 01:14:40,452 - INFO - [dataprovider_realtime.py:2097] - Retrieved 300 candles for 1s +2025-05-24 01:14:40,453 - INFO - [dataprovider_realtime.py:2097] - Retrieved 1000 candles for 1m +2025-05-24 01:14:40,454 - INFO - [test_chart_data.py:93] - 1s candles: 300 +2025-05-24 01:14:40,454 - INFO - [test_chart_data.py:94] - 1m candles: 1000 +2025-05-24 01:14:40,454 - INFO - [test_chart_data.py:98] - Latest 1m candle: 2025-05-23 22:14:00 - $2556.00 +2025-05-24 01:14:40,455 - INFO - [test_chart_data.py:131] - +============================================================ +2025-05-24 01:14:40,456 - INFO - [test_chart_data.py:133] - ============================================================ +2025-05-24 01:14:40,459 - INFO - [test_chart_data.py:142] - +Passed: 3/3 tests diff --git a/test_chart_data.py b/test_chart_data.py new file mode 100644 index 0000000..51e3b82 --- /dev/null +++ b/test_chart_data.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +""" +Test script to verify chart data loading functionality +""" + +import logging +import sys +import os + +# Add the project root to the path +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from dataprovider_realtime import RealTimeChart, TickStorage, BinanceHistoricalData + +# Set up logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +def test_binance_data_fetch(): + """Test fetching data from Binance API""" + logger.info("Testing Binance historical data fetch...") + + try: + binance_data = BinanceHistoricalData() + + # Test fetching 1m data for ETH/USDT + df = binance_data.get_historical_candles("ETH/USDT", 60, 100) + + if df is not None and not df.empty: + logger.info(f"โœ… Successfully fetched {len(df)} 1m candles") + logger.info(f" Latest price: ${df.iloc[-1]['close']:.2f}") + logger.info(f" Date range: {df.iloc[0]['timestamp']} to {df.iloc[-1]['timestamp']}") + return True + else: + logger.error("โŒ Failed to fetch Binance data") + return False + + except Exception as e: + logger.error(f"โŒ Error fetching Binance data: {str(e)}") + return False + +def test_tick_storage(): + """Test TickStorage data loading""" + logger.info("Testing TickStorage data loading...") + + try: + # Create tick storage + tick_storage = TickStorage("ETH/USDT", ["1s", "1m", "5m", "1h"]) + + # Load historical data + success = tick_storage.load_historical_data("ETH/USDT", limit=100) + + if success: + logger.info("โœ… TickStorage data loading successful") + + # Check what we have + for tf in ["1s", "1m", "5m", "1h"]: + candles = tick_storage.get_candles(tf) + logger.info(f" {tf}: {len(candles)} candles") + + if candles: + latest = candles[-1] + logger.info(f" Latest {tf}: {latest['timestamp']} - ${latest['close']:.2f}") + + return True + else: + logger.error("โŒ TickStorage data loading failed") + return False + + except Exception as e: + logger.error(f"โŒ Error in TickStorage: {str(e)}") + import traceback + logger.error(traceback.format_exc()) + return False + +def test_chart_initialization(): + """Test RealTimeChart initialization and data loading""" + logger.info("Testing RealTimeChart initialization...") + + try: + # Create chart (without app to avoid GUI issues) + chart = RealTimeChart( + app=None, + symbol="ETH/USDT", + standalone=False + ) + + # Test getting candles + candles_1s = chart.get_candles(1) # 1 second + candles_1m = chart.get_candles(60) # 1 minute + + logger.info(f"โœ… Chart initialized successfully") + logger.info(f" 1s candles: {len(candles_1s)}") + logger.info(f" 1m candles: {len(candles_1m)}") + + if candles_1m: + latest = candles_1m[-1] + logger.info(f" Latest 1m candle: {latest['timestamp']} - ${latest['close']:.2f}") + + return len(candles_1s) > 0 or len(candles_1m) > 0 + + except Exception as e: + logger.error(f"โŒ Error in chart initialization: {str(e)}") + import traceback + logger.error(traceback.format_exc()) + return False + +def main(): + """Run all tests""" + logger.info("๐Ÿงช Starting chart data loading tests...") + logger.info("=" * 60) + + tests = [ + ("Binance API fetch", test_binance_data_fetch), + ("TickStorage loading", test_tick_storage), + ("Chart initialization", test_chart_initialization) + ] + + results = [] + for test_name, test_func in tests: + logger.info(f"\n๐Ÿ“‹ Running test: {test_name}") + logger.info("-" * 40) + try: + result = test_func() + results.append((test_name, result)) + except Exception as e: + logger.error(f"โŒ Test {test_name} crashed: {str(e)}") + results.append((test_name, False)) + + # Print summary + logger.info("\n" + "=" * 60) + logger.info("๐Ÿ“Š TEST RESULTS SUMMARY") + logger.info("=" * 60) + + passed = 0 + for test_name, result in results: + status = "โœ… PASS" if result else "โŒ FAIL" + logger.info(f"{status}: {test_name}") + if result: + passed += 1 + + logger.info(f"\nPassed: {passed}/{len(results)} tests") + + if passed == len(results): + logger.info("๐ŸŽ‰ All tests passed! Chart data loading is working correctly.") + return True + else: + logger.warning(f"โš ๏ธ {len(results) - passed} test(s) failed. Please check the issues above.") + return False + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/train_rl_with_realtime.py b/train_rl_with_realtime.py index dadd9ac..8073bf9 100644 --- a/train_rl_with_realtime.py +++ b/train_rl_with_realtime.py @@ -1089,8 +1089,11 @@ async def start_realtime_chart(symbol="ETH/USDT", port=8050, manual_mode=False): logger.info("Enabling manual trading mode") logger.warning("Manual trading mode not supported by this simplified chart implementation") - logger.info(f"Started realtime chart for {symbol} on port {port}") - logger.info(f"You can view the chart at http://localhost:{port}/") + logger.info("="*60) + logger.info(f"โœ… REALTIME CHART READY FOR {symbol}") + logger.info(f"๐Ÿ”— ACCESS WEB UI AT: http://localhost:{port}/") + logger.info(f"๐Ÿ“Š View live trading data and charts in your browser") + logger.info("="*60) # Start websocket in the background websocket_task = asyncio.create_task(chart.start_websocket())