You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python Lightweight Charts多子图(25+)币安Websocket价格更新延迟问题求助

Python Lightweight Charts多子图(25+)币安Websocket价格更新延迟问题求助

大家好,我最近碰到一个特别头疼的问题:我用Python的lightweight-charts库,通过create_sub_chart创建多个子图表,同时对接币安的Websocket来实时更新K线价格。但当我创建25个甚至更多子图时,价格更新变得异常缓慢,图表上的价格总是滞后很久,完全没法实现实时显示的需求。

我把自己的代码贴在下面,麻烦各位帮忙看看问题出在哪,有没有优化的方向可以推荐?

import threading
import time
from time import sleep
import ccxt
import pandas as pd
import json
import websocket
from lightweight_charts import Chart
import asyncio
from binance.streams import BinanceSocketManager
from binance.client import Client
import tkinter as tk
from tkinter import messagebox
from datetime import datetime, timedelta
import numpy as np

api_key = "your_api_key_here"
api_secret = "your_secret_key_here"
client = Client(api_key, api_secret)

PreviousPositionList = []


def calculate_percentage_change(price1, price2):
    try:
        return ((price2 - price1) / price1) * 100
    except ZeroDivisionError:
        return 0


def create_tool_window(chart):
    window = tk.Tk()
    window.title("Chart Tools")
    label = tk.Label(window, text="Price Percentage: ", font=("Arial", 12))
    label.pack(pady=10)
    draw_button = tk.Button(window, text="Enable Drawing Tool", command=lambda: enable_drawing_tool(chart))
    draw_button.pack(pady=5)
    entry1_label = tk.Label(window, text="Enter price 1:", font=("Arial", 10))
    entry1_label.pack(pady=5)
    price1_entry = tk.Entry(window)
    price1_entry.pack(pady=5)
    entry2_label = tk.Label(window, text="Enter price 2:", font=("Arial", 10))
    entry2_label.pack(pady=5)
    price2_entry = tk.Entry(window)
    price2_entry.pack(pady=5)

    def calculate_percentage():
        try:
            price1 = float(price1_entry.get())
            price2 = float(price2_entry.get())
            percentage_change = calculate_percentage_change(price1, price2)
            label.config(text=f"Price Percentage: {percentage_change:.2f}%")
        except ValueError:
            messagebox.showerror("Invalid Input", "Please enter valid numeric prices.")

    calculate_button = tk.Button(window, text="Calculate Percentage", command=calculate_percentage)
    calculate_button.pack(pady=5)
    window.mainloop()


def BigSMAUpdater(ClosesDF, chart, sma_data, RayLines, FunctionRunning):
    time.sleep(1)

    def calculate_sma(closes, window=7):
        closes = pd.to_numeric(closes, errors='coerce')  # This will convert non-numeric values to NaN
        closes = closes[~np.isnan(closes)]
        if len(closes) < window:
            return None
        return np.mean(closes[-window:])

    ClosesDF['SMA_7'] = ClosesDF['Closes'].apply(lambda x: calculate_sma(x, window=7))
    ClosesDF['SMA_25'] = ClosesDF['Closes'].apply(lambda x: calculate_sma(x, window=25))
    ClosesDF['SMA_99'] = ClosesDF['Closes'].apply(lambda x: calculate_sma(x, window=99))

    if len(RayLines) != 0:
        for TimeFrame, SMA7, SMA25, SMA99 in zip(ClosesDF['Timeframe'], ClosesDF['SMA_7'], ClosesDF['SMA_25'],
                                                 ClosesDF['SMA_99']):
            if len(RayLines) != 0:
                RayDataFrame = pd.DataFrame(RayLines, columns=['Ray', 'TimeFrame', 'Type'])
                for Ray, RayTimeFrame, RayType in zip(RayDataFrame['Ray'], RayDataFrame['TimeFrame'],
                                                      RayDataFrame['Type']):
                    if RayTimeFrame == TimeFrame:
                        if RayType == 'SMA_7':
                            Ray.delete()
                            RayLines.remove([Ray, RayTimeFrame, "SMA_7"])
                            Ray = chart.ray_line(sma_data[-5]['time'], value=SMA7, color="#ffff00")
                            RayLines.append([Ray, RayTimeFrame, "SMA_7"])
                        elif RayType == 'SMA_25':
                            Ray.delete()
                            RayLines.remove([Ray, RayTimeFrame, "SMA_25"])
                            Ray = chart.ray_line(sma_data[-5]['time'], value=SMA25, color="#ff3399")
                            RayLines.append([Ray, RayTimeFrame, "SMA_25"])
                        elif RayType == 'SMA_99':
                            Ray.delete()
                            RayLines.remove([Ray, RayTimeFrame, "SMA_99"])
                            Ray = chart.ray_line(sma_data[-5]['time'], value=SMA99, color="#00ccff")
                            RayLines.append([Ray, RayTimeFrame, "SMA_99"])
    else:
        for TimeFrame, SMA7, SMA25, SMA99 in zip(ClosesDF['Timeframe'], ClosesDF['SMA_7'], ClosesDF['SMA_25'],
                                                 ClosesDF['SMA_99']):
            Ray = chart.ray_line(sma_data[-5]['time'], value=SMA7, color="#ffff00")
            RayLines.append([Ray, TimeFrame, "SMA_7"])
            Ray = chart.ray_line(sma_data[-5]['time'], value=SMA25, color="#ff3399")
            RayLines.append([Ray, TimeFrame, "SMA_25"])
            Ray = chart.ray_line(sma_data[-5]['time'], value=SMA99, color="#00ccff")
            RayLines.append([Ray, TimeFrame, "SMA_99"])
    FunctionRunning.append(False)


def binance_kline_stream(symbol, interval, line, line2, line3, chart, sma_data, RayLines):
    FunctionRunning = []

    async def process_kline_data(coin, line, line2, line3, chart, sma_data, RayLines):
        bm = BinanceSocketManager(client)
        ts = bm.kline_futures_socket(symbol=coin, interval=interval)

        async with ts as tscm:
            while True:
                res = await tscm.recv()
                if res:
                    candle = res['k']
                    close = candle['c']
                    open = candle['o']
                    high = candle['h']
                    low = candle['l']
                    time = pd.to_datetime(candle['t'], unit='ms')
                    print(symbol, candle)
                    if time != sma_data[-1]['time']:
                        sma_data.append({'time': time, 'open': open, 'high': high, 'low': low, 'close': close})
                    else:
                        sma_data[-1] = {'time': time, 'open': open, 'high': high, 'low': low, 'close': close}
                    DataFrame = pd.DataFrame([{
                        'time': pd.to_datetime(candle['t'], unit='ms'),
                        'open': float(open),
                        'high': float(high),
                        'low': float(low),
                        'close': float(close),
                    }])
                    DataFrame['price'] = DataFrame['close']

                    if len(sma_data) >= 99:
                        sma_df = pd.DataFrame(sma_data)
                        sma99 = sma_df['close'].rolling(window=99).mean()
                        sma25 = sma_df['close'].rolling(window=25).mean()
                        sma7 = sma_df['close'].rolling(window=7).mean()
                        sma_df['SMA 99'] = sma99
                        sma_df['SMA 25'] = sma25
                        sma_df['SMA 7'] = sma7
                        sma_df['price'] = sma_df['close']
                        sma_df['price'] = pd.to_numeric(sma_df['price'], errors='coerce')
                        sma_df['SMA 99'] = pd.to_numeric(sma_df['SMA 99'], errors='coerce')
                        sma_df['SMA 25'] = pd.to_numeric(sma_df['SMA 25'], errors='coerce')
                        sma_df['SMA 7'] = pd.to_numeric(sma_df['SMA 7'], errors='coerce')
                        DataFrame = sma_df.tail(1)
                        for i, tick in DataFrame.iterrows():
                            chart.update(tick,_from_tick=True)
                            line.update(tick)
                            line2.update(tick)
                            line3.update(tick)

    async def main(coin, line, line2, line3, chart, sma_data, RayLines):
        await process_kline_data(coin, line, line2, line3, chart, sma_data, RayLines)

    asyncio.run(main(symbol, line, line2, line3, chart, sma_data, RayLines))


def calculate_sma(df, period: int = 99):
    return pd.DataFrame({
        'time': df['time'],
        f'SMA {period}': df['close'].rolling(window=period).mean()
    }).dropna()


def ChartCreater(Coin, chart):
    Data = ccxt.binanceusdm().fetch_ohlcv(symbol=Coin, timeframe="5m", limit=1000)
    DataFrame = pd.DataFrame(Data, columns=['Timestamp', 'open', 'high', 'low', 'close', 'volume'])
    DataFrame['Timestamp'] = pd.to_datetime(DataFrame['Timestamp'], unit='ms')
    DataFrame['time'] = DataFrame['Timestamp']
    DataToPlot = DataFrame[['time', 'open', 'high', 'low', 'close']]

    if PreviousPositionList[-1] == 'left':
        chart2 = chart.create_subchart(position="right", width=0.45, height=0.3, CountDown=300)
        PreviousPositionList.append('right')
    else:
        chart2 = chart.create_subchart(position="left", width=0.45, height=0.3, CountDown=300)
        PreviousPositionList.append('left')
    chart2.set(DataToPlot)
    chart2.watermark(f'{Coin}', color='rgba(180, 180, 240, 1)', horzAlign='center', vertAlign='top')

    line = chart2.create_line('SMA 7', color='rgba(255, 255, 0,1)', width=0.5, price_label=False, price_line=False)
    sma7_data = calculate_sma(DataToPlot, period=7)
    line.set(sma7_data)

    line2 = chart2.create_line('SMA 25', color='rgba(255, 51, 204,1)', width=0.5, price_label=False, price_line=False)
    sma25_data = calculate_sma(DataToPlot, period=25)
    line2.set(sma25_data)

    line3 = chart2.create_line('SMA 99', color='rgba(0, 102, 255,1)', width=0.5, price_label=False, price_line=False)
    sma99_data = calculate_sma(DataToPlot, period=99)
    line3.set(sma99_data)

我自己初步排查了一下,怀疑是不是每个子图都单独开启了Websocket连接导致资源占用过高?或者是SMA计算的逻辑太耗时?但不确定具体是哪个环节拖慢了整体速度,希望大家能给点思路,谢谢!

备注:内容来源于stack exchange,提问作者Pasha Pasha

火山引擎 最新活动