Patch() to update specific columns

Hi,
I have created a simple app to live stream stock price changes.

import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
from dash import Dash, dcc, html, Input, Output, State, dash_table, Patch, no_update
from yahooquery import Ticker
app = Dash(__name__)
ticker_list = ['AAPL', 'ORCL', 'PREM.L', 'UKOG.L', 'KOD.L', 'TOM.L', 'VELA.L', 'MSFT', 'AMZN', 'GOOG']
def get_data():
    all_symbols = " ".join(ticker_list)
    myInfo = Ticker(all_symbols)
    myDict = myInfo.price
    changes = []
    for ticker in ticker_list:
        #ticker = str(ticker)
        price = myDict[ticker]['regularMarketPrice']
        change = myDict[ticker]['regularMarketChange']
        #mcap = myDict[ticker]['marketCap']
        #print(ticker,change, price)
        result = change,price
        changes.append(result)
        df = pd.DataFrame(changes, columns=['Price','Change'])
    return df.to_dict('records')
df = pd.DataFrame(ticker_list,columns=['Stock'])
tblcols=[{'name': 'Stock', 'id': 'Stock'},
         {'name': 'Price', 'id': 'Price'},
         {'name': 'Change', 'id': 'Change'}]
app.layout = dbc.Container([
                html.Div(
                    [

                    dbc.Row(
                        dbc.Col(
                            [
                        dash_table.DataTable(
                            data=df.to_dict("records"),
                            columns=tblcols,
                            id="df-table",
                        ),
                        dcc.Interval('update', interval = 5000, n_intervals = 0)
                            ]
                        ),
                    )
                    ],
                    className="dbc",
                ),
            ],fluid=True)

@app.callback(
    Output('df-table', 'data'),
    Input('update', 'n_intervals'),
    prevent_initial_call=True,
)
def add_data_to_fig(n):
    patched_table =  Patch()
    patched_table[:,[1,2]].update(get_data())
    return patched_table

if __name__ == '__main__':
    app.run_server(debug=True, host= '192.168.0.13')

I tried to use iloc to update column “Price” and “Change” :slight_smile:

https://community.plotly.com/t/dash-2-9-2-released-partial-property-updates-with-patch-duplicate-outputs-dcc-geolocation-scatter-group-attributes-and-more/72114/31

However it returned error AttributeError: ‘list’ object has no attribute ‘update’ .

Whenever i try to patched_table.prepend(get_data()) it append new rows to the end of the table but to the right columns.

What shall i do to update only columns “Price” and “Change”.

Thanks in advance.

I think you can use pd.merge to merge your dataframe that get by querying from yahooquery and dataframe with ticker_list. Something as below:

import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
from dash import Dash, dcc, html, Input, Output, State, dash_table, Patch, no_update
from yahooquery import Ticker
app = Dash(__name__)
ticker_list = ['AAPL', 'ORCL', 'PREM.L', 'UKOG.L', 'KOD.L', 'TOM.L', 'VELA.L', 'MSFT', 'AMZN', 'GOOG']

tblcols=[{'name': 'Stock', 'id': 'Stock'},
         {'name': 'Price', 'id': 'Price'},
         {'name': 'Change', 'id': 'Change'}]

app.layout = dbc.Container([
    html.Div([
        dbc.Row(
            dbc.Col([
                dash_table.DataTable(
                    data=[],
                    columns=tblcols,
                    id="df-table"),
                dcc.Interval('update', interval = 5000, n_intervals = 0)
            ])
        )
    ],className="dbc")
],fluid=True)

@app.callback(
    Output('df-table', 'data'),
    Input('update', 'n_intervals'),
    prevent_initial_call=True,
)

def add_data_to_fig(n):
    all_symbols = " ".join(ticker_list)
    myInfo = Ticker(all_symbols)
    myDict = myInfo.price
    changes = []
    for ticker in ticker_list:
        #ticker = str(ticker)
        price = myDict[ticker]['regularMarketPrice']
        change = myDict[ticker]['regularMarketChange']
        #mcap = myDict[ticker]['marketCap']
        #print(ticker,change, price)
        result = ticker, price, change 
        changes.append(result)
        df = pd.DataFrame(changes, columns=['Stock','Price','Change'])
        print(df)
        df2 = pd.DataFrame(ticker_list,columns=['Stock'])
        df3 = pd.merge(df,df2, on='Stock', how='outer')
    return df3.to_dict('records')

if __name__ == '__main__':
    app.run_server(debug=False)

1 Like

This updates entire table. I just need to update Price and Change columns.

Hello @celal,

This can be done through Patch and datatable.

However, you could also use AG Grid, check this out:

You can perform partial updates via the rowTransaction {"update": [updates]}, the easiest way would be to setup the Ticker as your getRowId.

With all this being said, you can use Patch():

data = Patch()
data[tickerindex]['Price'] = newPrice
data[tickerindex]['Change'] = change
return data

import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
from dash import Dash, dcc, html, Input, Output, State, dash_table, Patch, no_update
from yahooquery import Ticker
app = Dash(__name__)
ticker_list = ['AAPL', 'ORCL', 'PREM.L', 'UKOG.L', 'KOD.L', 'TOM.L', 'VELA.L', 'MSFT', 'AMZN', 'GOOG']
def get_data():
    all_symbols = " ".join(ticker_list)
    myInfo = Ticker(all_symbols)
    myDict = myInfo.price
    changes = []
    for ticker in ticker_list:
        #ticker = str(ticker)
        price = myDict[ticker]['regularMarketPrice']
        change = myDict[ticker]['regularMarketChange']
        #mcap = myDict[ticker]['marketCap']
        #print(ticker,change, price)
        result = change,price
        changes.append(result)
    df = pd.DataFrame(changes, columns=['Change','Price'])
    return df.to_dict('records')
df = pd.DataFrame(ticker_list,columns=['Stock'])
tblcols=[{'name': 'Stock', 'id': 'Stock'},
         {'name': 'Price', 'id': 'Price'},
         {'name': 'Change', 'id': 'Change'}]
app.layout = dbc.Container([
                html.Div(
                    [

                    dbc.Row(
                        dbc.Col(
                            [
                        dash_table.DataTable(
                            data=df.to_dict("records"),
                            columns=tblcols,
                            id="df-table",
                        ),
                        dcc.Interval('update', interval = 5000, n_intervals = 0)
                            ]
                        ),
                    )
                    ],
                    className="dbc",
                ),
            ],fluid=True)

@app.callback(
    Output('df-table', 'data'),
    Input('update', 'n_intervals'),
    prevent_initial_call=True,
)
def add_data_to_fig(n):
    patched_table =  Patch()
    newData = get_data()
    for i in range(len(newData)):
        patched_table[i]['Price'] = newData[i]['Price']
        patched_table[i]['Change'] = newData[i]['Change']
    return patched_table

if __name__ == '__main__':
    app.run_server(debug=True)

Here it is as AG Grid:
image

import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
from dash import Dash, dcc, html, Input, Output, State, dash_table, Patch, no_update
from yahooquery import Ticker
app = Dash(__name__)
ticker_list = ['AAPL', 'ORCL', 'PREM.L', 'UKOG.L', 'KOD.L', 'TOM.L', 'VELA.L', 'MSFT', 'AMZN', 'GOOG']
def get_data():
    all_symbols = " ".join(ticker_list)
    myInfo = Ticker(all_symbols)
    myDict = myInfo.price
    changes = []
    for ticker in ticker_list:
        price = myDict[ticker]['regularMarketPrice']
        change = myDict[ticker]['regularMarketChange']
        #mcap = myDict[ticker]['marketCap']
        #print(ticker,change, price)
        result = ticker, change,price
        changes.append(result)
    df = pd.DataFrame(changes, columns=['Stock', 'Change','Price'])
    return df.to_dict('records')

numericFormat = 'params.value > 1 ? d3.format("($,.2f")(params.value) : d3.format("($,.4f")(params.value)'
df = pd.DataFrame(ticker_list, columns=['Stock'])
tblcols=[{'field': 'Stock'},
         {'field': 'Price', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat}, 'type': 'numericColumn'},
         {'field': 'Change', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat},
                            'type': 'numericColumn', "cellStyle": {'styleConditions':
                                                                       [{"condition": "params.value > 0",
                                                                         "style": {"color": "green"}},
                                                                        {"condition": "params.value < 0",
                                                                         "style": {"color": "red"}}
                                                                        ]}}]
app.layout = dbc.Container([
                html.Div(
                    [

                    dbc.Row(
                        dbc.Col(
                            [
                        dag.AgGrid(
                            rowData=df.to_dict("records"),
                            columnDefs=tblcols,
                            id="df-table",
                            getRowId="params.data.Stock"
                        ),
                        dcc.Interval('update', interval = 5000, n_intervals = 0)
                            ]
                        ),
                    )
                    ],
                    className="dbc",
                ),
            ],fluid=True)

@app.callback(
    Output('df-table', 'rowTransaction'),
    Input('update', 'n_intervals'),
    prevent_initial_call=True,
)
def add_data_to_fig(n):
    return {'update': get_data()}

if __name__ == '__main__':
    app.run_server(debug=True)
1 Like

This is a great answer. I think it’s time to use Dash Ag-Grid.
BTW, If you are using yahooquery i found it useful to add
formatted=True for Ticker.
myInfo = Ticker(all_symbols, formatted=True)
also you need to specify [‘fmt’]

price = myDict[ticker]['regularMarketPrice']['fmt']
change = myDict[ticker]['regularMarketChangePercent']['fmt']

I guess this way you don’t have to apply numeric format.

Regards.

1 Like

The nice thing about the numeric format is the ability to split at 1 dollar to show the difference in 4 decimal places vs 2. :slight_smile:

I see… So how shall I format [‘regularMarketChangePercent’] ?

Thanks.

Glad you asked, I had already worked this up.

import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
from dash import Dash, dcc, html, Input, Output, State, dash_table, Patch, no_update
from yahooquery import Ticker
app = Dash(__name__)
ticker_list = ['AAPL', 'ORCL', 'PREM.L', 'UKOG.L', 'KOD.L', 'TOM.L', 'VELA.L', 'MSFT', 'AMZN', 'GOOG']
def get_data():
    all_symbols = " ".join(ticker_list)
    myInfo = Ticker(all_symbols)
    myDict = myInfo.price
    changes = []
    for ticker in ticker_list:
        price = myDict[ticker]['regularMarketPrice']
        change = myDict[ticker]['regularMarketChange']
        percent = myDict[ticker]['regularMarketChangePercent']
        #mcap = myDict[ticker]['marketCap']
        #print(ticker,change, price)
        result = ticker, change,price, percent
        changes.append(result)
    df = pd.DataFrame(changes, columns=['Stock', 'Change','Price', 'Percent'])
    return df.to_dict('records')

numericFormat = 'params.value > 1 ? d3.format("($,.2f")(params.value) : d3.format("($,.4f")(params.value)'
df = pd.DataFrame(ticker_list, columns=['Stock'])
tblcols=[{'field': 'Stock'},
         {'field': 'Price', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat}, 'type': 'numericColumn'},
         {'field': 'Change', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat},
                            'type': 'numericColumn', "cellStyle": {'styleConditions':
                                                                       [{"condition": "params.value > 0",
                                                                         "style": {"color": "green"}},
                                                                        {"condition": "params.value < 0",
                                                                         "style": {"color": "red"}}
                                                                        ]},
          "headerName": "Change ($)"},
         {'field': 'Percent', 'valueFormatter': {'function': 'params.value ? d3.format(".3%")(params.value) : null'},
          'type': 'rightAligned',
         "cellStyle": {'styleConditions':
                           [{"condition": "params.value > 0",
                             "style": {"color": "green"}},
                            {"condition": "params.value < 0",
                             "style": {"color": "red"}}
                            ]},
          'cellRenderer': 'agAnimateShowChangeCellRenderer',
          "headerName": "Change (%)"}
]
app.layout = dbc.Container([
                html.Div(
                    [

                    dbc.Row(
                        dbc.Col(
                            [
                        dag.AgGrid(
                            rowData=get_data(),
                            columnDefs=tblcols,
                            id="df-table",
                            getRowId="params.data.Stock"
                        ),
                        dcc.Interval('update', interval = 5000, n_intervals = 0)
                            ]
                        ),
                    )
                    ],
                    className="dbc",
                ),
            ],fluid=True)

@app.callback(
    Output('df-table', 'rowTransaction'),
    Input('update', 'n_intervals'),
    prevent_initial_call=True,
)
def add_data_to_fig(n):
    return {'update': get_data()}

if __name__ == '__main__':
    app.run_server(debug=True)

This is awesome. One last thing:
Is it possible to partially update ag-grid table just like dash datatable?

The cool thing about the rowTransaction is that it will only update what is passed to it. As long as you have included the identifying row information. You will need to pass the whole row’s info.

As far as using Patch, any dash component has the ability to use it. It’s all about how you map it. :slight_smile:

Here is an example where the data only queries one ticker at a time based upon interval:

import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
from dash import Dash, dcc, html, Input, Output, State, dash_table, Patch, no_update
from yahooquery import Ticker
app = Dash(__name__)
ticker_list = ['AAPL', 'ORCL', 'PREM.L', 'UKOG.L', 'KOD.L', 'TOM.L', 'VELA.L', 'MSFT', 'AMZN', 'GOOG']
def get_data(n=None):
    if n is not None:
        all_symbols = ticker_list[n]
    else:
        all_symbols = " ".join(ticker_list)
    myInfo = Ticker(all_symbols)
    myDict = myInfo.price
    changes = []
    for ticker in ticker_list:
        if ticker in myDict:
            price = myDict[ticker]['regularMarketPrice']
            change = myDict[ticker]['regularMarketChange']
            percent = myDict[ticker]['regularMarketChangePercent']
            #mcap = myDict[ticker]['marketCap']
            #print(ticker,change, price)
            result = ticker, change, price, percent
            changes.append(result)
    df = pd.DataFrame(changes, columns=['Stock', 'Change','Price', 'Percent'])
    return df.to_dict('records')

numericFormat = 'params.value > 1 ? d3.format("($,.2f")(params.value) : d3.format("($,.4f")(params.value)'
df = pd.DataFrame(ticker_list, columns=['Stock'])
tblcols=[{'field': 'Stock'},
         {'field': 'Price', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat}, 'type': 'numericColumn'},
         {'field': 'Change', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat},
                            'type': 'numericColumn', "cellStyle": {'styleConditions':
                                                                       [{"condition": "params.value > 0",
                                                                         "style": {"color": "green"}},
                                                                        {"condition": "params.value < 0",
                                                                         "style": {"color": "red"}}
                                                                        ]},
          "headerName": "Change ($)"},
         {'field': 'Percent', 'valueFormatter': {'function': 'params.value ? d3.format(".3%")(params.value) : null'},
          'type': 'rightAligned',
         "cellStyle": {'styleConditions':
                           [{"condition": "params.value > 0",
                             "style": {"color": "green"}},
                            {"condition": "params.value < 0",
                             "style": {"color": "red"}}
                            ]},
          'cellRenderer': 'agAnimateShowChangeCellRenderer',
          "headerName": "Change (%)"}
]
app.layout = dbc.Container([
                html.Div(
                    [

                    dbc.Row(
                        dbc.Col(
                            [
                        dag.AgGrid(
                            rowData=get_data(),
                            columnDefs=tblcols,
                            id="df-table",
                            getRowId="params.data.Stock"
                        ),
                        dcc.Interval('update', interval = 1000, n_intervals = 0)
                            ]
                        ),
                    )
                    ],
                    className="dbc",
                ),
            ],fluid=True)

@app.callback(
    Output('df-table', 'rowTransaction'),
    Input('update', 'n_intervals'),
    prevent_initial_call=True,
)
def add_data_to_fig(n):
    i = n % 10
    return {'update': get_data(i)}

if __name__ == '__main__':
    app.run_server(debug=True)

If you wanted to use patch, you would do something like:

import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
from dash import Dash, dcc, html, Input, Output, State, dash_table, Patch, no_update
from yahooquery import Ticker
app = Dash(__name__)
ticker_list = ['AAPL', 'ORCL', 'PREM.L', 'UKOG.L', 'KOD.L', 'TOM.L', 'VELA.L', 'MSFT', 'AMZN', 'GOOG']
def get_data(n=None):
    if n is not None:
        all_symbols = ticker_list[n]
    else:
        all_symbols = " ".join(ticker_list)
    myInfo = Ticker(all_symbols)
    myDict = myInfo.price
    changes = []
    for ticker in ticker_list:
        if ticker in myDict:
            price = myDict[ticker]['regularMarketPrice']
            change = myDict[ticker]['regularMarketChange']
            percent = myDict[ticker]['regularMarketChangePercent']
            #mcap = myDict[ticker]['marketCap']
            #print(ticker,change, price)
            result = ticker, change, price, percent
            changes.append(result)
    df = pd.DataFrame(changes, columns=['Stock', 'Change','Price', 'Percent'])
    df['Holding'] = ''
    return df.to_dict('records')

numericFormat = 'params.value > 1 ? d3.format("($,.2f")(params.value) : d3.format("($,.4f")(params.value)'
df = pd.DataFrame(ticker_list, columns=['Stock'])
tblcols=[{'field': 'Stock'},
         {'field': 'Price', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat}, 'type': 'numericColumn'},
         {'field': 'Change', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat},
                            'type': 'numericColumn', "cellStyle": {'styleConditions':
                                                                       [{"condition": "params.value > 0",
                                                                         "style": {"color": "green"}},
                                                                        {"condition": "params.value < 0",
                                                                         "style": {"color": "red"}}
                                                                        ]},
          "headerName": "Change ($)"},
         {'field': 'Percent', 'valueFormatter': {'function': 'params.value ? d3.format(".3%")(params.value) : null'},
          'type': 'rightAligned',
         "cellStyle": {'styleConditions':
                           [{"condition": "params.value > 0",
                             "style": {"color": "green"}},
                            {"condition": "params.value < 0",
                             "style": {"color": "red"}}
                            ]},
          'cellRenderer': 'agAnimateShowChangeCellRenderer',
          "headerName": "Change (%)"},
         {'field': 'Holding', 'cellRenderer': 'agAnimateShowChangeCellRenderer',}
]
app.layout = dbc.Container([
                html.Div(
                    [

                    dbc.Row(
                        dbc.Col(
                            [
                        dag.AgGrid(
                            rowData=get_data(),
                            columnDefs=tblcols,
                            id="df-table",
                            getRowId="params.data.Stock"
                        ),
                        dcc.Interval('update', interval = 5000, n_intervals = 0)
                            ]
                        ),
                    )
                    ],
                    className="dbc",
                ),
            ],fluid=True)

@app.callback(
    Output('df-table', 'rowData'),
    Input('update', 'n_intervals'),
    prevent_initial_call=True,
)
def add_data_to_fig(n):
    patched_table =  Patch()
    newData = get_data()
    for i in range(len(newData)):
        patched_table[i]['Holding'] = n
    return patched_table

if __name__ == '__main__':
    app.run_server(debug=True)
2 Likes

These are all good solutions. my function to create datatable is a bit messy. It calculates some formulas and algorithms to return Stock list to datatable in a specific order. Usually there are average of 150-200 stocks on this screener. Without running this function you can’t create stock list. I tried to keep datatable data in dcc.store and return only stocks list to update function. It works fine only if i limit screener to 25-30 stocks. I guess 5secs is too short to update new data. I’m searching for new solutions to make it work.

AG grid can handle the updates on the rowTransaction, that way you only have to pass what is actually updating. Takes the guess work out with finding the right point to update.

You can have your interval still be 5 seconds, but instead query from yahoo groups of tickers.

Hello jinnyzor, thank you for your help. However no matter how i tried i couldn’t get this working. Maybe i’m getting you wrong.
ag-grid rowTransaction update the whole datatable. Not just the columns i want to update.

import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
from dash import Dash, dcc, html, Input, Output, State, dash_table, Patch, no_update
from yahooquery import Ticker
app = Dash(__name__)
ticker_list = ['AAPL', 'ORCL', 'MSFT', 'AMZN', 'GOOG']
def get_data():
    all_symbols = " ".join(ticker_list)
    myInfo = Ticker(all_symbols)
    myDict = myInfo.price
    changes = []
    for ticker in ticker_list:
        price = myDict[ticker]['regularMarketPrice']
        change = myDict[ticker]['regularMarketChange']
        percent = myDict[ticker]['regularMarketChangePercent']
        #mcap = myDict[ticker]['marketCap']
        #print(ticker,change, price)
        result = ticker, change,price, percent
        changes.append(result)
    df = pd.DataFrame(changes, columns=['Stock', 'Price','Change','Percent'])
    return df.to_dict('records')

numericFormat = 'params.value > 1 ? d3.format("($,.2f")(params.value) : d3.format("($,.2f")(params.value)'
df = pd.DataFrame(ticker_list, columns=['Stock'])
df['Test'] = 'Test'
df['Price'] = 'Price'
df['Change'] = 'Change'
df['Percent'] = 'Percent'

tblcols=[{'field': 'Stock'},
         {'field': 'Test'},
         {'field': 'Price', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat}, 'type': 'numericColumn'},
         {'field': 'Change', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat},
                            'type': 'numericColumn', "cellStyle": {'styleConditions':
                                                                       [{"condition": "params.value > 0",
                                                                         "style": {"color": "green"}},
                                                                        {"condition": "params.value < 0",
                                                                         "style": {"color": "red"}}
                                                                        ]},
          "headerName": "Change ($)"},
         {'field': 'Percent', 'valueFormatter': {'function': 'params.value ? d3.format(".2%")(params.value) : null'},
          'type': 'rightAligned',
         "cellStyle": {'styleConditions':
                           [{"condition": "params.value > 0",
                             "style": {"color": "green"}},
                            {"condition": "params.value < 0",
                             "style": {"color": "red"}}
                            ]},
          'cellRenderer': 'agAnimateShowChangeCellRenderer',
          "headerName": "Change (%)"}
]
app.layout = dbc.Container([
                html.Div(
                    [

                    dbc.Row(
                        dbc.Col(
                            [
                        dag.AgGrid(
                            rowData=df.to_dict("records"), #original dataframe.
                            columnDefs=tblcols,
                            id="df-table",
                            getRowId="params.data.Stock"
                        ),
                        dcc.Interval('update', interval = 10000, n_intervals = 0)
                            ]
                        ),
                    )
                    ],
                    className="dbc",
                ),
            ],fluid=True)
@app.callback(
    Output('df-table', 'rowTransaction'),
    Input('update', 'n_intervals'),
    prevent_initial_call=True,
)
def add_data_to_fig(n):
    return {'update': get_data()}
if __name__ == '__main__':
    app.run_server(debug=True)

I try to use another dataframe to create the datatable and added a ‘Test’ column.
get_update() updates entire datatable with blank Test column:

table345

Hello @celal,

This is partially true. When you use rowTransaction, you need to pass the whole row’s info again. But you do not need to pass the whole set of rowData again.

Check out here:

import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
from dash import Dash, dcc, html, Input, Output, State, dash_table, Patch, no_update
from yahooquery import Ticker
import datetime
app = Dash(__name__)
ticker_list = ['MULN', 'LHDX', 'CS', 'BBY', 'HLBZ',
               'VORB',
               'IDEX',
               'CORZ',
               'VLTA',
               'RIDE',
               'DOGZ',
               'OEG',
               'SMFR',
               'SIVB',
               'FFIE',
               'AMRS',
               'WETG',
               'NKLA',
               'GOEV',
               'SSPI',
               'HUBC',
               'TRKA',
               'XELA',
               'MMAT',
               'NVOS',
               'WE',
               'BNGO',
               'OCGN',
               'AKBA',
               'ATXG',
               'LGHL',
               'PIRS',
               'WKHS',
               'TAOP',
               'SRNE',
               'BBIG',
               'SIVBQ',
               'HYMC',
               'SATX',
               'CLOV',
               'SPIR',
               'GETR',
               'SUNW',
               'DBD',
               'VXRT',
               'BZFD',
               'ZFOX',
               'CIH',
               'QRTEA',
               'PRCH',
               'PONO',
               'BLND',
               'TNXP',
               'ACB',
               'CENN',
               'NUTX',
               'SDIG',
               'DHC',
               'SPRU',
               'CEMI',
               'INO',
               'PGY',
               'UP',
               'VRM',
               'ZKIN',
               'SKLZ',
               'PHUN',
               'AVRO',
               'CYXT',
               'NKTR',
               'CLEU',
               'ONCT',
               'APRN',
               'DTEA',
               'BRDS',
               'TSHA',
               'WPRT',
               'CTRM',
               'XXII',
               'HLGN',
               'QBTS',
               'NISN',
               'APPH',
               'LIZI',
               'HYZN',
               'EVLO',
               'NCMI',
               'CNTG',
               'AEVA',
               'SOLO',
               'SYBX',
               'SABS',
               'VSTM',
               'KSCP',
               'GSMG',
               'LILM', 'AMV', 'MPLN', 'DOMA',
               'MSFT', 'AAPL', 'TSLA', 'BORR']
partition = 10
def get_data(n = None, tickers=ticker_list):
    if n:
        parts = len(tickers)//partition
        i = n%partition
        if i == (partition-1):
            tickers = ticker_list[i*parts:]
        else:
            tickers = ticker_list[i * parts:i*parts+parts]
    all_symbols = " ".join(tickers)
    myInfo = Ticker(all_symbols)
    myDict = myInfo.price
    changes = []
    for ticker in tickers:
        if 'Quote not found for ticker symbol' not in myDict[ticker]:
            if 'regularMarketPrice' in myDict[ticker].keys():
                price = myDict[ticker]['regularMarketPrice']
                change = myDict[ticker]['regularMarketChange']
                percent = myDict[ticker]['regularMarketChangePercent']
                vol = datetime.datetime.now()
                result = ticker, price, change, percent, vol
                changes.append(result)
        else:
            ticker_list.remove(ticker)
    df = pd.DataFrame(changes, columns=['Stock', 'Price','Change','Percent', 'Time'])
    return df.to_dict('records')

numericFormat = 'params.value > 1 ? d3.format("($,.2f")(params.value) : d3.format("($,.4f")(params.value)'

tblcols=[{'field': 'Stock'},
         {'field': 'Price', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat}, 'type': 'numericColumn'},
         {'field': 'Change', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat},
                            'type': 'numericColumn', "cellStyle": {'styleConditions':
                                                                       [{"condition": "params.value > 0",
                                                                         "style": {"color": "green"}},
                                                                        {"condition": "params.value < 0",
                                                                         "style": {"color": "red"}}
                                                                        ]},
          "headerName": "Change ($)"},
         {'field': 'Percent', 'valueFormatter': {'function': 'params.value ? d3.format(".2%")(params.value) : null'},
          'type': 'rightAligned',
         "cellStyle": {'styleConditions':
                           [{"condition": "params.value > 0",
                             "style": {"color": "green"}},
                            {"condition": "params.value < 0",
                             "style": {"color": "red"}}
                            ]},
          'cellRenderer': 'agAnimateShowChangeCellRenderer',
          "headerName": "Change (%)"},
         {'field': 'Time', 'type':'numericColumn', 'sort': 'desc'}
]
app.layout = dbc.Container([
                html.Div(
                    [

                    dbc.Row(
                        dbc.Col(
                            [
                        dag.AgGrid(
                            rowData=get_data(),
                            columnDefs=tblcols,
                            id="df-table",
                            getRowId="params.data.Stock",
                            defaultColDef={'sortable': True}
                        ),
                        dcc.Interval('update', interval = 1100, n_intervals = 0)
                            ]
                        ),
                    )
                    ],
                    className="dbc",
                ),
            ],fluid=True)
@app.callback(
    Output('df-table', 'rowTransaction'),
    Input('update', 'n_intervals'),
    prevent_initial_call=True,
)
def add_data_to_fig(n):
    return {'update': get_data(n)}
if __name__ == '__main__':
    app.run_server(debug=True)

I have the ticker_list being split into 10 partitions, and then after the initial update, it loops through and updates each group by itself every 1100 ms.

This way, you are only pushing the updated rows, instead of querying the whole list each time.

The cool thing about using rowTransaction is that you dont have to worry about where in the data you are trying to update, because the Grid already does that for you based upon params.data.Stock

2 Likes

The mechanism you created to query new data is quite imressive.

Assume using this data as rowData to create the table:

[{'Stock': 'MMCAS.IS', 'M': 'M', '2W': '', 'W': 'W', '3D': '3D', 'D': '', 'SS': '', 'Price': '', 'Change': '', 'Percent': ''}, {'Stock': 'SEKFK.IS', 'M': '', '2W': '2W', 'W': 'W', '3D': '', 'D': 'D', 'SS': 'SS', 'Price': '', 'Change': '', 'Percent': ''}, {'Stock': 'AGHOL.IS', 'M': '', '2W': '', 'W': 'W STRONG', '3D': '3D', 'D': 'D', 'SS': '', 'Price': '', 'Change': '', 'Percent': ''}, {'Stock': 'CANTE.IS', 'M': '', '2W': '', 'W': 'W', '3D': '3D', 'D': 'D', 'SS': 'SS', 'Price': '', 'Change': '', 'Percent': ''}, {'Stock': 'CMBTN.IS', 'M': '', '2W': '', 'W': 'W', '3D': '3D', 'D': 'D', 'SS': 'SS', 'Price': '', 'Change': '', 'Percent': ''}]

Now i want to update this table using get_data() function and without changing values in

         {'field': 'M'},
         {'field': '2W'},
         {'field': 'W'},
         {'field': '3D'},
         {'field': 'D'},
         {'field': 'SS'},

fields.

So basically function creating rowData is different than updating it.

And i dont want to re-use this function at every update.

Unfortunately i couldn’t find a way to make this work with rowTransaction.
It works fine, If i patch rowData with get_update().

import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
from dash import Dash, dcc, html, Input, Output, State, dash_table, Patch, no_update
from yahooquery import Ticker
app = Dash(__name__)

datatable = [{'Stock': 'MMCAS.IS', 'M': 'M', '2W': '', 'W': 'W', '3D': '3D', 'D': '', 'SS': '', 'Price': '', 'Change': '', 'Percent': ''}, {'Stock': 'SEKFK.IS', 'M': '', '2W': '2W', 'W': 'W', '3D': '', 'D': 'D', 'SS': 'SS', 'Price': '', 'Change': '', 'Percent': ''}, {'Stock': 'AGHOL.IS', 'M': '', '2W': '', 'W': 'W STRONG', '3D': '3D', 'D': 'D', 'SS': '', 'Price': '', 'Change': '', 'Percent': ''}, {'Stock': 'CANTE.IS', 'M': '', '2W': '', 'W': 'W', '3D': '3D', 'D': 'D', 'SS': 'SS', 'Price': '', 'Change': '', 'Percent': ''}, {'Stock': 'CMBTN.IS', 'M': '', '2W': '', 'W': 'W', '3D': '3D', 'D': 'D', 'SS': 'SS', 'Price': '', 'Change': '', 'Percent': ''}]

df_stocks = pd.DataFrame(datatable,columns=['Stock'])
tickers = df_stocks['Stock']
ticker_list = tickers.to_list()

def get_data(n=None):
    if n is not None:
        all_symbols = ticker_list[n]
    else:
        all_symbols = " ".join(ticker_list)
    myInfo = Ticker(all_symbols)
    myDict = myInfo.price
    changes = []
    for ticker in ticker_list:
        if ticker in myDict:
            price = myDict[ticker]['regularMarketPrice']
            change = myDict[ticker]['regularMarketChange']
            percent = myDict[ticker]['regularMarketChangePercent']
            result = ticker, price, change, percent
            changes.append(result)
    df = pd.DataFrame(changes, columns=['Stock','Price','Change','Percent'])
    return df.to_dict('records')

numericFormat = 'params.value > 1 ? d3.format("($,.2f")(params.value) : d3.format("($,.2f")(params.value)'

tblcols=[{'field': 'Stock'},
         {'field': 'M'},
         {'field': '2W'},
         {'field': 'W'},
         {'field': '3D'},
         {'field': 'D'},
         {'field': 'SS'},
         {'field': 'Price', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat}, 'type': 'numericColumn'},
         {'field': 'Change', 'cellRenderer': 'agAnimateShowChangeCellRenderer',
          'valueFormatter':{'function': numericFormat},
                            'type': 'numericColumn', "cellStyle": {'styleConditions':
                                                                       [{"condition": "params.value > 0",
                                                                         "style": {"color": "green"}},
                                                                        {"condition": "params.value < 0",
                                                                         "style": {"color": "red"}}
                                                                        ]},
          "headerName": "Change ($)"},
         {'field': 'Percent', 'valueFormatter': {'function': 'params.value ? d3.format(".2%")(params.value) : null'},
          'type': 'rightAligned',
         "cellStyle": {'styleConditions':
                           [{"condition": "params.value > 0",
                             "style": {"color": "green"}},
                            {"condition": "params.value < 0",
                             "style": {"color": "red"}}
                            ]},
          'cellRenderer': 'agAnimateShowChangeCellRenderer',
          "headerName": "Change (%)"},
]
app.layout = dbc.Container([
                html.Div(
                    [
                    dbc.Row(
                        dbc.Col(
                            [
                        dag.AgGrid(
                            rowData=datatable,
                            columnDefs=tblcols,
                            id="df-table",
                            getRowId="params.data.Stock"
                        ),
                        dcc.Interval('update', interval = 5000, n_intervals = 0)
                            ]
                        ),
                    )
                    ],
                    className="dbc",
                ),
            ],fluid=True)

@app.callback(
    Output('df-table', 'rowData'),
    Input('update', 'n_intervals'),
    prevent_initial_call=True,
)
def add_data_to_fig(n):
    patched_table =  Patch()
    newData = get_data()
    for i in range(len(newData)):
        patched_table[i]['Price'] = newData[i]['Price']
        patched_table[i]['Change'] = newData[i]['Change']
        patched_table[i]['Percent'] = newData[i]['Percent']
    return patched_table

if __name__ == '__main__':
    app.run_server(debug=True)

Whatever works for you. Patch will only update the columns you pass, but you have to make sure you map it properly, that is the issue.