import pandas as pd import pandas_ta as ta import dash from dash import dcc, html from dash.dependencies import Input, Output, State import plotly.graph_objects as go from plotly.subplots import make_subplots # Load sample data (replace with actual data) data = pd.read_csv('data/brent_futures.csv') data['Date'] = pd.to_datetime(data['Date']) data.sort_values('Date', inplace=True) # Initialize Dash app app = dash.Dash(__name__) # Dash layout app.layout = html.Div([ html.H1("OHLCV Chart with Technical Indicators"), # Dropdown for selecting moving averages and other technical indicators html.Div([ html.Label("Select Moving Averages"), dcc.Dropdown( id='moving-avg-choice', options=[ {'label': 'MA 4', 'value': 'MA_4'}, {'label': 'MA 10', 'value': 'MA_10'}, {'label': 'MA 30', 'value': 'MA_30'} ], multi=True, value=['MA_4', 'MA_10'] # Default selected ), html.Br(), # Sliders for lengths of technical indicators html.Label("MA 4 Length"), dcc.Slider(id='ma4-length', min=2, max=20, value=4, marks={i: str(i) for i in range(2, 21)}), html.Br(), html.Label("MA 10 Length"), dcc.Slider(id='ma10-length', min=5, max=50, value=10, marks={i: str(i) for i in range(5, 51, 5)}), html.Br(), html.Label("MA 30 Length"), dcc.Slider(id='ma30-length', min=10, max=100, value=30, marks={i: str(i) for i in range(10, 101, 10)}), html.Br(), dcc.Checklist( id='indicator-choice', options=[ {'label': 'Bollinger Bands', 'value': 'BB'}, {'label': 'RSI', 'value': 'RSI'}, {'label': 'MACD', 'value': 'MACD'} ], value=['BB', 'RSI'], # Default checked labelStyle={'display': 'inline-block'} ) ], style={'width': '30%', 'display': 'inline-block', 'padding': '20px'}), # Div for rendering plotly chart html.Div([dcc.Graph(id='technical-chart')], style={'width': '68%', 'display': 'inline-block'}), ]) # Callback to update chart based on selected technical analyses and lengths @app.callback( Output('technical-chart', 'figure'), Input('moving-avg-choice', 'value'), Input('ma4-length', 'value'), Input('ma10-length', 'value'), Input('ma30-length', 'value'), Input('indicator-choice', 'value') ) def update_chart(moving_avg_choice, ma4_length, ma10_length, ma30_length, indicators): # Reset calculations for indicators data['MA_4'] = ta.sma(data['Close'], length=ma4_length) if 'MA_4' in moving_avg_choice else None data['MA_10'] = ta.sma(data['Close'], length=ma10_length) if 'MA_10' in moving_avg_choice else None data['MA_30'] = ta.sma(data['Close'], length=ma30_length) if 'MA_30' in moving_avg_choice else None if 'BB' in indicators: bbands = ta.bbands(data['Close'], length=20) data['BB_upper'], data['BB_middle'], data['BB_lower'] = bbands.iloc[:, 2], bbands.iloc[:, 1], bbands.iloc[:, 0] if 'RSI' in indicators: data['RSI'] = ta.rsi(data['Close'], length=8) if 'MACD' in indicators: macd = ta.macd(data['Close'], fast=12, slow=26, signal=9) data['MACD'], data['MACD_Signal'], data['MACD_Hist'] = macd.iloc[:, 0], macd.iloc[:, 1], macd.iloc[:, 2] # Create subplots fig = make_subplots( rows=4 if 'MACD' in indicators else 3, cols=1, shared_xaxes=True, vertical_spacing=0.05, row_heights=[0.6, 0.2, 0.2, 0.2] if 'MACD' in indicators else [0.6, 0.2, 0.2], subplot_titles=('OHLC with Technical Indicators', 'RSI', 'MACD', 'Volume') if 'MACD' in indicators else ('OHLC with Technical Indicators', 'RSI', 'Volume') ) # Plot OHLC candlestick fig.add_trace(go.Candlestick(x=data['Date'], open=data['Open'], high=data['High'], low=data['Low'], close=data['Close'], name="OHLC"), row=1, col=1) # Plot Moving Averages if 'MA_4' in moving_avg_choice: fig.add_trace(go.Scatter(x=data['Date'], y=data['MA_4'], mode='lines', name=f'MA {ma4_length}', line=dict(color='blue')), row=1, col=1) if 'MA_10' in moving_avg_choice: fig.add_trace(go.Scatter(x=data['Date'], y=data['MA_10'], mode='lines', name=f'MA {ma10_length}', line=dict(color='orange')), row=1, col=1) if 'MA_30' in moving_avg_choice: fig.add_trace(go.Scatter(x=data['Date'], y=data['MA_30'], mode='lines', name=f'MA {ma30_length}', line=dict(color='green')), row=1, col=1) # Plot Bollinger Bands if 'BB' in indicators: fig.add_trace(go.Scatter(x=data['Date'], y=data['BB_upper'], mode='lines', name='BB Upper', line=dict(color='purple')), row=1, col=1) fig.add_trace(go.Scatter(x=data['Date'], y=data['BB_middle'], mode='lines', name='BB Middle', line=dict(color='gray')), row=1, col=1) fig.add_trace(go.Scatter(x=data['Date'], y=data['BB_lower'], mode='lines', name='BB Lower', line=dict(color='purple')), row=1, col=1) # Plot RSI if 'RSI' in indicators: fig.add_trace(go.Scatter(x=data['Date'], y=data['RSI'], mode='lines', name='RSI', line=dict(color='magenta')), row=2, col=1) # Plot MACD if 'MACD' in indicators: fig.add_trace(go.Scatter(x=data['Date'], y=data['MACD'], mode='lines', name='MACD', line=dict(color='blue')), row=3, col=1) fig.add_trace(go.Scatter(x=data['Date'], y=data['MACD_Signal'], mode='lines', name='MACD Signal', line=dict(color='red')), row=3, col=1) fig.add_trace(go.Bar(x=data['Date'], y=data['MACD_Hist'], name='MACD Hist', marker=dict(color='green')), row=3, col=1) # Plot Volume fig.add_trace(go.Bar(x=data['Date'], y=data['Volume'], name='Volume', marker=dict(color='darkgreen')), row=4 if 'MACD' in indicators else 3, col=1) # Customize layout fig.update_layout( height=1000, showlegend=True, xaxis_rangeslider_visible=False, dragmode='drawline' # Allow drawing lines on the chart ) # Enable dynamic range for y-axes fig.update_yaxes(autorange=True) return fig # Run the app if __name__ == '__main__': app.run_server(debug=True)