highfestiva/finplot

[Question not an issue:] Run finplot without blocking main thread

dhinesh03 opened this issue · 6 comments

First of all thanks for your awesome library. I'm newbie to python, I need to run finplot without blocking main thread.
I have the following class, everything works as expected, but it blocks my main thread, i've lot of logics on main thread. it manages multiple background thread to collect data from multiple data sources.

`
import pandas as pd
import finplot as fplt

ax = fplt.create_plot('TradingView')

class PlotScheme(object):

def __init__(self, strategy):
    self.plots = []
    self.strategy = strategy
    self.candle = self.strategy.candle

def update(self):
    candlesticks = pd.DataFrame(columns=['datetime', 'open', 'close', 'high', 'low'])
    candlesticks['datetime'] = self.candle.datetime
    candlesticks['open'] = self.candle.open
    candlesticks['close'] = self.candle.close
    candlesticks['high'] = self.candle.high
    candlesticks['low'] = self.candle.low

    indicators = pd.DataFrame(columns=['datetime', 'SPH', 'SPL', 'LPH', 'LPL', 'Average', 'Average_long'])
    indicators['datetime'] = self.candle.datetime
    indicators['SPH'] = self.strategy.spm.lines.SPH
    indicators['SPL'] = self.strategy.spm.lines.SPL
    indicators['LPH'] = self.strategy.spm.lines.LPH
    indicators['LPL'] = self.strategy.spm.lines.LPL
    indicators['Average'] = self.strategy.bar_size.lines.av
    indicators['Average_long'] = self.strategy.large_bar_size.lines.av

    LPL_indicator = indicators['datetime LPL'.split()]
    SPL_indicator = indicators['datetime SPL'.split()]
    LPH_indicator = indicators['datetime LPH'.split()]
    SPH_indicator = indicators['datetime SPH'.split()]

    if not self.plots:
        # first time we create the plots
        self.plots.append(fplt.candlestick_ochl(candlesticks, ax=ax))
        self.plots.append(fplt.plot(LPH_indicator, ax=ax, color='#105900', width=2, legend='LPH'))
        self.plots.append(fplt.plot(SPH_indicator, ax=ax, color='#1a8c00', width=1, legend='SPH'))
        self.plots.append(fplt.plot(SPL_indicator, ax=ax, color='#FF5733', width=1, legend='SPL'))
        self.plots.append(fplt.plot(LPL_indicator, ax=ax, color='#900C3F', width=2, legend='LPL'))
    else:
        self.plots[0].update_data(candlesticks)
        self.plots[1].update_data(LPH_indicator)
        self.plots[1].update_data(SPH_indicator)
        self.plots[1].update_data(SPL_indicator)
        self.plots[1].update_data(LPL_indicator)

def plot(self):
    self.update()
    fplt.timer_callback(self.update, 2.0)  # update every N seconds
    fplt.show()

`

please guide me to show the fplt without blocking main thread. i've tried using thread but its not working.

This works in a thread:

#!/usr/bin/env python3

import finplot as fplt
import yfinance as yf
import time
import threading

def run():
    fplt.create_plot('TradingView')
    load_candles = lambda: yf.download('GLEN.L', '2020-10-20', interval='1m')[['Open','Close','High','Low']]
    plot = fplt.candlestick_ochl(load_candles())
    def update():
        plot.update_data(load_candles())
    fplt.timer_callback(update, 30)
    fplt.show()
threading.Thread(target=run).start()
time.sleep(70)

It seems the problem stems from creating the window in another thread from the one you're showing from. I guess perhaps this. As long as you create the window (create_plot()) in the same thread as you show in, you're good. Try uncommenting the line ax = fplt.create_plot('TradingView') and hopefully everything works!

Thanks for your quick response. I have been receiving the following error
WARNING: QApplication was not created in the main() thread.
2020-10-21 22:41:33.484 Python[8982:236287] WARNING: NSWindow drag regions should only be invalidated on the Main Thread! This will throw an exception in the future. Called from (
0 AppKit 0x00007fff4ae06607 -[NSWindow(NSWindow_Theme) _postWindowNeedsToResetDragMarginsUnlessPostingDisabled] + 378
1 AppKit 0x00007fff4ae039f7 -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 1479
2 AppKit 0x00007fff4ae0342a -[NSWindow initWithContentRect:styleMask:backing:defer:] + 45
3 AppKit 0x00007fff4b0cfd08 -[NSWindow initWithContentRect:styleMask:backing:defer:screen:] + 52
4 libqcocoa.dylib 0x000000011408ce75 qt_plugin_instance + 159781
5 libqcocoa.dylib 0x000000011407af40 qt_plugin_instance + 86256
6 libqcocoa.dylib 0x0000000114074b62 qt_plugin_instance + 60690
7 libqcocoa.dylib 0x00000001140745eb qt_plugin_instance + 59291
8 QtGui 0x00000001119f207c _ZN14QWindowPrivate6createEby + 140
9 QtWidgets 0x0000000112e4b731 _ZN14QWidgetPrivate10create_sysEybb + 1201
10 QtWidgets 0x0000000112e4a3e6 _ZN7QWidget6createEybb + 326
11 QtWidgets 0x0000000112e5d963 _ZN14QWidgetPrivate10setVisibleEb + 195
12 QtWidgets.so 0x000000011296f24e _ZN16sipQGraphicsView10setVisibleEb + 110
13 QtWidgets.so 0x0000000112afe76b ZL17meth_QWidget_showP7_objectS0 + 75
14 Python 0x0000000104738f92 cfunction_call_varargs + 290
15 Python 0x0000000104738955 _PyObject_MakeTpCall + 373
16 Python 0x0000000104806eb5 call_function + 533
17 Python 0x0000000104803c6d _PyEval_EvalFrameDefault + 25677
18 Python 0x00000001047392a0 function_code_fastcall + 128
19 Python 0x0000000104806e5c call_function + 444
20 Python 0x0000000104803c6d _PyEval_EvalFrameDefault + 25677
21 Python 0x00000001047392a0 function_code_fastcall + 128
22 Python 0x000000010473b9ab method_vectorcall + 235
23 Python 0x0000000104738c7d PyVectorcall_Call + 109
24 Python 0x0000000104804178 _PyEval_EvalFrameDefault + 26968
25 Python 0x00000001047392a0 function_code_fastcall + 128
26 Python 0x0000000104806e5c call_function + 444
27 Python 0x0000000104803c49 _PyEval_EvalFrameDefault + 25641
28 Python 0x00000001047392a0 function_code_fastcall + 128
29 Python 0x0000000104806e5c call_function + 444
30 Python 0x0000000104803c49 _PyEval_EvalFrameDefault + 25641
31 Python 0x00000001047392a0 function_code_fastcall + 128
32 Python 0x000000010473b9ab method_vectorcall + 235
33 Python 0x0000000104738c7d PyVectorcall_Call + 109
34 Python 0x00000001048a61da t_bootstrap + 74
35 Python 0x0000000104859aa9 pythread_wrapper + 25
36 libsystem_pthread.dylib 0x00007fff7992c2eb _pthread_body + 126
37 libsystem_pthread.dylib 0x00007fff7992f249 _pthread_start + 66
38 libsystem_pthread.dylib 0x00007fff7992b40d thread_start + 13
)
Segmentation fault: 11

Thank you

I want to perform historical testing, loading data incrementally using historical data. I need to speed up the chart display, but currently, it's slow because it redraws the entire chart each time instead of appending a new candlestick.
def updataCsvData(self):
#self.thread.start()
logger.info(self.interface.strategyTesterUI.runBacktestBtn.text())
if self.interface.strategyTesterUI.runBacktestBtn.text() == "Run":
self.timer = fplt.timer_callback(self.timerUpdataCsvData, 0.1)
self.interface.strategyTesterUI.runBacktestBtn.setText("Stop")
else:
self.timer.stop()
self.timer.deleteLater()
self.interface.strategyTesterUI.runBacktestBtn.setText("Run")

def timerUpdataCsvData(self):
    self.dataFile.index += 1
    if self.timeFrame == self.dataFile.timeFrame:
        self.df = self.dataFile.dataFrame.head(self.dataFile.index)
    else:
        newdf = self.dataFile.dataFrame.iloc[self.dataFile.index:self.dataFile.index+1]
        newdf = newdf.resample(self.timeFrame).agg({'Open': 'first', 'High': 'max', 'Low': 'min', 'Close': 'last', 'Volume': 'sum'})
        if newdf.index[0] == self.df.index[-1]:
            newdf = pd.concat([self.df.tail(1), newdf], ignore_index=False).resample(self.timeFrame).agg({'Open': 'first', 'High': 'max', 'Low': 'min', 'Close': 'last', 'Volume': 'sum'})

        self.df.loc[newdf.index[-1]] = newdf.iloc[-1]

    self.interface.fpltWindow.data = self.df

“”“
Here the entire database is updated each time.
“””
self.interface.fpltWindow.item["ax0"].update_data(self.df['Open Close High Low'.split()])
self.interface.fpltWindow.item["ax1"].update_data(self.df['Open Close Volume'.split()])
if self.interface.strategyChartsUI.checkBoxMA.isChecked():
self.interface.fpltWindow.item["Ema"].update_data(self.df['Open Close High Low'.split()])
fplt.refresh()