Hi there,
I am new to DASH and plotly. I need to control Y axis range with mouse, like you can do on tradingview app. For this I have tried to use slider in my candlestick chart but it does not work. Any alternative solution/suggestion is much appreciated. Thanks so much.
relevant scripts below -
app.layout = html.Div([
html.H4('Candlestick chart with selectable hover mode'),
html.P("Select hovermode:"),
dcc.RadioItems(
id='hovermode',
inline=True,
options=['x', 'x unified', 'closest'],
value='x'
),
html.Div([
html.Div([
dcc.Dropdown(
id='ticker-dropdown',
options=[{'label': ticker, 'value': ticker} for ticker in tickers],
value=tickers[0], # Default value could be the first ticker in the list
searchable=True,
style={'width': '60%', 'padding': '3px', 'color': 'black', 'margin-left': '2%'}
),
dash_table.DataTable(
id='ticker-table',
columns=[{'name': col, 'id': col} for col in df.columns],
page_size=100,
data=df.to_dict('records'),
fixed_rows={'headers': True},
row_selectable='single',
sort_action='native',
style_table={'height': '900px', 'overflowY': 'auto'},
style_header={
'backgroundColor': 'rgb(10, 10, 10)',
'fontWeight': 'bold',
'color': 'lightblue',
'textAlign': 'center'
},
style_cell={
'backgroundColor': 'rgb(30, 30, 30)',
'color': 'lightgrey',
'border': '1px solid black',
'minWidth': '150px', 'maxWidth': '200px', 'width': '150px'
},
style_data_conditional=[
{'if': {'row_index': 'odd'}, 'backgroundColor': 'rgb(20, 20, 20)'},
{'if': {'state': 'selected'}, 'backgroundColor': 'rgba(0, 116, 217, 0.3)', 'border': '1px solid blue'},
{
'if': {'column_id': '% Change', 'filter_query': '{% Change} > 0'},
'color': 'green'
},
{
'if': {'column_id': '% Change', 'filter_query': '{% Change} < 0'},
'color': 'red'
}
]
),
], id='ticker-table-container', style={'width': '25%', 'overflow': 'auto', 'display': 'inline-block', 'margin-left': '2%', 'verticalAlign': 'top'}),
html.Div([
dcc.Graph(id='candlestick-chart', style={'height': '1200px', 'width': '85%', 'display': 'inline-block'},
config={'scrollZoom': True}),
dcc.Slider(
id='yaxis-slider',
min=0,
max=100,
step=1,
value=50,
marks=None,
tooltip={"placement": "right", "always_visible": True},
vertical=True,
verticalHeight=900 # Adjust the height to match the graph
)
], style={'width': '100%', 'display': 'flex', 'align-items': 'center', 'justify-content': 'space-between'})
], style={'display': 'flex', 'align-items': 'flex-start'}),
dcc.Interval(id='candlestick-update-interval', interval=5 * 1000, n_intervals=0),
dcc.Interval(id='ticker-update-interval', interval=10 * 1000, n_intervals=0),
dcc.Store(id='initial-load', data={'loaded': False}),
dcc.Store(id='active-ticker-store', data={'ticker': 'HINDCOPPER'}),
html.Div(id='app-launch-trigger', style={'display': 'none'})
])
@app.callback(
[Output('candlestick-chart', 'figure'),
Output('yaxis-slider', 'min'),
Output('yaxis-slider', 'max'),
Output('yaxis-slider', 'value')],
[Input('active-ticker-store', 'data'),
Input('candlestick-update-interval', 'n_intervals'),
Input('hovermode', 'value'),
Input('yaxis-slider', 'value'),
Input('candlestick-chart', 'relayoutData')],
[State('candlestick-chart', 'figure')]
)
def update_candlestick_chart(active_ticker_data, n_intervals, hovermode, yaxis_slider_value, relayoutData, figure):
ticker = active_ticker_data['ticker']
df, _, dt_breaks = get_hist(ticker) # Fetch historical data along with date breaks
if df.empty:
return go.Figure(), 0, 100, 50
y_min = df['buy-sell_EMA_low'].min()
y_max = df['buy-sell_EMA_high'].max()
y_range = y_max - y_min
# Create the candlestick chart
fig = go.Figure(data=[go.Candlestick(
x=df.index,
open=df['buy-sell_EMA_open'],
high=df['buy-sell_EMA_high'],
low=df['buy-sell_EMA_low'],
close=df['buy-sell_EMA_close'],
increasing_line_color='green',
decreasing_line_color='red'
)])
# Apply the relayout data if it exists and ticker has not changed
if relayoutData and figure and figure['layout']['uirevision'] == ticker:
if 'xaxis.range[0]' in relayoutData:
fig.update_layout(xaxis_range=[relayoutData['xaxis.range[0]'], relayoutData['xaxis.range[1]']])
if 'yaxis.range[0]' in relayoutData:
fig.update_layout(yaxis_range=[relayoutData['yaxis.range[0]'], relayoutData['yaxis.range[1]']])
else:
# Reset y-axis range if ticker changes
fig.update_layout(yaxis_range=[y_min, y_max])
# Adjust y-axis range based on slider value
if yaxis_slider_value:
fig.update_layout(yaxis_range=[y_min, yaxis_slider_value])
# Customize layout to include crosshairs with labels at the end
fig.update_layout(
title=dict(text=f'{ticker}: Candlestick Chart', font=dict(size=30)),
xaxis_title=dict(text='Time', font=dict(size=30)),
yaxis_title=dict(text='Price', font=dict(size=30)),
xaxis_rangeslider_visible=False,
hovermode=hovermode, # Dynamic hovermode based on radio button selection
xaxis=dict(
type='date',
title_font=dict(size=30),
rangebreaks=[
dict(bounds=["sat", "mon"]), # hide weekends
dict(bounds=[16, 9], pattern="hour"), # hide hours outside of 9.30am-4pm
dict(values=["2019-12-25", "2020-12-24"]) # hide holidays
],
showspikes=True,
spikemode='across',
spikesnap='cursor',
spikedash='solid',
spikethickness=2,
showline=True,
showgrid=True,
),
yaxis=dict(
title_font=dict(size=30),
autorange=False,
range=[y_min, yaxis_slider_value], # Use slider value for range
showspikes=True,
spikemode='across',
spikesnap='cursor',
spikedash='solid',
spikethickness=2,
showline=True,
showgrid=True,
),
plot_bgcolor='black',
paper_bgcolor='black',
font_color='white',
font=dict(
family="Arial, sans-serif",
size=30,
color="white"
),
hoverlabel=dict(
bgcolor="white",
font_size=16,
font_family="Arial"
),
dragmode='pan',
uirevision=ticker, # Ensure the user interactions persist across updates
)
# Update the y-axis slider dynamically
slider_min = y_min
slider_max = y_max
slider_value = y_max # Set slider value to the max of the y range
return fig, slider_min, slider_max, slider_value