2025-08-26 17:22:07 +08:00
|
|
|
import logging
|
|
|
|
|
import typer
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from datetime import datetime, timezone
|
2025-09-10 15:39:16 +08:00
|
|
|
import subprocess
|
|
|
|
|
import time
|
|
|
|
|
import threading
|
|
|
|
|
from db_interpreter import DBInterpreter
|
|
|
|
|
from ohlc_processor import OHLCProcessor
|
|
|
|
|
from desktop_app import MainWindow
|
|
|
|
|
import sys
|
|
|
|
|
from PySide6.QtWidgets import QApplication
|
2025-08-26 17:22:07 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(instrument: str = typer.Argument(..., help="Instrument to backtest, e.g. BTC-USDT"),
|
2025-09-10 15:39:16 +08:00
|
|
|
start_date: str = typer.Argument(..., help="Start date, e.g. 2025-07-01"),
|
|
|
|
|
end_date: str = typer.Argument(..., help="End date, e.g. 2025-08-01"),
|
|
|
|
|
window_seconds: int = typer.Option(60, help="OHLC window size in seconds")):
|
|
|
|
|
"""
|
|
|
|
|
Process orderbook data and visualize OHLC charts in real-time.
|
|
|
|
|
"""
|
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
|
|
|
|
|
2025-08-26 17:22:07 +08:00
|
|
|
start_date = datetime.strptime(start_date, "%Y-%m-%d").replace(tzinfo=timezone.utc)
|
|
|
|
|
end_date = datetime.strptime(end_date, "%Y-%m-%d").replace(tzinfo=timezone.utc)
|
|
|
|
|
|
2025-09-10 15:39:16 +08:00
|
|
|
databases_path = Path("../data/OKX")
|
|
|
|
|
|
|
|
|
|
if not databases_path.exists():
|
|
|
|
|
logging.error(f"Database path does not exist: {databases_path}")
|
|
|
|
|
return
|
|
|
|
|
|
2025-08-26 17:22:07 +08:00
|
|
|
db_paths = list(databases_path.glob(f"{instrument}*.db"))
|
|
|
|
|
db_paths.sort()
|
2025-09-10 15:39:16 +08:00
|
|
|
|
|
|
|
|
if not db_paths:
|
|
|
|
|
logging.error(f"No database files found for instrument {instrument} in {databases_path}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
logging.info(f"Found {len(db_paths)} database files: {[p.name for p in db_paths]}")
|
2025-08-26 17:22:07 +08:00
|
|
|
|
2025-09-10 15:39:16 +08:00
|
|
|
processor = OHLCProcessor(window_seconds=window_seconds)
|
2025-08-26 17:22:07 +08:00
|
|
|
|
2025-09-10 15:39:16 +08:00
|
|
|
def process_data():
|
|
|
|
|
"""Process database data in a separate thread."""
|
|
|
|
|
try:
|
|
|
|
|
for db_path in db_paths:
|
|
|
|
|
db_name_parts = db_path.name.split(".")[0].split("-")
|
|
|
|
|
if len(db_name_parts) < 5:
|
|
|
|
|
logging.warning(f"Unexpected filename format: {db_path.name}")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
db_name = db_name_parts[2:5]
|
|
|
|
|
db_date = datetime.strptime("".join(db_name), "%y%m%d").replace(tzinfo=timezone.utc)
|
2025-08-26 17:22:07 +08:00
|
|
|
|
2025-09-10 15:39:16 +08:00
|
|
|
if db_date < start_date or db_date >= end_date:
|
|
|
|
|
logging.info(f"Skipping {db_path.name} - outside date range")
|
|
|
|
|
continue
|
2025-08-26 17:22:07 +08:00
|
|
|
|
2025-09-10 15:39:16 +08:00
|
|
|
logging.info(f"Processing database: {db_path.name}")
|
|
|
|
|
db_interpreter = DBInterpreter(db_path)
|
|
|
|
|
|
|
|
|
|
batch_count = 0
|
|
|
|
|
for orderbook_update, trades in db_interpreter.stream():
|
|
|
|
|
batch_count += 1
|
|
|
|
|
|
|
|
|
|
processor.process_trades(trades)
|
|
|
|
|
processor.update_orderbook(orderbook_update)
|
|
|
|
|
|
|
|
|
|
processor.finalize()
|
|
|
|
|
logging.info("Data processing completed")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.error(f"Error in data processing: {e}")
|
2025-08-26 17:22:07 +08:00
|
|
|
|
2025-09-10 15:39:16 +08:00
|
|
|
try:
|
|
|
|
|
app = QApplication(sys.argv)
|
|
|
|
|
desktop_app = MainWindow()
|
|
|
|
|
|
|
|
|
|
desktop_app.setup_data_processor(processor)
|
|
|
|
|
desktop_app.show()
|
2025-08-26 17:22:07 +08:00
|
|
|
|
2025-09-10 15:39:16 +08:00
|
|
|
logging.info("Desktop visualizer started")
|
2025-08-26 17:22:07 +08:00
|
|
|
|
2025-09-10 15:39:16 +08:00
|
|
|
data_thread = threading.Thread(target=process_data, daemon=True)
|
|
|
|
|
data_thread.start()
|
2025-08-26 17:22:07 +08:00
|
|
|
|
2025-09-10 15:39:16 +08:00
|
|
|
app.exec()
|
2025-08-26 17:22:07 +08:00
|
|
|
|
2025-09-10 15:39:16 +08:00
|
|
|
except Exception as e:
|
|
|
|
|
logging.error(f"Failed to start desktop visualizer: {e}")
|
2025-08-26 17:22:07 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
typer.run(main)
|