- Introduced dynamic generation of parameter fields and callback handling for indicators, enhancing modularity and maintainability. - Updated `config_utils.py` with new utility functions to load indicator templates and generate dynamic outputs and states for parameter fields. - Refactored `indicators.py` to utilize these utilities, streamlining the callback logic and improving user experience by reducing hardcoded elements. - Modified `indicator_modal.py` to create parameter fields dynamically based on JSON templates, eliminating the need for manual updates when adding new indicators. - Added documentation outlining the new data-driven architecture for indicators, improving clarity and guidance for future development. These changes significantly enhance the flexibility and scalability of the indicator system, aligning with project goals for maintainability and performance.
165 lines
6.9 KiB
Python
165 lines
6.9 KiB
Python
"""
|
|
Indicator modal component for creating and editing indicators.
|
|
"""
|
|
|
|
from dash import html, dcc
|
|
import dash_bootstrap_components as dbc
|
|
from utils.timeframe_utils import load_timeframe_options
|
|
from config.indicators.config_utils import get_indicator_dropdown_options, generate_parameter_fields_config, load_indicator_templates
|
|
|
|
|
|
def create_dynamic_parameter_fields(indicator_type: str) -> html.Div:
|
|
"""Create parameter input fields dynamically based on indicator configuration.
|
|
|
|
Args:
|
|
indicator_type (str): The indicator type (e.g., 'sma', 'ema')
|
|
|
|
Returns:
|
|
html.Div: Div containing the parameter input fields
|
|
"""
|
|
fields_config = generate_parameter_fields_config(indicator_type)
|
|
|
|
if not fields_config:
|
|
return html.Div(
|
|
html.P("No parameters available for this indicator", className="text-muted fst-italic"),
|
|
id=f'{indicator_type}-parameters',
|
|
style={'display': 'none'},
|
|
className="mb-3"
|
|
)
|
|
|
|
field_elements = []
|
|
|
|
# Handle single parameter (like SMA, EMA, RSI)
|
|
if len(fields_config) == 1:
|
|
param_name, field_config = next(iter(fields_config.items()))
|
|
field_elements.append(
|
|
html.Div([
|
|
dbc.Label(f"{field_config['label']}:"),
|
|
dcc.Input(
|
|
id=field_config['input_id'],
|
|
type='number',
|
|
value=field_config['default'],
|
|
min=field_config.get('min'),
|
|
max=field_config.get('max'),
|
|
step=field_config.get('step', 1 if field_config['type'] == 'int' else 0.1)
|
|
),
|
|
dbc.FormText(field_config['description']) if field_config['description'] else None
|
|
])
|
|
)
|
|
else:
|
|
# Handle multiple parameters (like MACD, Bollinger Bands)
|
|
rows = []
|
|
params_per_row = min(len(fields_config), 4) # Max 4 parameters per row
|
|
|
|
param_items = list(fields_config.items())
|
|
for i in range(0, len(param_items), params_per_row):
|
|
row_params = param_items[i:i + params_per_row]
|
|
cols = []
|
|
|
|
for param_name, field_config in row_params:
|
|
col = dbc.Col([
|
|
dbc.Label(f"{field_config['label']}:"),
|
|
dcc.Input(
|
|
id=field_config['input_id'],
|
|
type='number',
|
|
value=field_config['default'],
|
|
min=field_config.get('min'),
|
|
max=field_config.get('max'),
|
|
step=field_config.get('step', 1 if field_config['type'] == 'int' else 0.1)
|
|
)
|
|
], width=12 // len(row_params))
|
|
cols.append(col)
|
|
|
|
rows.append(dbc.Row(cols))
|
|
|
|
field_elements.extend(rows)
|
|
|
|
# Add description for multi-parameter indicators
|
|
if any(config['description'] for config in fields_config.values()):
|
|
descriptions = [f"{config['label']}: {config['description']}"
|
|
for config in fields_config.values() if config['description']]
|
|
field_elements.append(
|
|
dbc.FormText("; ".join(descriptions))
|
|
)
|
|
|
|
return html.Div(
|
|
field_elements,
|
|
id=f'{indicator_type}-parameters',
|
|
style={'display': 'none'},
|
|
className="mb-3"
|
|
)
|
|
|
|
|
|
def create_indicator_modal():
|
|
"""Create the indicator modal dialog for adding/editing indicators."""
|
|
return html.Div([
|
|
dcc.Store(id='edit-indicator-store', data=None),
|
|
dbc.Modal([
|
|
dbc.ModalHeader(dbc.ModalTitle("📊 Add New Indicator", id="modal-title")),
|
|
dbc.ModalBody([
|
|
# Basic Settings
|
|
html.H5("Basic Settings"),
|
|
dbc.Row([
|
|
dbc.Col(dbc.Label("Indicator Name:"), width=12),
|
|
dbc.Col(dcc.Input(id='indicator-name-input', type='text', placeholder='e.g., "SMA 30 Custom"', className="w-100"), width=12)
|
|
], className="mb-3"),
|
|
dbc.Row([
|
|
dbc.Col(dbc.Label("Indicator Type:"), width=12),
|
|
dbc.Col(dcc.Dropdown(
|
|
id='indicator-type-dropdown',
|
|
options=get_indicator_dropdown_options(),
|
|
placeholder='Select indicator type',
|
|
), width=12)
|
|
], className="mb-3"),
|
|
dbc.Row([
|
|
dbc.Col(dbc.Label("Timeframe (Optional):"), width=12),
|
|
dbc.Col(dcc.Dropdown(
|
|
id='indicator-timeframe-dropdown',
|
|
options=[{'label': 'Chart Timeframe', 'value': ''}] + load_timeframe_options(),
|
|
value='',
|
|
placeholder='Defaults to chart timeframe'
|
|
), width=12),
|
|
], className="mb-3"),
|
|
dbc.Row([
|
|
dbc.Col(dbc.Label("Description (Optional):"), width=12),
|
|
dbc.Col(dcc.Textarea(
|
|
id='indicator-description-input',
|
|
placeholder='Brief description of this indicator configuration...',
|
|
style={'width': '100%', 'height': '60px'}
|
|
), width=12)
|
|
], className="mb-3"),
|
|
html.Hr(),
|
|
|
|
# Parameters Section
|
|
html.H5("Parameters"),
|
|
html.Div(
|
|
id='indicator-parameters-message',
|
|
children=[html.P("Select an indicator type to configure parameters", className="text-muted fst-italic")]
|
|
),
|
|
|
|
# Dynamically generate parameter fields for all indicator types
|
|
*[create_dynamic_parameter_fields(indicator_type) for indicator_type in load_indicator_templates().keys()],
|
|
|
|
html.Hr(),
|
|
# Styling Section
|
|
html.H5("Styling"),
|
|
dbc.Row([
|
|
dbc.Col([
|
|
dbc.Label("Color:"),
|
|
dcc.Input(id='indicator-color-input', type='text', value='#007bff', className="w-100")
|
|
], width=6),
|
|
dbc.Col([
|
|
dbc.Label("Line Width:"),
|
|
dcc.Slider(id='indicator-line-width-slider', min=1, max=5, step=1, value=2, marks={i: str(i) for i in range(1, 6)})
|
|
], width=6)
|
|
], className="mb-3"),
|
|
]),
|
|
dbc.ModalFooter([
|
|
html.Div(id='save-indicator-feedback', className="me-auto"),
|
|
dbc.Button("Cancel", id="cancel-indicator-btn", color="secondary"),
|
|
dbc.Button("Save Indicator", id="save-indicator-btn", color="primary")
|
|
])
|
|
], id='indicator-modal', size="lg", is_open=False),
|
|
])
|
|
|
|
|