- Introduced `monitor_clean.py` for monitoring database status with detailed logging and status updates. - Added `production_clean.py` for running OKX data collection with minimal console output and comprehensive logging. - Implemented command-line argument parsing for both scripts to customize monitoring intervals and collection durations. - Enhanced logging capabilities to provide clear insights into data collection and monitoring processes. - Updated documentation to include usage examples and descriptions for the new scripts, ensuring clarity for users.
226 lines
7.2 KiB
Python
226 lines
7.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Clean Database Monitor
|
|
|
|
Provides clean status updates for production data collection
|
|
with detailed logging to files.
|
|
|
|
Usage:
|
|
python scripts/monitor_clean.py [--interval seconds]
|
|
|
|
Examples:
|
|
# Check status once
|
|
python scripts/monitor_clean.py
|
|
|
|
# Monitor every 60 seconds
|
|
python scripts/monitor_clean.py --interval 60
|
|
"""
|
|
|
|
import asyncio
|
|
import argparse
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
# Add project root to path
|
|
project_root = Path(__file__).parent.parent
|
|
sys.path.insert(0, str(project_root))
|
|
|
|
# Set environment for clean output
|
|
import os
|
|
os.environ['DEBUG'] = 'false'
|
|
|
|
from database.connection import DatabaseManager
|
|
from database.models import MarketData, RawTrade
|
|
from sqlalchemy import func, desc
|
|
from utils.logger import get_logger
|
|
|
|
class CleanMonitor:
|
|
"""Clean database monitor for production use."""
|
|
|
|
def __init__(self):
|
|
self.logger = get_logger("clean_monitor", verbose=False)
|
|
self.db_manager = None
|
|
|
|
def connect(self) -> bool:
|
|
"""Connect to database quietly."""
|
|
try:
|
|
self.db_manager = DatabaseManager()
|
|
self.db_manager.initialize()
|
|
return True
|
|
except Exception as e:
|
|
print(f"❌ Database connection failed: {e}")
|
|
return False
|
|
|
|
def get_summary_stats(self) -> dict:
|
|
"""Get essential statistics for console display."""
|
|
try:
|
|
with self.db_manager.get_session() as session:
|
|
# Raw data count
|
|
raw_count = session.query(func.count(RawTrade.id)).scalar()
|
|
|
|
# Candle count
|
|
candle_count = session.query(func.count(MarketData.id)).scalar()
|
|
|
|
# Time range for raw data
|
|
raw_oldest = session.query(func.min(RawTrade.timestamp)).scalar()
|
|
raw_newest = session.query(func.max(RawTrade.timestamp)).scalar()
|
|
|
|
# Recent activity (last 5 minutes)
|
|
from datetime import timedelta, timezone
|
|
cutoff = datetime.now(timezone.utc) - timedelta(minutes=5)
|
|
recent_raw = session.query(func.count(RawTrade.id)).filter(
|
|
RawTrade.created_at >= cutoff
|
|
).scalar()
|
|
recent_candles = session.query(func.count(MarketData.id)).filter(
|
|
MarketData.created_at >= cutoff
|
|
).scalar()
|
|
|
|
# Timeframe breakdown
|
|
timeframes = session.query(
|
|
MarketData.timeframe,
|
|
func.count(MarketData.id)
|
|
).group_by(MarketData.timeframe).all()
|
|
|
|
# Latest prices
|
|
latest_prices = {}
|
|
for symbol in ['BTC-USDT', 'ETH-USDT']:
|
|
latest = session.query(MarketData).filter(
|
|
MarketData.symbol == symbol,
|
|
MarketData.timeframe == '1m'
|
|
).order_by(desc(MarketData.created_at)).first()
|
|
|
|
if latest:
|
|
latest_prices[symbol] = {
|
|
'price': float(latest.close),
|
|
'time': latest.timestamp
|
|
}
|
|
|
|
return {
|
|
'raw_count': raw_count,
|
|
'candle_count': candle_count,
|
|
'raw_timespan': (raw_newest - raw_oldest).total_seconds() / 3600 if raw_oldest and raw_newest else 0,
|
|
'recent_raw': recent_raw,
|
|
'recent_candles': recent_candles,
|
|
'timeframes': dict(timeframes),
|
|
'latest_prices': latest_prices
|
|
}
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Error getting stats: {e}")
|
|
return {}
|
|
|
|
def print_status(self):
|
|
"""Print clean status summary."""
|
|
stats = self.get_summary_stats()
|
|
if not stats:
|
|
print("❌ Unable to get database statistics")
|
|
return
|
|
|
|
print("\n" + "="*50)
|
|
print(f"📊 DATA COLLECTION STATUS - {datetime.now().strftime('%H:%M:%S')}")
|
|
print("="*50)
|
|
|
|
# Main metrics
|
|
raw_count = stats.get('raw_count', 0)
|
|
candle_count = stats.get('candle_count', 0)
|
|
timespan = stats.get('raw_timespan', 0)
|
|
|
|
print(f"📈 Raw Data: {raw_count:,} entries ({timespan:.1f} hours)")
|
|
|
|
# Candle breakdown
|
|
timeframes = stats.get('timeframes', {})
|
|
if timeframes:
|
|
tf_summary = ", ".join([f"{tf}:{count}" for tf, count in timeframes.items()])
|
|
print(f"📊 Candles: {candle_count:,} total ({tf_summary})")
|
|
else:
|
|
print(f"📊 Candles: {candle_count:,} total")
|
|
|
|
# Recent activity
|
|
recent_raw = stats.get('recent_raw', 0)
|
|
recent_candles = stats.get('recent_candles', 0)
|
|
print(f"🕐 Recent (5m): {recent_raw:,} raw, {recent_candles} candles")
|
|
|
|
# Latest prices
|
|
latest_prices = stats.get('latest_prices', {})
|
|
if latest_prices:
|
|
print("💰 Latest Prices:")
|
|
for symbol, data in latest_prices.items():
|
|
price = data['price']
|
|
time_str = data['time'].strftime('%H:%M:%S')
|
|
print(f" {symbol}: ${price:,.2f} at {time_str}")
|
|
|
|
print("="*50)
|
|
|
|
def disconnect(self):
|
|
"""Disconnect from database."""
|
|
if self.db_manager:
|
|
self.db_manager.close()
|
|
|
|
async def monitor_clean(interval: int = 0):
|
|
"""Run clean monitoring."""
|
|
|
|
monitor = CleanMonitor()
|
|
|
|
try:
|
|
if not monitor.connect():
|
|
return False
|
|
|
|
if interval <= 0:
|
|
# Single check
|
|
monitor.print_status()
|
|
return True
|
|
|
|
# Continuous monitoring
|
|
print(f"📊 Monitoring every {interval} seconds (Ctrl+C to stop)")
|
|
|
|
while True:
|
|
monitor.print_status()
|
|
print(f"\n⏰ Next update in {interval} seconds...\n")
|
|
await asyncio.sleep(interval)
|
|
|
|
except KeyboardInterrupt:
|
|
print("\n👋 Monitoring stopped")
|
|
return True
|
|
except Exception as e:
|
|
print(f"❌ Monitor error: {e}")
|
|
return False
|
|
finally:
|
|
monitor.disconnect()
|
|
|
|
def main():
|
|
"""Main entry point."""
|
|
parser = argparse.ArgumentParser(
|
|
description="Clean Database Monitor",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
# Single status check
|
|
python scripts/monitor_clean.py
|
|
|
|
# Monitor every minute
|
|
python scripts/monitor_clean.py --interval 60
|
|
"""
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--interval',
|
|
type=int,
|
|
default=0,
|
|
help='Monitor interval in seconds (0 = single check, default: 0)'
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
success = asyncio.run(monitor_clean(args.interval))
|
|
sys.exit(0 if success else 1)
|
|
except KeyboardInterrupt:
|
|
print("\n👋 Exiting...")
|
|
sys.exit(0)
|
|
except Exception as e:
|
|
print(f"❌ Fatal error: {e}")
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
main() |