TCPDashboard/dashboard/components/indicator_modal.py
Vasily.onl 89b071230e Add utility functions for managing indicator configurations
- Introduced `config_utils.py` to provide utility functions for loading and managing indicator templates, enhancing modularity and maintainability.
- Implemented functions to load templates, generate dropdown options, and retrieve parameter schemas, default parameters, and styling for various indicators.
- Updated the indicator modal to dynamically create parameter fields based on the loaded configurations, improving user experience and reducing redundancy.
- Refactored existing parameter field creation logic to utilize the new utility functions, streamlining the codebase and adhering to project standards for clarity and maintainability.

These changes significantly enhance the configuration management for indicators, aligning with project goals for modularity and performance.
2025-06-11 18:52:02 +08:00

169 lines
7.0 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
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")]
),
# Parameter fields (SMA, EMA, etc.)
create_dynamic_parameter_fields('sma'),
create_dynamic_parameter_fields('ema'),
create_dynamic_parameter_fields('rsi'),
create_dynamic_parameter_fields('macd'),
create_dynamic_parameter_fields('bollinger_bands'),
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),
])