249 lines
8.3 KiB
Markdown
249 lines
8.3 KiB
Markdown
# Quick Guide: Adding New Indicators
|
|
|
|
## Overview
|
|
|
|
This guide provides a step-by-step checklist for adding new technical indicators to the Crypto Trading Bot Dashboard, updated for the new modular dashboard structure.
|
|
|
|
## Prerequisites
|
|
|
|
- Understanding of Python and technical analysis
|
|
- Familiarity with the project structure and Dash callbacks
|
|
- Knowledge of the indicator type (overlay vs subplot)
|
|
|
|
## Step-by-Step Checklist
|
|
|
|
### ✅ Step 1: Plan Your Indicator
|
|
|
|
- [ ] Determine indicator type (overlay or subplot)
|
|
- [ ] Define required parameters
|
|
- [ ] Choose default styling
|
|
- [ ] Research calculation formula
|
|
|
|
### ✅ Step 2: Create Indicator Class
|
|
|
|
**File**: `components/charts/layers/indicators.py` (overlay) or `components/charts/layers/subplots.py` (subplot)
|
|
|
|
Create a class for your indicator that inherits from `IndicatorLayer`.
|
|
|
|
```python
|
|
class StochasticLayer(IndicatorLayer):
|
|
def __init__(self, config: Dict[str, Any]):
|
|
super().__init__(config)
|
|
self.name = "stochastic"
|
|
self.display_type = "subplot"
|
|
|
|
def calculate_values(self, df: pd.DataFrame) -> Dict[str, pd.Series]:
|
|
k_period = self.config.get('k_period', 14)
|
|
d_period = self.config.get('d_period', 3)
|
|
lowest_low = df['low'].rolling(window=k_period).min()
|
|
highest_high = df['high'].rolling(window=k_period).max()
|
|
k_percent = 100 * ((df['close'] - lowest_low) / (highest_high - lowest_low))
|
|
d_percent = k_percent.rolling(window=d_period).mean()
|
|
return {'k_percent': k_percent, 'd_percent': d_percent}
|
|
|
|
def create_traces(self, df: pd.DataFrame, values: Dict[str, pd.Series]) -> List[go.Scatter]:
|
|
traces = []
|
|
traces.append(go.Scatter(x=df.index, y=values['k_percent'], mode='lines', name=f"%K ({self.config.get('k_period', 14)})", line=dict(color=self.config.get('color', '#007bff'), width=self.config.get('line_width', 2))))
|
|
traces.append(go.Scatter(x=df.index, y=values['d_percent'], mode='lines', name=f"%D ({self.config.get('d_period', 3)})", line=dict(color=self.config.get('secondary_color', '#ff6b35'), width=self.config.get('line_width', 2))))
|
|
return traces
|
|
```
|
|
|
|
### ✅ Step 3: Register Indicator
|
|
|
|
**File**: `components/charts/layers/__init__.py`
|
|
|
|
Register your new indicator class in the appropriate registry.
|
|
|
|
```python
|
|
from .subplots import StochasticLayer
|
|
|
|
SUBPLOT_REGISTRY = {
|
|
'rsi': RSILayer,
|
|
'macd': MACDLayer,
|
|
'stochastic': StochasticLayer,
|
|
}
|
|
|
|
INDICATOR_REGISTRY = {
|
|
'sma': SMALayer,
|
|
'ema': EMALayer,
|
|
'bollinger_bands': BollingerBandsLayer,
|
|
}
|
|
```
|
|
|
|
### ✅ Step 4: Add UI Dropdown Option
|
|
|
|
**File**: `dashboard/components/indicator_modal.py`
|
|
|
|
Add your new indicator to the `indicator-type-dropdown` options.
|
|
|
|
```python
|
|
dcc.Dropdown(
|
|
id='indicator-type-dropdown',
|
|
options=[
|
|
{'label': 'Simple Moving Average (SMA)', 'value': 'sma'},
|
|
{'label': 'Exponential Moving Average (EMA)', 'value': 'ema'},
|
|
{'label': 'Relative Strength Index (RSI)', 'value': 'rsi'},
|
|
{'label': 'MACD', 'value': 'macd'},
|
|
{'label': 'Bollinger Bands', 'value': 'bollinger_bands'},
|
|
{'label': 'Stochastic Oscillator', 'value': 'stochastic'},
|
|
],
|
|
placeholder='Select indicator type',
|
|
)
|
|
```
|
|
|
|
### ✅ Step 5: Add Parameter Fields to Modal
|
|
|
|
**File**: `dashboard/components/indicator_modal.py`
|
|
|
|
In `create_parameter_fields`, add the `dcc.Input` components for your indicator's parameters.
|
|
|
|
```python
|
|
def create_parameter_fields():
|
|
return html.Div([
|
|
# ... existing parameter fields ...
|
|
html.Div([
|
|
dbc.Row([
|
|
dbc.Col([dbc.Label("%K Period:"), dcc.Input(id='stochastic-k-period-input', type='number', value=14)], width=6),
|
|
dbc.Col([dbc.Label("%D Period:"), dcc.Input(id='stochastic-d-period-input', type='number', value=3)], width=6),
|
|
]),
|
|
dbc.FormText("Stochastic oscillator periods for %K and %D lines")
|
|
], id='stochastic-parameters', style={'display': 'none'}, className="mb-3")
|
|
])
|
|
```
|
|
|
|
### ✅ Step 6: Update Parameter Visibility Callback
|
|
|
|
**File**: `dashboard/callbacks/indicators.py`
|
|
|
|
In `update_parameter_fields`, add an `Output` and logic to show/hide your new parameter fields.
|
|
|
|
```python
|
|
@app.callback(
|
|
[Output('indicator-parameters-message', 'style'),
|
|
Output('sma-parameters', 'style'),
|
|
Output('ema-parameters', 'style'),
|
|
Output('rsi-parameters', 'style'),
|
|
Output('macd-parameters', 'style'),
|
|
Output('bb-parameters', 'style'),
|
|
Output('stochastic-parameters', 'style')],
|
|
Input('indicator-type-dropdown', 'value'),
|
|
)
|
|
def update_parameter_fields(indicator_type):
|
|
styles = { 'sma': {'display': 'none'}, 'ema': {'display': 'none'}, 'rsi': {'display': 'none'}, 'macd': {'display': 'none'}, 'bb': {'display': 'none'}, 'stochastic': {'display': 'none'} }
|
|
message_style = {'display': 'block'} if not indicator_type else {'display': 'none'}
|
|
if indicator_type:
|
|
styles[indicator_type] = {'display': 'block'}
|
|
return [message_style] + list(styles.values())
|
|
```
|
|
|
|
### ✅ Step 7: Update Save Indicator Callback
|
|
|
|
**File**: `dashboard/callbacks/indicators.py`
|
|
|
|
In `save_new_indicator`, add `State` inputs for your parameters and logic to collect them.
|
|
|
|
```python
|
|
@app.callback(
|
|
# ... Outputs ...
|
|
Input('save-indicator-btn', 'n_clicks'),
|
|
[# ... States ...
|
|
State('stochastic-k-period-input', 'value'),
|
|
State('stochastic-d-period-input', 'value'),
|
|
State('edit-indicator-store', 'data')],
|
|
)
|
|
def save_new_indicator(n_clicks, name, indicator_type, ..., stochastic_k, stochastic_d, edit_data):
|
|
# ...
|
|
elif indicator_type == 'stochastic':
|
|
parameters = {'k_period': stochastic_k or 14, 'd_period': stochastic_d or 3}
|
|
# ...
|
|
```
|
|
|
|
### ✅ Step 8: Update Edit Callback Parameters
|
|
|
|
**File**: `dashboard/callbacks/indicators.py`
|
|
|
|
In `edit_indicator`, add `Output`s for your parameter fields and logic to load values.
|
|
|
|
```python
|
|
@app.callback(
|
|
[# ... Outputs ...
|
|
Output('stochastic-k-period-input', 'value'),
|
|
Output('stochastic-d-period-input', 'value')],
|
|
Input({'type': 'edit-indicator-btn', 'index': dash.ALL}, 'n_clicks'),
|
|
)
|
|
def edit_indicator(edit_clicks, button_ids):
|
|
# ...
|
|
stochastic_k, stochastic_d = 14, 3
|
|
if indicator:
|
|
# ...
|
|
elif indicator.type == 'stochastic':
|
|
stochastic_k = params.get('k_period', 14)
|
|
stochastic_d = params.get('d_period', 3)
|
|
return (..., stochastic_k, stochastic_d)
|
|
```
|
|
|
|
### ✅ Step 9: Update Reset Callback
|
|
|
|
**File**: `dashboard/callbacks/indicators.py`
|
|
|
|
In `reset_modal_form`, add `Output`s for your parameter fields and their default values.
|
|
|
|
```python
|
|
@app.callback(
|
|
[# ... Outputs ...
|
|
Output('stochastic-k-period-input', 'value', allow_duplicate=True),
|
|
Output('stochastic-d-period-input', 'value', allow_duplicate=True)],
|
|
Input('cancel-indicator-btn', 'n_clicks'),
|
|
)
|
|
def reset_modal_form(cancel_clicks):
|
|
# ...
|
|
return ..., 14, 3
|
|
```
|
|
|
|
### ✅ Step 10: Create Default Template
|
|
|
|
**File**: `components/charts/indicator_defaults.py`
|
|
|
|
Create a default template for your indicator.
|
|
|
|
```python
|
|
def create_stochastic_template() -> UserIndicator:
|
|
return UserIndicator(
|
|
id=f"stochastic_{generate_short_id()}",
|
|
name="Stochastic 14,3",
|
|
type="stochastic",
|
|
display_type="subplot",
|
|
parameters={"k_period": 14, "d_period": 3},
|
|
styling=IndicatorStyling(color="#9c27b0", line_width=2)
|
|
)
|
|
|
|
DEFAULT_TEMPLATES = {
|
|
# ...
|
|
"stochastic": create_stochastic_template,
|
|
}
|
|
```
|
|
|
|
### ✅ Step 11: Add Calculation Function (Optional)
|
|
|
|
**File**: `data/common/indicators.py`
|
|
|
|
Add a standalone calculation function.
|
|
|
|
```python
|
|
def calculate_stochastic(df: pd.DataFrame, k_period: int = 14, d_period: int = 3) -> tuple:
|
|
lowest_low = df['low'].rolling(window=k_period).min()
|
|
highest_high = df['high'].rolling(window=k_period).max()
|
|
k_percent = 100 * ((df['close'] - lowest_low) / (highest_high - lowest_low))
|
|
d_percent = k_percent.rolling(window=d_period).mean()
|
|
return k_percent, d_percent
|
|
```
|
|
|
|
## File Change Summary
|
|
|
|
When adding a new indicator, you'll typically modify these files:
|
|
1. **`components/charts/layers/indicators.py`** or **`subplots.py`**
|
|
2. **`components/charts/layers/__init__.py`**
|
|
3. **`dashboard/components/indicator_modal.py`**
|
|
4. **`dashboard/callbacks/indicators.py`**
|
|
5. **`components/charts/indicator_defaults.py`**
|
|
6. **`data/common/indicators.py`** (optional) |