Skip to content

Market Candles example. #500

@mifth

Description

@mifth

Hi, I would like to contribute an example but I'm lazy to do forking etc. I'm just pasting my code here in hope that one hero will be able to polish it and check in.

Image

import pandas as pd
import numpy as np
import plotly.graph_objects as go
import panel as pn

pn.extension('plotly')


class SessionPanel:
    def __init__(self):
        self.n = 50
        self.df: pd.DataFrame = self._make_df()
        self.fig: go.Figure = self._make_fig()
        self.plotly_pane: pn.pane.Plotly = pn.pane.Plotly(self.fig, sizing_mode='stretch_both')
        # self.plotly_pane.object = self.fig  # Example of setting the object directly

    def __del__(self):
        print("SessionPanel instance is being deleted!!!")

    def _make_df(self) -> pd.DataFrame:
        dates = pd.date_range("2023-01-01", periods=self.n, freq='min')
        price = np.cumsum(np.random.randn(self.n)) + 100
        df = pd.DataFrame(dict(
            Date=dates,
            Open=price + np.random.uniform(-1, 1, self.n),
        ))
        df['Close'] = df['Open'] + np.random.uniform(-2, 2, self.n)
        df['High'] = np.maximum(df['Open'], df['Close']) + np.random.uniform(0, 1, self.n)
        df['Low'] = np.minimum(df['Open'], df['Close']) - np.random.uniform(0, 1, self.n)
        return df

    def _make_fig(self) -> go.Figure:
        fig = go.Figure(
            go.Candlestick(
                x=self.df['Date'],
                open=self.df['Open'],
                high=self.df['High'],
                low=self.df['Low'],
                close=self.df['Close']
            )
        )
        fig.update_layout(
            title="Live Candlestick Chart",
            xaxis_rangeslider_visible=False
        )
        return fig

    def update(self):
        # Create one new row of data
        new_date = self.df['Date'].iloc[-1] + pd.Timedelta(minutes=1)
        new_open = self.df['Close'].iloc[-1] + np.random.uniform(-1, 1)
        new_close = new_open + np.random.uniform(-2, 2)
        new_high = max(new_open, new_close) + np.random.uniform(0, 1)
        new_low = min(new_open, new_close) - np.random.uniform(0, 1)

        new_row = pd.DataFrame(dict(
            Date=[new_date],
            Open=[new_open],
            High=[new_high],
            Low=[new_low],
            Close=[new_close]
        ))

        # Slide the window
        self.df = pd.concat([self.df.iloc[1:], new_row], ignore_index=True)

        # Patch only the changed data in the dict
        self.plotly_pane.object['data'][0]['x'] = self.df['Date']
        self.plotly_pane.object['data'][0]['open'] = self.df['Open']
        self.plotly_pane.object['data'][0]['high'] = self.df['High']
        self.plotly_pane.object['data'][0]['low'] = self.df['Low']
        self.plotly_pane.object['data'][0]['close'] = self.df['Close']

class DashboardPanel(pn.Column):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # Create session-unique data and plot
        self.session = SessionPanel()

        # Widgets
        self.button = pn.widgets.Button(name='Click Me', button_type='primary')
        self.select_opt = pn.widgets.Select(name='Market', options=['BTC/USD', 'ETH/USD', 'SPY'])
        self.text = pn.widgets.TextInput(name='Notes', placeholder='Enter notes...')

        self.button.on_click(self.say_hello)

        self.tab1 = pn.Column(
            "## Controls",
            self.button,
            self.select_opt,
        )

        self.tab2 = pn.Column(
            "## Notes",
            self.text
        )

        self.tabs = pn.Tabs(
            ("Tab 1", self.tab1),
            ("Tab 2", self.tab2)
        )

        self.dashboard = pn.Row(
            pn.Column("# Left Panel", self.tabs, width=300, sizing_mode='stretch_height'),
            pn.Column("# Right Panel", self.session.plotly_pane, sizing_mode='stretch_both'),
            sizing_mode='stretch_both'
        )

        # Add the dashboard layout to this Column
        self.append(self.dashboard)

        # Add periodic callback for updates (session-specific)
        pn.state.add_periodic_callback(self.session.update, period=1000)

        # Add session cleanup callback
        pn.state.on_session_destroyed(self.on_session_destroyed)

    def say_hello(self, event):
        print(f"Button clicked! Market: {self.select_opt.value}, Note: {self.text.value}")

    def on_session_destroyed(self, session_context):
        print("Session ended!")


# Show dashboard based on session
def app():
    main_panel: pn.Column = pn.Column()
    main_panel.append(DashboardPanel())
    return main_panel

app().servable()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions