Refactor indicator management to a data-driven approach

- 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.
This commit is contained in:
Vasily.onl
2025-06-11 19:09:52 +08:00
parent 89b071230e
commit 3e0e89b826
8 changed files with 406 additions and 249 deletions

View File

@@ -35,11 +35,57 @@ components/charts/config/
## Indicator Definitions
### Core Classes
The indicator definitions are now primarily managed through **JSON template files** located in `config/indicators/templates/`. These JSON files define the schema, default parameters, display properties, and styling for each technical indicator. This approach allows for easy addition and modification of indicators without requiring code changes.
#### `ChartIndicatorConfig`
### Core Schema Fields (defined in JSON templates)
The main configuration class for individual indicators:
Each indicator JSON template includes the following key fields:
- **`name`**: Display name of the indicator (e.g., "Simple Moving Average")
- **`description`**: Brief explanation of the indicator.
- **`type`**: Unique identifier for the indicator (e.g., "sma", "ema"). This is used for internal mapping.
- **`display_type`**: How the indicator is rendered on the chart ("overlay" or "subplot").
- **`timeframe`**: Optional default timeframe for the indicator (can be null for chart timeframe).
- **`default_parameters`**: Default values for the indicator's calculation parameters.
- **`parameter_schema`**: Defines the type, validation rules (min/max), default values, and descriptions for each parameter.
- **`default_styling`**: Default color, line width, and other visual properties.
**Example JSON Template (`config/indicators/templates/sma_template.json`):**
```json
{
"name": "Simple Moving Average",
"description": "Simple Moving Average indicator",
"type": "sma",
"display_type": "overlay",
"timeframe": null,
"default_parameters": {
"period": 20
},
"parameter_schema": {
"period": {
"type": "int",
"min": 1,
"max": 200,
"default": 20,
"description": "Period for SMA calculation"
},
"timeframe": {
"type": "string",
"default": null,
"description": "Indicator timeframe (e.g., '1h', '4h'). Null for chart timeframe."
}
},
"default_styling": {
"color": "#007bff",
"line_width": 2
}
}
```
### `ChartIndicatorConfig` (Python representation)
The `ChartIndicatorConfig` Python dataclass in `components/charts/config/indicator_defs.py` serves as the runtime representation of an indicator's configuration, parsed from the JSON templates.
```python
@dataclass
@@ -56,7 +102,9 @@ class ChartIndicatorConfig:
subplot_height_ratio: float = 0.3 # For subplot indicators
```
#### Enums
### Enums (for internal type safety)
Enums like `IndicatorType`, `DisplayType`, `LineStyle` are still used internally for type safety and consistent value representation within the Python codebase.
**IndicatorType**
```python
@@ -67,6 +115,7 @@ class IndicatorType(str, Enum):
MACD = "macd"
BOLLINGER_BANDS = "bollinger_bands"
VOLUME = "volume"
# ... new indicator types should be added here for internal consistency
```
**DisplayType**
@@ -86,7 +135,19 @@ class LineStyle(str, Enum):
DASH_DOT = "dashdot"
```
### Schema Validation
**PriceColumn**
```python
class PriceColumn(str, Enum):
OPEN = "open"
HIGH = "high"
LOW = "low"
CLOSE = "close"
VOLUME = "volume"
```
### Schema Validation (driven by JSON templates)
The validation system now primarily reads parameter schemas from the JSON templates. The `IndicatorParameterSchema` and `IndicatorSchema` dataclasses are used for internal representation when parsing and validating these JSON definitions.
#### `IndicatorParameterSchema`
@@ -119,39 +180,9 @@ class IndicatorSchema:
description: str = ""
```
### Schema Definitions
### Schema Definitions (now loaded dynamically)
The system includes complete schemas for all supported indicators:
```python
INDICATOR_SCHEMAS = {
IndicatorType.SMA: IndicatorSchema(
indicator_type=IndicatorType.SMA,
display_type=DisplayType.OVERLAY,
parameters=[
IndicatorParameterSchema(
name="period",
type=int,
min_value=1,
max_value=200,
default_value=20,
description="Number of periods for the moving average"
),
IndicatorParameterSchema(
name="price_column",
type=str,
required=False,
default_value="close",
valid_values=["open", "high", "low", "close"],
description="Price column to use for calculation"
)
],
description="Simple Moving Average - arithmetic mean of prices",
calculation_description="Sum of closing prices divided by period"
),
# ... more schemas
}
```
The `INDICATOR_SCHEMAS` dictionary is now populated dynamically at runtime by loading and parsing the JSON template files. Manual definitions in `indicator_defs.py` are deprecated.
### Utility Functions
@@ -636,33 +667,58 @@ if performance_issues:
### Adding New Indicators
1. **Define Indicator Type**
```python
# Add to IndicatorType enum
class IndicatorType(str, Enum):
# ... existing types
STOCHASTIC = "stochastic"
```
- Add to `IndicatorType` enum (if not already present)
2. **Create Schema**
```python
# Add to INDICATOR_SCHEMAS
INDICATOR_SCHEMAS[IndicatorType.STOCHASTIC] = IndicatorSchema(
indicator_type=IndicatorType.STOCHASTIC,
display_type=DisplayType.SUBPLOT,
parameters=[
IndicatorParameterSchema(
name="k_period",
type=int,
min_value=1,
max_value=100,
default_value=14
),
# ... more parameters
],
description="Stochastic Oscillator",
calculation_description="Momentum indicator comparing closing price to price range"
)
```
2. **Create JSON Template**
- Create a new JSON file in `config/indicators/templates/` (e.g., `stochastic_template.json`)
- Define the indicator's name, type, display type, default parameters, parameter schema, and default styling.
- **Example (`stochastic_template.json`):**
```json
{
"name": "Stochastic Oscillator",
"description": "Stochastic momentum oscillator indicator",
"type": "stochastic",
"display_type": "subplot",
"timeframe": null,
"default_parameters": {
"k_period": 14,
"d_period": 3,
"smooth_k": 1
},
"parameter_schema": {
"k_period": {
"type": "int",
"min": 2,
"max": 50,
"default": 14,
"description": "Period for %K calculation"
},
"d_period": {
"type": "int",
"min": 1,
"max": 20,
"default": 3,
"description": "Period for %D (moving average of %K)"
},
"smooth_k": {
"type": "int",
"min": 1,
"max": 10,
"default": 1,
"description": "Smoothing factor for %K"
},
"timeframe": {
"type": "string",
"default": null,
"description": "Indicator timeframe (e.g., '1h', '4h'). Null for chart timeframe."
}
},
"default_styling": {
"color": "#e83e8c",
"line_width": 2
}
}
```
3. **Create Default Presets**
```python

View File

@@ -89,7 +89,7 @@ These indicators are displayed in separate panels:
- Name: Custom name for the indicator
- Type: Select from available indicator types
- Description: Optional description
3. **Set Parameters**: Type-specific parameters appear dynamically
3. **Set Parameters**: Type-specific parameters appear dynamically (generated from JSON templates)
4. **Customize Styling**:
- Color: Hex color code
- Line Width: 1-5 pixels
@@ -169,7 +169,7 @@ class UserIndicator:
description: str # User description
type: str # Indicator type (sma, ema, etc.)
display_type: str # "overlay" or "subplot"
parameters: Dict[str, Any] # Type-specific parameters
parameters: Dict[str, Any] # Type-specific parameters, dynamically loaded from JSON templates
styling: IndicatorStyling # Appearance settings
visible: bool = True # Default visibility
created_date: datetime # Creation timestamp

View File

@@ -304,12 +304,13 @@ for timestamp, row in result_df.iterrows():
## Contributing
When adding new indicators:
1. Create a new class in `implementations/`
2. Inherit from `BaseIndicator`
3. Implement the `calculate` method to return a DataFrame
4. Ensure proper warm-up periods
5. Add comprehensive tests
6. Update documentation
1. Create a new class in `implementations/`.
2. Inherit from `BaseIndicator`.
3. Implement the `calculate` method to return a DataFrame.
4. Ensure proper warm-up periods.
5. Add comprehensive tests.
6. Create a corresponding **JSON template file** in `config/indicators/templates/` to define its parameters, display properties, and styling for UI integration.
7. Update documentation in `docs/guides/adding-new-indicators.md`.
See [Adding New Indicators](./adding-new-indicators.md) for detailed instructions.