CryptoMarketParser/ChartView.py
2025-03-25 08:15:27 +08:00

139 lines
5.2 KiB
Python

import mplfinance as mpf
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import matplotlib.animation as animation
import ccxt
from matplotlib.dates import date2num
import threading
import tkinter as tk
from tkinter import ttk
class ChartView:
def __init__(self, exchange_ids, symbol='BTC/USDT', timeframe='1m', limit=100):
self.exchange_ids = exchange_ids
self.symbol = symbol
self.timeframe = timeframe
self.limit = limit
self.running = False
self.fig = None
self.ax1 = None
self.ax2 = None
self.animation = None
self.current_exchange_id = exchange_ids[0]
self.root = None
self.canvas = None
# Create exchanges dictionary to avoid reconnecting each time
self.exchanges = {}
for exchange_id in exchange_ids:
self.connect_to_exchange(exchange_id)
def connect_to_exchange(self, exchange_id):
if exchange_id in self.exchanges:
self.exchange = self.exchanges[exchange_id]
return
try:
exchange_class = getattr(ccxt, exchange_id)
exchange = exchange_class({
'enableRateLimit': True,
})
exchange.load_markets()
if self.symbol not in exchange.markets:
raise Exception(f"Symbol {self.symbol} not found on {exchange_id}")
self.exchanges[exchange_id] = exchange
self.exchange = exchange
except Exception as e:
raise Exception(f"Failed to connect to {exchange_id}: {str(e)}")
def fetch_ohlcv(self, exchange_id):
try:
exchange = self.exchanges[exchange_id]
ohlcv = exchange.fetch_ohlcv(self.symbol, self.timeframe, limit=self.limit)
df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df.set_index('timestamp', inplace=True)
return df
except Exception as e:
print(f"Error fetching OHLCV data from {exchange_id}: {str(e)}")
return None
def update_chart(self):
if not self.running:
return
df = self.fetch_ohlcv(self.current_exchange_id)
if df is not None:
self.ax1.clear()
self.ax2.clear()
mpf.plot(df, type='candle', ax=self.ax1, volume=self.ax2,
style='yahoo', xrotation=0, ylabel='Price',
ylabel_lower='Volume', show_nontrading=False)
latest_price = df['close'].iloc[-1]
self.ax1.set_title(f'{self.symbol} on {self.current_exchange_id} - Last: ${latest_price:.2f}')
# Refresh the canvas
self.canvas.draw()
def on_exchange_change(self, event=None):
selected_exchange = self.exchange_var.get()
if selected_exchange != self.current_exchange_id:
self.current_exchange_id = selected_exchange
self.update_chart()
def start_gui(self):
self.root = tk.Tk()
self.root.title("Crypto Chart Viewer")
self.root.geometry("1200x800")
# Create frame for controls
control_frame = ttk.Frame(self.root)
control_frame.pack(side=tk.TOP, fill=tk.X, padx=10, pady=5)
# Dropdown for selecting exchange
ttk.Label(control_frame, text="Exchange:").pack(side=tk.LEFT, padx=(0, 5))
self.exchange_var = tk.StringVar(value=self.current_exchange_id)
exchange_dropdown = ttk.Combobox(control_frame, textvariable=self.exchange_var, values=self.exchange_ids, width=15)
exchange_dropdown.pack(side=tk.LEFT, padx=(0, 10))
exchange_dropdown.bind("<<ComboboxSelected>>", self.on_exchange_change)
# Create the figure
self.fig = plt.Figure(figsize=(12, 8), dpi=100)
self.ax1 = self.fig.add_subplot(6, 1, (1, 4))
self.ax2 = self.fig.add_subplot(6, 1, (5, 6), sharex=self.ax1)
# Create the canvas to display the figure
self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
# Add toolbar
toolbar = NavigationToolbar2Tk(self.canvas, self.root)
toolbar.update()
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
# Initial chart update
self.running = True
self.update_chart()
# Set up periodic updates
def update():
if self.running:
self.update_chart()
self.root.after(10000, update) # Update every 10 seconds
self.root.after(10000, update)
# Handle window close
def on_closing():
self.running = False
self.root.destroy()
self.root.protocol("WM_DELETE_WINDOW", on_closing)
# Start the Tkinter event loop
self.root.mainloop()