TCPDashboard/dashboard/components/indicator_modal.py

169 lines
7.0 KiB
Python
Raw Normal View History

"""
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),
])