highfestiva / finplot

Performant and effortless finance plotting for Python
MIT License
940 stars 187 forks source link

[BUG] on update, indicators below price chart are not scaled properly #266

Closed SpiffSpaceman closed 3 years ago

SpiffSpaceman commented 3 years ago

Hello, Thank you for this great tool.

There is maybe an issue in initial autoscaling on charts below the top one, when i change the stock.

I have slightly modified embed example, and drawn macd below main chart. If you keep changing stocks, eventually you will see that macd panel is blank or not scaled correctly. This gets fixed immediately on zooming.

I dont really understand much of the code, but on debugging, what i saw was that fplt.refresh() does not call update_y_zoom() for ax_2. And maybe that's the issue for this use case. i was able to do a workaround fix by calling it in updateYZoom() below and for me it looks ok. Ofc, it might break some other use case, please have a look. thanks

# !/usr/bin/env python3
import finplot as fplt
from functools import lru_cache
from PyQt5.QtWidgets import QApplication, QGridLayout, QGraphicsView, QComboBox, QLabel
from threading import Thread
import yfinance as yf

app = QApplication([])
win = QGraphicsView()
win.setWindowTitle('TradingView wannabe')
layout = QGridLayout()
win.setLayout(layout)
win.resize(600, 500)

combo = QComboBox()
combo.setEditable(True)
[combo.addItem(i) for i in 'AMRK FB GFN REVG TSLA TWTR WMT CT=F GC=F ^FTSE ^N225 EURUSD=X ETH-USD'.split()]
layout.addWidget(combo, 0, 0, 1, 1)
info = QLabel()
layout.addWidget(info, 0, 1, 1, 1)

ax, ax_2 = fplt.create_plot(init_zoom_periods=100, rows=2 )
win.axs = [ax, ax_2]  # finplot requres this property
axo = ax.overlay()
layout.addWidget(ax.vb.win, 1, 0, 1, 2)

@lru_cache(maxsize=15)
def download(symbol):
    return yf.download(symbol, '2019-01-01')

@lru_cache(maxsize=100)
def get_name(symbol):
    return yf.Ticker(symbol).info['shortName']

plots = []

def update(txt):
    df = download(txt)
    if len(df) < 20:  # symbol does not exist
        return
    info.setText('Loading symbol name...')
    price = df['Open Close High Low'.split()]
    ma20 = df.Close.rolling(20).mean()
    ma50 = df.Close.rolling(50).mean()
    volume = df['Open Close Volume'.split()]
    macd   = df.Close.ewm(span=12).mean() - df.Close.ewm(span=26).mean()

    ax.reset()  # remove previous plots
    axo.reset()  # remove previous plots
    ax_2.reset()

    fplt.candlestick_ochl(price)
    fplt.plot(ma20, legend='MA-20')
    fplt.plot(ma50, legend='MA-50')
    fplt.volume_ocv(volume, ax=axo)

    fplt.plot(macd, ax=ax_2, legend='MACD')

    fplt.refresh()  # refresh autoscaling when all plots complete

    def updateYZoom(_ax):
        vb = _ax.vb
        datasrc = vb.datasrc_or_standalone
        vb.update_y_zoom(datasrc.init_x0, datasrc.init_x1)
    # updateYZoom(ax_2)           # Workaround fix

    Thread(target=lambda: info.setText(get_name(txt))).start()  # slow, so use thread

combo.currentTextChanged.connect(update)
update(combo.currentText())

fplt.show(qt_exec=False)  # prepares plots when they're all setup
win.show()
app.exec_()
highfestiva commented 3 years ago

Thanks, fix in f4d4ab7.

SpiffSpaceman commented 3 years ago

yes, problem solved. Thanks