Vasily.onl dbe58e5cef Remove deprecated app_new.py and consolidate main application logic into main.py
- Deleted `app_new.py`, which was previously the main entry point for the dashboard application, to streamline the codebase.
- Consolidated the application initialization and callback registration logic into `main.py`, enhancing modularity and maintainability.
- Updated the logging and error handling practices in `main.py` to ensure consistent application behavior and improved debugging capabilities.

These changes simplify the application structure, aligning with project standards for modularity and maintainability.
2025-06-11 18:36:34 +08:00

141 lines
5.7 KiB
Python

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'<span style="color: {color}; font-weight: bold;">{icon} {status_text}</span><br><small>{status["message"]}</small>'