- Updated `ChartBuilder` to support dynamic indicator integration, allowing users to specify overlay and subplot indicators for enhanced chart analysis. - Implemented a new `get_indicator_data` method in `MarketDataIntegrator` for fetching indicator data based on user configurations. - Added `create_export_controls` in `chart_controls.py` to facilitate data export options (CSV/JSON) for user analysis. - Enhanced error handling and logging throughout the chart and data analysis processes to improve reliability and user feedback. - Updated documentation to reflect new features and usage guidelines for indicator management and data export functionalities.
225 lines
8.4 KiB
Python
225 lines
8.4 KiB
Python
"""
|
||
Chart control components for the market data layout.
|
||
"""
|
||
|
||
from dash import html, dcc
|
||
from utils.logger import get_logger
|
||
|
||
logger = get_logger("default_logger")
|
||
|
||
|
||
def create_chart_config_panel(strategy_options, overlay_options, subplot_options):
|
||
"""Create the chart configuration panel with add/edit UI."""
|
||
return html.Div([
|
||
html.H5("🎯 Chart Configuration", style={'color': '#2c3e50', 'margin-bottom': '15px'}),
|
||
|
||
# Add New Indicator Button
|
||
html.Div([
|
||
html.Button(
|
||
"➕ Add New Indicator",
|
||
id="add-indicator-btn-visible",
|
||
className="btn btn-primary",
|
||
style={
|
||
'background-color': '#007bff',
|
||
'color': 'white',
|
||
'border': 'none',
|
||
'padding': '8px 16px',
|
||
'border-radius': '4px',
|
||
'cursor': 'pointer',
|
||
'margin-bottom': '15px',
|
||
'font-weight': 'bold'
|
||
}
|
||
)
|
||
]),
|
||
|
||
# Strategy Selection
|
||
html.Div([
|
||
html.Label("Strategy Template:", style={'font-weight': 'bold', 'margin-bottom': '5px'}),
|
||
dcc.Dropdown(
|
||
id='strategy-dropdown',
|
||
options=strategy_options,
|
||
value=None,
|
||
placeholder="Select a strategy template (optional)",
|
||
style={'margin-bottom': '15px'}
|
||
)
|
||
]),
|
||
|
||
# Indicator Controls with Edit Buttons
|
||
html.Div([
|
||
# Overlay Indicators
|
||
html.Div([
|
||
html.Label("Overlay Indicators:", style={'font-weight': 'bold', 'margin-bottom': '10px', 'display': 'block'}),
|
||
html.Div([
|
||
# Hidden checklist for callback compatibility
|
||
dcc.Checklist(
|
||
id='overlay-indicators-checklist',
|
||
options=overlay_options,
|
||
value=[], # Start with no indicators selected
|
||
style={'display': 'none'} # Hide the basic checklist
|
||
),
|
||
# Custom indicator list with edit buttons
|
||
html.Div(id='overlay-indicators-list', children=[
|
||
# This will be populated dynamically
|
||
])
|
||
])
|
||
], style={'width': '48%', 'display': 'inline-block', 'margin-right': '4%', 'vertical-align': 'top'}),
|
||
|
||
# Subplot Indicators
|
||
html.Div([
|
||
html.Label("Subplot Indicators:", style={'font-weight': 'bold', 'margin-bottom': '10px', 'display': 'block'}),
|
||
html.Div([
|
||
# Hidden checklist for callback compatibility
|
||
dcc.Checklist(
|
||
id='subplot-indicators-checklist',
|
||
options=subplot_options,
|
||
value=[], # Start with no indicators selected
|
||
style={'display': 'none'} # Hide the basic checklist
|
||
),
|
||
# Custom indicator list with edit buttons
|
||
html.Div(id='subplot-indicators-list', children=[
|
||
# This will be populated dynamically
|
||
])
|
||
])
|
||
], style={'width': '48%', 'display': 'inline-block', 'vertical-align': 'top'})
|
||
])
|
||
], style={
|
||
'border': '1px solid #bdc3c7',
|
||
'border-radius': '8px',
|
||
'padding': '15px',
|
||
'background-color': '#f8f9fa',
|
||
'margin-bottom': '20px'
|
||
})
|
||
|
||
|
||
def create_auto_update_control():
|
||
"""Create the auto-update control section."""
|
||
return html.Div([
|
||
dcc.Checklist(
|
||
id='auto-update-checkbox',
|
||
options=[{'label': ' Auto-update charts', 'value': 'auto'}],
|
||
value=['auto'],
|
||
style={'margin-bottom': '10px'}
|
||
),
|
||
html.Div(id='update-status', style={'font-size': '12px', 'color': '#7f8c8d'})
|
||
])
|
||
|
||
|
||
def create_time_range_controls():
|
||
"""Create the time range control panel."""
|
||
return html.Div([
|
||
html.H5("⏰ Time Range Controls", style={'color': '#2c3e50', 'margin-bottom': '15px'}),
|
||
|
||
# Quick Select Dropdown
|
||
html.Div([
|
||
html.Label("Quick Select:", style={'font-weight': 'bold', 'margin-bottom': '5px', 'display': 'block'}),
|
||
dcc.Dropdown(
|
||
id='time-range-quick-select',
|
||
options=[
|
||
{'label': '🕐 Last 1 Hour', 'value': '1h'},
|
||
{'label': '🕐 Last 4 Hours', 'value': '4h'},
|
||
{'label': '🕐 Last 6 Hours', 'value': '6h'},
|
||
{'label': '🕐 Last 12 Hours', 'value': '12h'},
|
||
{'label': '📅 Last 1 Day', 'value': '1d'},
|
||
{'label': '📅 Last 3 Days', 'value': '3d'},
|
||
{'label': '📅 Last 7 Days', 'value': '7d'},
|
||
{'label': '📅 Last 30 Days', 'value': '30d'},
|
||
{'label': '📅 Custom Range', 'value': 'custom'},
|
||
{'label': '🔴 Real-time', 'value': 'realtime'}
|
||
],
|
||
value='7d',
|
||
placeholder="Select time range",
|
||
style={'margin-bottom': '15px'}
|
||
)
|
||
]),
|
||
|
||
# Custom Date Range Picker
|
||
html.Div([
|
||
html.Label("Custom Date Range:", style={'font-weight': 'bold', 'margin-bottom': '5px', 'display': 'block'}),
|
||
html.Div([
|
||
dcc.DatePickerRange(
|
||
id='custom-date-range',
|
||
display_format='YYYY-MM-DD',
|
||
style={'display': 'inline-block', 'margin-right': '10px'}
|
||
),
|
||
html.Button(
|
||
"Clear",
|
||
id="clear-date-range-btn",
|
||
className="btn btn-sm btn-outline-secondary",
|
||
style={
|
||
'display': 'inline-block',
|
||
'vertical-align': 'top',
|
||
'margin-top': '7px',
|
||
'padding': '5px 10px',
|
||
'font-size': '12px'
|
||
}
|
||
)
|
||
], style={'margin-bottom': '15px'})
|
||
]),
|
||
|
||
# Analysis Mode Toggle
|
||
html.Div([
|
||
html.Label("Analysis Mode:", style={'font-weight': 'bold', 'margin-bottom': '5px', 'display': 'block'}),
|
||
dcc.RadioItems(
|
||
id='analysis-mode-toggle',
|
||
options=[
|
||
{'label': '🔴 Real-time Updates', 'value': 'realtime'},
|
||
{'label': '🔒 Analysis Mode (Locked)', 'value': 'locked'}
|
||
],
|
||
value='realtime',
|
||
inline=True,
|
||
style={'margin-bottom': '10px'}
|
||
)
|
||
]),
|
||
|
||
# Time Range Status
|
||
html.Div(id='time-range-status',
|
||
style={'font-size': '12px', 'color': '#7f8c8d', 'font-style': 'italic'})
|
||
|
||
], style={
|
||
'border': '1px solid #bdc3c7',
|
||
'border-radius': '8px',
|
||
'padding': '15px',
|
||
'background-color': '#f0f8ff',
|
||
'margin-bottom': '20px'
|
||
})
|
||
|
||
|
||
def create_export_controls():
|
||
"""Create the data export control panel."""
|
||
return html.Div([
|
||
html.H5("💾 Data Export", style={'color': '#2c3e50', 'margin-bottom': '15px'}),
|
||
html.Button(
|
||
"Export to CSV",
|
||
id="export-csv-btn",
|
||
className="btn btn-primary",
|
||
style={
|
||
'background-color': '#28a745',
|
||
'color': 'white',
|
||
'border': 'none',
|
||
'padding': '8px 16px',
|
||
'border-radius': '4px',
|
||
'cursor': 'pointer',
|
||
'margin-right': '10px'
|
||
}
|
||
),
|
||
html.Button(
|
||
"Export to JSON",
|
||
id="export-json-btn",
|
||
className="btn btn-primary",
|
||
style={
|
||
'background-color': '#17a2b8',
|
||
'color': 'white',
|
||
'border': 'none',
|
||
'padding': '8px 16px',
|
||
'border-radius': '4px',
|
||
'cursor': 'pointer'
|
||
}
|
||
),
|
||
dcc.Download(id="download-chart-data")
|
||
], style={
|
||
'border': '1px solid #bdc3c7',
|
||
'border-radius': '8px',
|
||
'padding': '15px',
|
||
'background-color': '#f8f9fa',
|
||
'margin-bottom': '20px'
|
||
}) |