Black Lives Matter. Please consider donating to Black Girls Code today.

Please help with plot callback function error

Hello :3, I’m trying to plot live bitcoin price but I keep getting a callback error that says

updated_btc_2h() takes 1 positional argument but 2 were given

If I’m making an error elsewhere when trying to plot the candles live, please feel free to let me know!

import json
from datetime import datetime
import requests
import pathlib
import time
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from plotly.graph_objs import *
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input, State


app = dash.Dash(__name__)

# Requesting JSON
BTC2H = requests.get(
    'https://poloniex.com/public?command=returnChartData&currencyPair=USDT_BTC&start=1571803200&end=9999999999&period=7200'
)

jsonBTC2H = BTC2H.json()
data = pd.DataFrame(jsonBTC2H)

# Converting dates (newdate1 for callback)
newDate = data['date'].apply(lambda x: time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(x)))
newDate1 = data.date.apply(lambda x: time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(x)))


# Candlesticks for BTC
chartBTC = go.Candlestick(x=newDate,
                          open=data['open'],
                          high=data['high'],
                          low=data['low'],
                          close=data['close'],
                          xaxis='x1',
                          yaxis='y1',
                          increasing=dict(line=dict(color="#00E676")),
                          decreasing=dict(line=dict(color="#FF5252")),
                          name="Candlesticks")


# Layout
layout = Layout(
    showlegend=False,
    xaxis=dict(
        domain=[0, 1]
    ),
    yaxis=dict(
        domain=[0, 1],
    )
    )


# Plot
btcplot = go.Figure(data=[chartBTC], layout=layout)


# Colours of the candles
cs = btcplot.data[0]
cs.increasing.fillcolor = '#131722'
cs.increasing.line.color = '#00E676'
cs.decreasing.fillcolor = '#FF5252'
cs.decreasing.line.color = '#FF5252'

# Removes rangeslider
btcplot.update(layout_xaxis_rangeslider_visible=False)

# Sets the automatic x-axis range when you first open it (plots lasts 250 candles)
btcplot.update_xaxes(range=[newDate.iloc[-250], newDate.max()])


# Sets the automatic y-axis range to the limit of
btcplot.update_yaxes(range=[(data['low'].iloc[-250:].min()) - 100, (data['high'].iloc[-250:].max()) + 100])

# Frequency of readings on both axis
btcplot.update_xaxes(nticks=20)
btcplot.update_yaxes(nticks=30)


# Configurations
config = dict({
    'showTips': False
})


# Dash interface

app.layout = html.Div(children=[
    dcc.Graph(id='btc_chart',
              figure=btcplot,
              config=config,
              animate=True,
              style={"height" : "99vh"}),  # Without height 99vh, height of dash app is small
    dcc.Interval(
        id='btc_update',
        interval=1000,
        n_intervals=0
    )
])


@app.callback(
    Output('btc_chart', 'figure'),
    [Input('btc_update', 'n_intervals')],
    [State('btc_update', 'figure')]
)
def updated_btc_2h(n_interval):
    global data
    newbtcplot= go.Candlestick(
        x=newDate1,
        open=data.open,
        high=data.high,
        low=data.low,
        close=data.close,
        increasing=dict(line=dict(color="#00E676")),
        decreasing=dict(line=dict(color="#FF5252")),
        showlegend=False,
        name="Candlestick Updated")
    newbtcplot1 = go.Figure(data=[newbtcplot], layout=layout)
    return newbtcplot1

# return Figure(data= figure, layout = layout)

# To run the app
if __name__ == '__main__':
    app.run_server(debug=True)

Hi @pythonnewbie the problem is related to the number of input parameters in your callback function:

@app.callback(
    Output('btc_chart', 'figure'),
    [Input('btc_update', 'n_intervals')],
    [State('btc_update', 'figure')]
)
def updated_btc_2h(n_interval):

You have defined 1 output variable and 2 input variables:

Output('btc_chart', 'figure'),
    [Input('btc_update', 'n_intervals')],
    [State('btc_update', 'figure')]

But the function is taking just 1 input variable:

def updated_btc_2h(n_interval):

State count as an input as well.

Try to change the function to something like this:

@app.callback(
    Output('btc_chart', 'figure'),
    [Input('btc_update', 'n_intervals')],
    [State('btc_update', 'figure')]
)
def updated_btc_2h(n_interval,figState):
...

Hi Daniel and thanks so much, it did get rid of the callback error but the candlesticks still aren’t printing in real time :(.

Any idea on why would be greatly appreciated, thanks for your time!!

Hi @pythonnewbie, I run your code and I got this:

The only error that I saw was in the callback function:

@app.callback(
    Output('btc_chart', 'figure'),
    [Input('btc_update', 'n_intervals')],
    [State('btc_update', 'figure')]
)

where you used btc_update to get the state from the figure. I changed that variable to the dcc.Graph where is the figure state.

@app.callback(
    Output('btc_chart', 'figure'),
    [Input('btc_update', 'n_intervals')],
    [State('btc_chart', 'figure')]
)
def updated_btc_2h(n_interval,figState):
    global data
    newbtcplot= go.Candlestick(
        x=newDate1,
        open=data.open,
        high=data.high,
        low=data.low,
        close=data.close,
        increasing=dict(line=dict(color="#00E676")),
        decreasing=dict(line=dict(color="#FF5252")),
        showlegend=False,
        name="Candlestick Updated")
    newbtcplot1 = go.Figure(data=[newbtcplot], layout=layout)
    return newbtcplot1

I hope this help!

Thanks for the help on that too. I read some examples on how to write the code properly.

Put all the plotly code below the callback as I see everyone do, but now I’m getting an error that says

Invalid argument figure passed into Graph with ID “btc_chart”.
Expected object.
Was supplied type array.

I’m indebted to you, thank you so much again!

import json
from datetime import datetime
import requests
import pathlib
import time
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from plotly.graph_objs import *
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input, State

# To start an application in dash
app = dash.Dash(__name__)
server = app.server


# This is the API call (requests.get)
BTC2H = requests.get('https://poloniex.com/public?command=returnChartData&currencyPair=USDT_BTC&start=1571803200&end'
                     '=9999999999&period=7200')

jsonBTC2H = BTC2H.json()
df = pd.DataFrame(jsonBTC2H)

newDate = df["date"].apply(lambda x: time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(x)))

app.layout = html.Div(
    [
        dcc.Graph(
            id='btc_chart',
            style={"height": "99vh"}),
        dcc.Interval(
            id='btc_update',
            interval=1000,
            n_intervals=0
        )
    ]
)


@app.callback(
    Output('btc_chart', 'figure'),
    [Input('btc_update', 'n_intervals')],
    [State('btc_chart', 'figure')]
)
def updated_btc_2h(n_intervals, figure_state):
    candlesticks_btc = go.Candlestick(x=newDate,
                                      open=df["open"],
                                      high=df["high"],
                                      low=df["low"],
                                      close=df["close"],
                                      xaxis='x1',
                                      yaxis='y1',
                                      increasing=dict(line=dict(color="#00E676")),
                                      decreasing=dict(line=dict(color="#FF5252")),
                                      name="Candlesticks")
    layout_ = Layout(
        showlegend=True,
        legend=dict(
            x=0.01,
            y=0.980,
            traceorder='normal',
            bgcolor="#232e4d",
            font=dict(
                size=10,
                color="#ffffff"), ),
        paper_bgcolor='rgba(0 ,0, 0, 0)',
        plot_bgcolor='rgba(19, 23, 34, 1)',
        dragmode="pan",
        xaxis=dict(
            domain=[0, 1]
        ),
        yaxis=dict(
            domain=[0, 1]
        )
    )
    data = [candlesticks_btc]
    plot_btc = go.Figure(data=data, layout=layout_)
    plot_btc.update_layout(
        # title="BTC chart",
        font_family="Monospace",
        font_color="#000000",
        title_font_family="Monospace",
        title_font_color="#000000",
        legend_title_font_color="#000000",
        yaxis=dict(
            side="right"),  # Puts the y-axis on the right
        hovermode="closest",  # Used to be "x" but spiked didn't always show. So get date from block
        spikedistance=-1,
        # Keeps the spikes always on with -1. hoverdistance=0   # 0 means no looking for data (wont show it)
        hoverlabel=dict(  # Changes appearance settings of the hover label and x-axis hover label
            bgcolor="#232e4d",
            font_family="Monospace",
            font_size=11,
            font_color="#ffffff"
        )
    )

    # Zero lines
    plot_btc.update_xaxes(zeroline=False, zerolinewidth=2, zerolinecolor='LightPink')
    plot_btc.update_yaxes(zeroline=False, zerolinewidth=2, zerolinecolor='LightPink')

    # Overriding the axis and grids colours
    plot_btc.update_xaxes(showline=True, linewidth=1, linecolor='black', gridcolor='#181F34')
    plot_btc.update_yaxes(showline=True, linewidth=1, linecolor='black', gridcolor='#181F34')

    # Pointers
    plot_btc.update_xaxes(showspikes=True, spikecolor="white", spikethickness=1, spikesnap="cursor", spikedash='dot',
                         spikemode="across")
    plot_btc.update_yaxes(showspikes=True, spikecolor="white", spikethickness=1, spikesnap="cursor", spikedash='dot',
                         spikemode="across")

    # Colours of the candles
    cs = plot_btc.data[0]
    cs.increasing.fillcolor = '#131722'
    cs.increasing.line.color = '#00E676'
    cs.decreasing.fillcolor = '#FF5252'
    cs.decreasing.line.color = '#FF5252'

    # Removes rangeslider
    plot_btc.update(layout_xaxis_rangeslider_visible=False)

    # Sets the automatic x-axis range when you first open it (plots lasts 250 candles)
    # When using positive values in [], use timeReference
    # When using negative values in []., use timeReference.iloc[]
    plot_btc.update_xaxes(range=[newDate.iloc[-250], newDate.max()])

    # Sets the automatic y-axis range to the limit of
    # Lower limit: lowest value of the last 250 candles .iloc[-250:] - 100 (for room)
    # Higher limit: highest value of the last 250 candles .iloc[-250] + 100 (for room)
    # Has to be an iloc then min/max order

    plot_btc.update_yaxes(range=[(df["low"].iloc[-250:].min()) - 100, (df["high"].iloc[-250:].max()) + 100])

    # Frequency of readings on both axis
    plot_btc.update_xaxes(nticks=20)
    plot_btc.update_yaxes(nticks=30)
    config = dict({
        'scrollZoom': True,
        'displayModeBar': False,
        'showTips': False
    })

    return candlesticks_btc, layout_, plot_btc, config


# To run the app
if __name__ == '__main__':
    app.run_server(debug=True)

Hi @pythonnewbie

If you have one Output:

@app.callback(
    Output('btc_chart', 'figure'),
    [Input('btc_update', 'n_intervals')],
    [State('btc_chart', 'figure')]
)

You should return only one object. But you are returning four:

return candlesticks_btc, layout_, plot_btc, config

That’s why you get the error:

Expected object .
Was supplied type array

It’s live! After your inputs and fiddling around for another few hours, it worked. I’m new to programming and it may not be much but i’m so happy! Thank you both once again :smiley:

1 Like