import pandas as pd from datetime import datetime, timezone, timedelta import plotly.graph_objects as go from database.operations import get_database_operations from utils.logger import get_logger from utils.timeframe_utils import load_timeframe_options from .builder import ChartBuilder from .utils import format_price, format_volume logger = get_logger("charts_data") def get_supported_symbols(): """Get list of symbols that have data in the database.""" builder = ChartBuilder() # Test query - consider optimizing or removing if not critical for initial check candles = builder.fetch_market_data("BTC-USDT", "1m", days_back=1) if candles: try: db = get_database_operations(logger) with db.market_data.get_session() as session: from sqlalchemy import text result = session.execute(text("SELECT DISTINCT symbol FROM market_data ORDER BY symbol")) return [row[0] for row in result] except Exception as e: logger.error(f"Error fetching supported symbols from DB: {e}") pass return ['BTC-USDT', 'ETH-USDT'] # Fallback def get_supported_timeframes(): """Get list of timeframes that have data in the database.""" builder = ChartBuilder() # Test query - consider optimizing or removing if not critical for initial check candles = builder.fetch_market_data("BTC-USDT", "1m", days_back=1) if candles: try: db = get_database_operations(logger) with db.market_data.get_session() as session: from sqlalchemy import text result = session.execute(text("SELECT DISTINCT timeframe FROM market_data ORDER BY timeframe")) return [row[0] for row in result] except Exception as e: logger.error(f"Error fetching supported timeframes from DB: {e}") pass # Fallback uses values from timeframe_options.json for consistency return [item['value'] for item in load_timeframe_options() if item['value'] in ['5s', '1m', '15m', '1h']] def get_market_statistics(symbol: str, timeframe: str = "1h", days_back: int = 1): # Changed from days_back: Union[int, float] to int """Calculate market statistics from recent data over a specified period.""" builder = ChartBuilder() candles = builder.fetch_market_data(symbol, timeframe, days_back=days_back) if not candles: return {'Price': 'N/A', f'Change ({days_back}d)': 'N/A', f'Volume ({days_back}d)': 'N/A', f'High ({days_back}d)': 'N/A', f'Low ({days_back}d)': 'N/A'} df = pd.DataFrame(candles) latest = df.iloc[-1] current_price = float(latest['close']) # Calculate change over the period if len(df) > 1: price_period_ago = float(df.iloc[0]['open']) change_percent = ((current_price - price_period_ago) / price_period_ago) * 100 else: change_percent = 0 # Determine label for period (e.g., "24h", "7d", "1h") # This part should be updated if `days_back` can be fractional again. if days_back == 1/24: period_label = "1h" elif days_back == 4/24: period_label = "4h" elif days_back == 6/24: period_label = "6h" elif days_back == 12/24: period_label = "12h" elif days_back < 1: # For other fractional days, show as hours period_label = f"{int(days_back * 24)}h" elif days_back == 1: period_label = "24h" # Keep 24h for 1 day for clarity else: period_label = f"{days_back}d" return { 'Price': format_price(current_price, decimals=2), f'Change ({period_label})': f"{'+' if change_percent >= 0 else ''}{change_percent:.2f}%", f'Volume ({period_label})': format_volume(df['volume'].sum()), f'High ({period_label})': format_price(df['high'].max(), decimals=2), f'Low ({period_label})': format_price(df['low'].min(), decimals=2) } def check_data_availability(symbol: str, timeframe: str): """Check data availability for a symbol and timeframe.""" try: db = get_database_operations(logger) latest_candle = db.market_data.get_latest_candle(symbol, timeframe) if latest_candle: latest_time = latest_candle['timestamp'] time_diff = datetime.now(timezone.utc) - latest_time.replace(tzinfo=timezone.utc) return { 'has_data': True, 'latest_timestamp': latest_time, 'time_since_last': time_diff, 'is_recent': time_diff < timedelta(hours=1), 'message': f"Latest data: {latest_time.strftime('%Y-%m-%d %H:%M:%S UTC')}" } else: return { 'has_data': False, 'latest_timestamp': None, 'time_since_last': None, 'is_recent': False, 'message': f"No data available for {symbol} {timeframe}" } except Exception as e: return { 'has_data': False, 'latest_timestamp': None, 'time_since_last': None, 'is_recent': False, 'message': f"Error checking data: {str(e)}" } def create_data_status_indicator(symbol: str, timeframe: str): """Create a data status indicator for the dashboard.""" status = check_data_availability(symbol, timeframe) if status['has_data']: if status['is_recent']: icon, color, status_text = "🟢", "#27ae60", "Real-time Data" else: icon, color, status_text = "🟡", "#f39c12", "Delayed Data" else: icon, color, status_text = "🔴", "#e74c3c", "No Data" return f'{icon} {status_text}
{status["message"]}'