working charts again!!!

This commit is contained in:
Dobromir Popov 2025-05-24 01:15:16 +03:00
parent 477e5dca39
commit 0c445435d0
5 changed files with 539 additions and 38 deletions

View File

@ -1017,6 +1017,7 @@ class TickStorage:
self.current_candle = {tf: None for tf in self.timeframes} self.current_candle = {tf: None for tf in self.timeframes}
self.last_candle_timestamp = {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_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.use_timescaledb = use_timescaledb
self.max_ticks = 10000 # Maximum number of ticks to store in memory 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) os.makedirs(self.cache_dir, exist_ok=True)
logger.info(f"Creating new tick storage for {symbol} with timeframes {self.timeframes}") 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: if use_timescaledb:
print(f"TickStorage: TimescaleDB integration is ENABLED for {symbol}") 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): def _save_to_cache(self):
"""Save ticks to a cache file""" """Save ticks to a cache file"""
@ -1282,40 +1287,57 @@ class TickStorage:
def load_historical_data(self, symbol, limit=1000): def load_historical_data(self, symbol, limit=1000):
"""Load historical data for all timeframes""" """Load historical data for all timeframes"""
logger.info(f"Starting historical data load for {symbol} with limit {limit}")
# Clear existing data # Clear existing data
self.ticks = [] self.ticks = []
self.candles = {tf: [] for tf in self.timeframes} self.candles = {tf: [] for tf in self.timeframes}
self.current_candle = {tf: None for tf in self.timeframes} self.current_candle = {tf: None for tf in self.timeframes}
# Try to load ticks from cache first # Try to load ticks from cache first
logger.info("Attempting to load from cache...")
cache_loaded = self._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 # 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") logger.info("Attempting to fetch historical data from TimescaleDB")
loaded_from_db = False loaded_from_db = False
# Load candles for each timeframe from TimescaleDB # Load candles for each timeframe from TimescaleDB
for tf in self.timeframes: for tf in self.timeframes:
candles = timescaledb_handler.fetch_candles(symbol, tf, limit) try:
if candles: candles = timescaledb_handler.fetch_candles(symbol, tf, limit)
self.candles[tf] = candles if candles:
loaded_from_db = True self.candles[tf] = candles
logger.info(f"Loaded {len(candles)} {tf} candles from TimescaleDB") 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: if loaded_from_db:
logger.info("Successfully loaded historical data from TimescaleDB") logger.info("Successfully loaded historical data from TimescaleDB")
return True 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 no TimescaleDB data and no cache, we need to get from Binance API
if not cache_loaded: if not cache_loaded:
logger.info("Loading data from Binance API...")
# Create a BinanceHistoricalData instance # Create a BinanceHistoricalData instance
historical_data = BinanceHistoricalData() historical_data = BinanceHistoricalData()
# Load data for each timeframe # Load data for each timeframe
success_count = 0
for tf in self.timeframes: for tf in self.timeframes:
if tf != "1s": # Skip 1s since we'll generate it from ticks if tf != "1s": # Skip 1s since we'll generate it from ticks
try: try:
logger.info(f"Fetching {tf} candles for {symbol}...")
df = historical_data.get_historical_candles(symbol, self._timeframe_to_seconds(tf), limit) df = historical_data.get_historical_candles(symbol, self._timeframe_to_seconds(tf), limit)
if df is not None and not df.empty: if df is not None and not df.empty:
logger.info(f"Loaded {len(df)} {tf} candles from Binance API") logger.info(f"Loaded {len(df)} {tf} candles from Binance API")
@ -1334,15 +1356,23 @@ class TickStorage:
candles.append(candle) candles.append(candle)
# Also save to TimescaleDB if enabled # 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) timescaledb_handler.upsert_candle(symbol, tf, candle)
self.candles[tf] = candles self.candles[tf] = candles
success_count += 1
else:
logger.warning(f"No data returned for {tf} candles")
except Exception as e: except Exception as e:
logger.error(f"Error loading {tf} candles: {str(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 # For 1s, load from API if possible or compute from first available timeframe
if "1s" in self.timeframes: if "1s" in self.timeframes:
logger.info("Loading 1s candles...")
# Try to get 1s data from Binance # Try to get 1s data from Binance
try: try:
df_1s = historical_data.get_historical_candles(symbol, 1, 300) # Only need recent 1s data 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) candles_1s.append(candle)
# Also save to TimescaleDB if enabled # 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) timescaledb_handler.upsert_candle(symbol, "1s", candle)
self.candles["1s"] = candles_1s 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 1s data not available or failed to load, approximate from 1m data
if not self.candles.get("1s"): 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 1s data not available, we can approximate from 1m data
if "1m" in self.timeframes and self.candles["1m"]: if "1m" in self.timeframes and self.candles["1m"]:
# For demonstration, just use the 1m candles as placeholders for 1s # 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 # Take the most recent 5 minutes of 1m candles
recent_1m = self.candles["1m"][-5:] if self.candles["1m"] else [] 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: for candle_1m in recent_1m:
# Create 60 1s candles for each 1m candle # Create 60 1s candles for each 1m candle
ts_base = candle_1m["timestamp"].timestamp() ts_base = candle_1m["timestamp"].timestamp()
@ -1397,17 +1429,23 @@ class TickStorage:
self.candles["1s"].append(candle_1s) self.candles["1s"].append(candle_1s)
# Also save to TimescaleDB if enabled # 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) 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 # Set the last candle of each timeframe as the current candle
for tf in self.timeframes: for tf in self.timeframes:
if self.candles[tf]: if self.candles[tf]:
self.current_candle[tf] = self.candles[tf][-1].copy() self.current_candle[tf] = self.candles[tf][-1].copy()
self.last_candle_timestamp[tf] = self.current_candle[tf]["timestamp"] 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 we loaded ticks from cache, rebuild candles
if cache_loaded: if cache_loaded:
logger.info("Rebuilding candles from cached ticks...")
# Clear candles # Clear candles
self.candles = {tf: [] for tf in self.timeframes} self.candles = {tf: [] for tf in self.timeframes}
self.current_candle = {tf: None 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) self._update_1s_candle(tick)
else: else:
self._update_candles_for_timeframe(tf, tick) 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): def _try_cache_ticks(self):
"""Try to save ticks to cache periodically""" """Try to save ticks to cache periodically"""
@ -1561,6 +1608,11 @@ class RealTimeChart:
self.trades_per_minute = 0 self.trades_per_minute = 0
self.trades_per_hour = 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 # Initialize interactive components
self.app = app self.app = app
@ -1583,6 +1635,23 @@ class RealTimeChart:
timeframes=["1s", "1m", "5m", "15m", "1h", "4h", "1d"], timeframes=["1s", "1m", "5m", "15m", "1h", "4h", "1d"],
use_timescaledb=use_timescaledb 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: else:
self.tick_storage = tick_storage self.tick_storage = tick_storage
@ -1592,7 +1661,7 @@ class RealTimeChart:
self.app.layout = self._create_layout() self.app.layout = self._create_layout()
# Register callbacks # Register callbacks
self._register_callbacks() self._setup_callbacks()
# Log initialization # Log initialization
if self.enable_logging: if self.enable_logging:
@ -1653,13 +1722,10 @@ class RealTimeChart:
# Store for the selected timeframe # Store for the selected timeframe
dcc.Store(id='interval-store', data={'interval': 1}), dcc.Store(id='interval-store', data={'interval': 1}),
# Chart content container that will be updated by callbacks # Chart content (without wrapper div to avoid callback issues)
html.Div(id='chart-content', children=[ dcc.Graph(id='live-chart', style={"height": "600px"}),
# Initial content dcc.Graph(id='secondary-charts', style={"height": "500px"}),
dcc.Graph(id='live-chart', style={"height": "600px"}), html.Div(id='positions-list')
dcc.Graph(id='secondary-charts', style={"height": "500px"}),
html.Div(id='positions-list')
])
]) ])
def _create_chart_and_controls(self): def _create_chart_and_controls(self):
@ -2016,26 +2082,20 @@ class RealTimeChart:
try: try:
# Get candles from tick storage # Get candles from tick storage
interval_key = self._get_interval_key(interval_seconds) 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: if not candles_list:
logger.warning(f"No candle data available for {interval_key}") logger.warning(f"No candle data available for {interval_key} - trying to load historical data")
return [] # Return empty list if no 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 if not candles_list:
candles = [] logger.error(f"Still no candle data available for {interval_key} after loading historical data")
for idx, row in df.iterrows(): return []
candle = {
'timestamp': idx,
'open': row['open'],
'high': row['high'],
'low': row['low'],
'close': row['close'],
'volume': row['volume']
}
candles.append(candle)
return candles logger.info(f"Retrieved {len(candles_list)} candles for {interval_key}")
return candles_list
except Exception as e: except Exception as e:
logger.error(f"Error getting candles: {str(e)}") logger.error(f"Error getting candles: {str(e)}")
@ -2197,7 +2257,11 @@ class RealTimeChart:
def run(self, host='localhost', port=8050): def run(self, host='localhost', port=8050):
"""Run the Dash app on the specified host and port""" """Run the Dash app on the specified host and port"""
try: 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) self.app.run(debug=False, use_reloader=False, host=host, port=port)
except Exception as e: except Exception as e:
logger.error(f"Error running Dash app: {str(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 # Placeholder for adding NN signals if needed in the future
pass 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"<b>{position.action} Entry</b><br>" +
f"Price: ${position.entry_price:.2f}<br>" +
f"Time: {position.entry_timestamp}<br>" +
f"ID: {position.trade_id}<extra></extra>",
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"<b>{position.action} Exit</b><br>" +
f"Price: ${position.exit_price:.2f}<br>" +
f"Time: {position.exit_timestamp}<br>" +
f"PnL: ${position.pnl:.2f}<br>" +
f"ID: {position.trade_id}<extra></extra>",
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: class BinanceWebSocket:
"""Binance WebSocket implementation for real-time tick data""" """Binance WebSocket implementation for real-time tick data"""
def __init__(self, symbol: str): def __init__(self, symbol: str):

View File

@ -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: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,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-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

153
test_chart_data.py Normal file
View File

@ -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)

View File

@ -1089,8 +1089,11 @@ async def start_realtime_chart(symbol="ETH/USDT", port=8050, manual_mode=False):
logger.info("Enabling manual trading mode") logger.info("Enabling manual trading mode")
logger.warning("Manual trading mode not supported by this simplified chart implementation") 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("="*60)
logger.info(f"You can view the chart at http://localhost:{port}/") 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 # Start websocket in the background
websocket_task = asyncio.create_task(chart.start_websocket()) websocket_task = asyncio.create_task(chart.start_websocket())