How to display Resampling of candles / time series data in plotly?

Viewed 181

How can I merge the two functions given below to achieve something like the histogram example. Any button or drop down would do fine.

If you run the function, you get a nice Candlesticks chart with the functionality of removing non trading day gaps.

def plot_candlesticks(df, names = ('DATE','OPEN','CLOSE','LOW','HIGH'), mv:list = [200], slider:bool = False, fig_size:bool = (1400,700), plot:bool = True):
        '''
        Plot a candlestick on a given dataframe
        args:
            df: DataFrame
            names: Tuple of column names showing ('DATE','OPEN','CLOSE','LOW','HIGH')
            mv: Moving Averages
            slider: Whether to have below zoom slider or not
            fig_size: Size of Figure as (Width, Height)
            plotting: Whether to plot the figure or just return the figure for firther modifications
        '''
        freq = 5 # 5 min candle
        candle_text = f"{str(freq)} Min"
        
        stocks = df.copy()
        stocks.sort_index(ascending=False, inplace = True)  # Without reverse, recent rolling mean will be either NaN or equal to the exact value

        Date, Open, Close, Low, High = names

        mv = [] if not mv else mv # just in case you don't want to have any moving averages
        colors = sample(['black','magenta','teal','brown','violet'],len(mv))

        # To remove, non-trading days, grab first and last observations from df.date and make a continuous date range from that
        start = stocks['DATE'].iloc[0] - timedelta(days=1)
        end = stocks['DATE'].iloc[-1] + timedelta(days=1)

        dt_all = pd.date_range(start=start,end=end, freq = f'{str(freq)}min')
        # check which dates from your source that also accur in the continuous date range
        dt_obs = [d.strftime("%Y-%m-%d %H:%M:%S") for d in stocks['DATE']]
        # isolate missing timestamps
        dt_breaks = [d for d in dt_all.strftime("%Y-%m-%d %H:%M:%S").tolist() if not d in dt_obs]

        rangebreaks=[dict(dvalue = freq*60*1000, values=dt_breaks)]

        range_selector = dict(buttons = list([dict(step = 'all', label = 'All')]))

        candle = go.Figure(data = [go.Candlestick(opacity = 0.9, x = stocks[Date], name = 'X',
                                                  open = stocks[Open], high = stocks[High], low = stocks[Low], close = stocks[Close]),])
        
        for i in range(len(mv)):
            stocks[f'{str(mv[i])}-SMA'] = stocks[Close].rolling(mv[i], min_periods = 1).mean()
            candle.add_trace(go.Scatter(name=f'{str(mv[i])} MA',x=stocks[Date], y=stocks[f'{str(mv[i])}-SMA'], 
                                             line=dict(color=colors[i], width=1.7)))

            
        candle.update_xaxes(title_text = 'Date', rangeslider_visible = slider, rangeselector = range_selector, rangebreaks=rangebreaks)


        candle.update_layout(autosize = False, width = fig_size[0], height = fig_size[1],
                             title = {'text': f"{stocks['SYMBOL'][0]} : {str(candle_text)} Candles",'y':0.97,'x':0.5,
                                      'xanchor': 'center','yanchor': 'top'},
                             margin=dict(l=30,r=30,b=30,t=30,pad=2),
                             paper_bgcolor="lightsteelblue")

        candle.update_yaxes(title_text = 'Price in Rupees', tickprefix = u"\u20B9" ) # Rupee symbol
        if plot:
            candle.show()
        return candle

and running the below code resamples your data.

def resample_data(self,to:str = '15min', names:tuple = ('OPEN','CLOSE','LOW','HIGH','DATE')):
    '''
    Resample the data from 5 Minutes to 15 or 75 Minutes
    args:
        data: Dataframe of Daily data
        to: One of  [15M, 75M]
    '''
    Open, Close, Low, High, Date = names
    data = data.resample(to,on=Date).agg({Open:'first', High:'max', Low: 'min', Close:'last'})
    return data.sort_index(ascending = False).reset_index()

Is there a functionality when I click 15M / 75M button in my chart, it shows me exactly the same data but resampled? Just like there is functionality in online trading softwares.

1 Answers
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import ipywidgets as widgets

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv",
    parse_dates=["Date"],
)

fig = go.FigureWidget(
    data=[
        go.Candlestick(
            x=df["Date"],
            open=df["AAPL.Open"],
            high=df["AAPL.High"],
            low=df["AAPL.Low"],
            close=df["AAPL.Close"],
        )
    ]
).update_layout(margin={"t": 30, "b": 0, "l": 0, "r": 0})

out = widgets.Output(layout={"border": "1px solid black"})
out.append_stdout("Output appended with append_stdout\n")

reset = widgets.Button(description="Reset")
slider = widgets.IntSlider(
    value=1,
    min=1,
    max=10,
    step=1,
    description='Days:',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='d'
)

@out.capture()
def on_slider_change(v):
    print(f"slider: {v['new']}")
    dfr = df.resample(f"{v['new']}B", on="Date").mean().reset_index()
    t = fig.data[0]
    t.update(
        x=dfr["Date"],
        open=dfr["AAPL.Open"],
        high=dfr["AAPL.High"],
        low=dfr["AAPL.Low"],
        close=dfr["AAPL.Close"],
    )

@out.capture()
def on_reset_clicked(b):
    print("reset")
    t = fig.data[0]
    t.update(
        x=df["Date"],
        open=df["AAPL.Open"],
        high=df["AAPL.High"],
        low=df["AAPL.Low"],
        close=df["AAPL.Close"],
    )
    out.clear_output()


reset.on_click(on_reset_clicked)
slider.observe(on_slider_change, names='value')

widgets.VBox([widgets.HBox([reset, slider]), widgets.VBox([fig, out])])

enter image description here

Related