diff --git a/realtime.py b/realtime.py
index c60fc3a..7d55211 100644
--- a/realtime.py
+++ b/realtime.py
@@ -17,6 +17,8 @@ from threading import Thread
import requests
import os
from datetime import datetime, timedelta
+import pytz
+import tzlocal
# Configure logging with more detailed format
logging.basicConfig(
@@ -29,6 +31,50 @@ logging.basicConfig(
)
logger = logging.getLogger(__name__)
+# Try to get local timezone, default to Sofia/EET if not available
+try:
+ local_timezone = tzlocal.get_localzone()
+ # Get timezone name safely
+ try:
+ tz_name = str(local_timezone)
+ # Handle case where it might be zoneinfo.ZoneInfo object instead of pytz timezone
+ if hasattr(local_timezone, 'zone'):
+ tz_name = local_timezone.zone
+ elif hasattr(local_timezone, 'key'):
+ tz_name = local_timezone.key
+ else:
+ tz_name = str(local_timezone)
+ except:
+ tz_name = "Local"
+ logger.info(f"Detected local timezone: {local_timezone} ({tz_name})")
+except Exception as e:
+ logger.warning(f"Could not detect local timezone: {str(e)}. Defaulting to Sofia/EET")
+ local_timezone = pytz.timezone('Europe/Sofia')
+ tz_name = "Europe/Sofia"
+
+def convert_to_local_time(timestamp):
+ """Convert timestamp to local timezone"""
+ try:
+ if isinstance(timestamp, pd.Timestamp):
+ dt = timestamp.to_pydatetime()
+ elif isinstance(timestamp, np.datetime64):
+ dt = pd.Timestamp(timestamp).to_pydatetime()
+ elif isinstance(timestamp, str):
+ dt = pd.to_datetime(timestamp).to_pydatetime()
+ else:
+ dt = timestamp
+
+ # If datetime is naive (no timezone), assume it's UTC
+ if dt.tzinfo is None:
+ dt = dt.replace(tzinfo=pytz.UTC)
+
+ # Convert to local timezone
+ local_dt = dt.astimezone(local_timezone)
+ return local_dt
+ except Exception as e:
+ logger.error(f"Error converting timestamp to local time: {str(e)}")
+ return timestamp
+
class TradeTickStorage:
"""Store and manage raw trade ticks for display and candle formation"""
def __init__(self, max_age_seconds: int = 1800): # 30 minutes by default
@@ -1110,10 +1156,19 @@ class RealTimeChart:
logger.debug(f"Displaying {len(display_df)} candles in main chart")
+ # Convert timestamps to local time for display
+ display_df = display_df.copy()
+ try:
+ display_df['local_time'] = display_df['timestamp'].apply(convert_to_local_time)
+ logger.debug(f"Converted timestamps to local time ({local_timezone})")
+ except Exception as e:
+ logger.error(f"Error converting timestamps to local time: {str(e)}")
+ display_df['local_time'] = display_df['timestamp']
+
# Add candlestick chart
fig.add_trace(
go.Candlestick(
- x=display_df['timestamp'],
+ x=display_df['local_time'], # Use local time for display
open=display_df['open'],
high=display_df['high'],
low=display_df['low'],
@@ -1131,7 +1186,7 @@ class RealTimeChart:
fig.add_trace(
go.Bar(
- x=display_df['timestamp'],
+ x=display_df['local_time'], # Use local time for display
y=display_df['volume'],
name='Volume',
marker_color=colors
@@ -1141,21 +1196,30 @@ class RealTimeChart:
# Add latest price line from the candlestick data
latest_price = display_df['close'].iloc[-1]
+ latest_time = display_df['local_time'].iloc[-1]
+ earliest_time = display_df['local_time'].iloc[0]
+
fig.add_shape(
type="line",
- x0=display_df['timestamp'].min(),
+ x0=earliest_time,
y0=latest_price,
- x1=display_df['timestamp'].max(),
+ x1=latest_time,
y1=latest_price,
line=dict(color="yellow", width=1, dash="dash"),
row=1, col=1
)
+ # Format the last update time in local timezone
+ try:
+ last_update_time = latest_time.strftime('%Y-%m-%d %H:%M:%S')
+ except:
+ last_update_time = datetime.now().astimezone(local_timezone).strftime('%Y-%m-%d %H:%M:%S')
+
# Annotation for last candle close price
fig.add_annotation(
- x=display_df['timestamp'].max(),
+ x=latest_time,
y=latest_price,
- text=f"{latest_price:.2f}",
+ text=f"{latest_price:.2f} ({last_update_time})",
showarrow=False,
font=dict(size=14, color="yellow"),
xshift=50,
@@ -1167,19 +1231,22 @@ class RealTimeChart:
# Add current price line
fig.add_shape(
type="line",
- x0=display_df['timestamp'].min(),
+ x0=earliest_time,
y0=current_price,
- x1=display_df['timestamp'].max(),
+ x1=latest_time,
y1=current_price,
line=dict(color="cyan", width=1, dash="dot"),
row=1, col=1
)
+ # Format current time in local timezone
+ current_time_str = datetime.now().astimezone(local_timezone).strftime('%Y-%m-%d %H:%M:%S')
+
# Add current price annotation
fig.add_annotation(
- x=display_df['timestamp'].max(),
+ x=latest_time,
y=current_price,
- text=f"Current: {current_price:.2f}",
+ text=f"Current: {current_price:.2f} ({current_time_str})",
showarrow=False,
font=dict(size=14, color="cyan"),
xshift=50,
@@ -1221,13 +1288,21 @@ class RealTimeChart:
if len(ohlcv_df) > 100:
ohlcv_df = ohlcv_df.tail(100)
+ # Convert to local time
+ ohlcv_display_df = ohlcv_df.copy()
+ try:
+ ohlcv_display_df['local_time'] = ohlcv_df['timestamp'].apply(convert_to_local_time)
+ except Exception as e:
+ logger.error(f"Error converting {interval_key} timestamps to local time: {str(e)}")
+ ohlcv_display_df['local_time'] = ohlcv_df['timestamp']
+
fig.add_trace(
go.Candlestick(
- x=ohlcv_df['timestamp'],
- open=ohlcv_df['open'],
- high=ohlcv_df['high'],
- low=ohlcv_df['low'],
- close=ohlcv_df['close'],
+ x=ohlcv_display_df['local_time'],
+ open=ohlcv_display_df['open'],
+ high=ohlcv_display_df['high'],
+ low=ohlcv_display_df['low'],
+ close=ohlcv_display_df['close'],
name=f'{label}',
increasing_line_color='#33CC33',
decreasing_line_color='#FF4136',
@@ -1237,23 +1312,33 @@ class RealTimeChart:
)
# Add latest price line
- latest_timeframe_price = ohlcv_df['close'].iloc[-1] if len(ohlcv_df) > 0 else None
+ latest_timeframe_price = ohlcv_display_df['close'].iloc[-1] if len(ohlcv_display_df) > 0 else None
if latest_timeframe_price:
+ # Get first and last timestamps
+ earliest_tf_time = ohlcv_display_df['local_time'].iloc[0]
+ latest_tf_time = ohlcv_display_df['local_time'].iloc[-1]
+
fig.add_shape(
type="line",
- x0=ohlcv_df['timestamp'].min(),
+ x0=earliest_tf_time,
y0=latest_timeframe_price,
- x1=ohlcv_df['timestamp'].max(),
+ x1=latest_tf_time,
y1=latest_timeframe_price,
line=dict(color="yellow", width=1, dash="dash"),
row=row_idx, col=1
)
+ # Get formatted time
+ try:
+ update_time = latest_tf_time.strftime('%Y-%m-%d %H:%M')
+ except:
+ update_time = ""
+
# Add price annotation
fig.add_annotation(
- x=ohlcv_df['timestamp'].max(),
+ x=latest_tf_time,
y=latest_timeframe_price,
- text=f"{latest_timeframe_price:.2f}",
+ text=f"{latest_timeframe_price:.2f} ({update_time})",
showarrow=False,
font=dict(size=12, color="yellow"),
xshift=50,
@@ -1279,13 +1364,21 @@ class RealTimeChart:
if len(ohlcv_df) > 100:
ohlcv_df = ohlcv_df.tail(100)
+ # Convert to local time
+ ohlcv_display_df = ohlcv_df.copy()
+ try:
+ ohlcv_display_df['local_time'] = ohlcv_df['timestamp'].apply(convert_to_local_time)
+ except Exception as e:
+ logger.error(f"Error converting {interval_key} timestamps to local time: {str(e)}")
+ ohlcv_display_df['local_time'] = ohlcv_df['timestamp']
+
fig.add_trace(
go.Candlestick(
- x=ohlcv_df['timestamp'],
- open=ohlcv_df['open'],
- high=ohlcv_df['high'],
- low=ohlcv_df['low'],
- close=ohlcv_df['close'],
+ x=ohlcv_display_df['local_time'],
+ open=ohlcv_display_df['open'],
+ high=ohlcv_display_df['high'],
+ low=ohlcv_display_df['low'],
+ close=ohlcv_display_df['close'],
name=f'{label}',
increasing_line_color='#33CC33',
decreasing_line_color='#FF4136',
@@ -1293,6 +1386,40 @@ class RealTimeChart:
),
row=row_idx, col=1
)
+
+ # Add latest price line
+ latest_timeframe_price = ohlcv_display_df['close'].iloc[-1] if len(ohlcv_display_df) > 0 else None
+ if latest_timeframe_price:
+ # Get first and last timestamps
+ earliest_tf_time = ohlcv_display_df['local_time'].iloc[0]
+ latest_tf_time = ohlcv_display_df['local_time'].iloc[-1]
+
+ fig.add_shape(
+ type="line",
+ x0=earliest_tf_time,
+ y0=latest_timeframe_price,
+ x1=latest_tf_time,
+ y1=latest_timeframe_price,
+ line=dict(color="yellow", width=1, dash="dash"),
+ row=row_idx, col=1
+ )
+
+ # Get formatted time
+ try:
+ update_time = latest_tf_time.strftime('%Y-%m-%d %H:%M')
+ except:
+ update_time = ""
+
+ # Add price annotation
+ fig.add_annotation(
+ x=latest_tf_time,
+ y=latest_timeframe_price,
+ text=f"{latest_timeframe_price:.2f} ({update_time})",
+ showarrow=False,
+ font=dict(size=12, color="yellow"),
+ xshift=50,
+ row=row_idx, col=1
+ )
# Add a message to the empty main chart
fig.add_annotation(
@@ -1317,6 +1444,10 @@ class RealTimeChart:
# Build info box text with all the statistics
info_lines = [f"{self.symbol}"]
+ # Add current time in local timezone
+ current_local_time = datetime.now().astimezone(local_timezone)
+ info_lines.append(f"Time: {current_local_time.strftime('%Y-%m-%d %H:%M:%S')} ({tz_name})")
+
# Add current price if available
if current_price:
info_lines.append(f"Current: {current_price:.2f} USDT")