# 01. Your first Python indicator

A guided 6-step walkthrough that ends with a working `dual_sma.py` indicator: two moving averages plus a fill that turns green when fast is above slow and red when it crosses below.

You will use [`@indicator`](https://quantchartsllc.com/docs/python/py-decorators.md#indicator), declare two [`input.int`](https://quantchartsllc.com/docs/python/py-inputs.md#input-int) parameters and two [`input.color`](https://quantchartsllc.com/docs/python/py-inputs.md#input-color) parameters, compute averages with [`ta.sma`](https://quantchartsllc.com/docs/python/py-ta.md#ta-sma), and call [`plot()`](https://quantchartsllc.com/docs/python/py-plotting.md#plot) and [`fill()`](https://quantchartsllc.com/docs/python/py-plotting.md#fill). By the end you will know how to: declare a Python indicator, expose user-tunable parameters, read the OHLC dataframe, plot named series, and fill between them. Read each step in order; the final step has the complete file you can paste into `indicators/`.

<a id="step-1-imports"></a>
## Step 1. Imports

Pull only what you need from `quant_charts`. Each imported name is documented in the Python tab.

### Example

```python
import numpy as np
from quant_charts import indicator, input, ta, plot, fill
```

### Notes

- `ta` is the vectorized technical-analysis namespace (`ta.sma`, [`ta.ema`](https://quantchartsllc.com/docs/python/py-ta.md#ta-ema), [`ta.rsi`](https://quantchartsllc.com/docs/python/py-ta.md#ta-rsi), [`ta.atr`](https://quantchartsllc.com/docs/python/py-ta.md#ta-atr), ...).
- Importing `plot` and `fill` here lets you call them as plain functions inside `calculate()`.

<a id="step-2-the-decorator"></a>
## Step 2. The decorator

Mark the class as an indicator. The decorator registers it with the engine and exposes its parameters in the chart settings panel.

### Example

```python
@indicator(
    name="Dual SMA",
    description="Fast/slow SMA with cross-direction fill.",
    overlay=True,
    data_mode="ohlc",
    required_columns=["close"],
)
class DualSMA:
    pass
```

### Notes

- `overlay=True` draws on the price pane. Set to `False` for a separate subpane (e.g. RSI, MACD).
- `data_mode="ohlc"` runs the indicator on aggregated bars. Use `"both"` if it should also work in tick-by-tick TBBO view.
- `required_columns` makes the loader ship only the columns you read across the worker boundary. Keeps sweep memory tight.

<a id="step-3-parameters"></a>
## Step 3. Parameters

Declare class-level `input.*` fields. They become editable inputs in the indicator panel and the analyzer parameter form.

### Example

```python
class DualSMA:
    fast_period = input.int(20, "Fast Period", min=2, max=500)
    slow_period = input.int(50, "Slow Period", min=2, max=500)
    fast_color = input.color("#7aa2f7", "Fast Color")
    slow_color = input.color("#e0af68", "Slow Color")
    bull_fill = input.color("#22c55e40", "Bull Fill")
    bear_fill = input.color("#ef444440", "Bear Fill")
```

### Notes

- `input.int(default, label, min=, max=, step=)` exposes an integer field. [`input.float`](https://quantchartsllc.com/docs/python/py-inputs.md#input-float) is the same shape for floats.
- `input.color(default_hex, label)` accepts 6-char `#rrggbb` or 8-char `#rrggbbaa`. The trailing `40` here is hex alpha (40/255 ~= 25%).
- Read parameters as `self.fast_period`, `self.fast_color`, etc., inside `calculate()`.

<a id="step-4-the-calculate-body"></a>
## Step 4. The calculate body

Compute both averages and decide the fill color per bar.

### Example

```python
    def calculate(self, df):
        close = np.asarray(df["close"], dtype=np.float64)
        fast = ta.sma(close, self.fast_period)
        slow = ta.sma(close, self.slow_period)
        # element-wise: pick bull color where fast > slow, bear color otherwise
        return fast, slow
```

### Notes

- Convert pandas columns to numpy with `np.asarray(..., dtype=np.float64)`. The TA namespace expects numpy arrays.
- `ta.sma(close, period)` returns an array the same length as [`close`](https://quantchartsllc.com/docs/python/py-data.md#close), with NaN for the warmup bars before `period` samples are available.
- The return value of `calculate()` is ignored when you use `plot()`/`fill()` directly. Returning is optional for indicators (signal dicts are for strategies, not indicators).

<a id="step-5-plot-the-lines-and-fill-between"></a>
## Step 5. Plot the lines and fill between

Two `plot()` calls register named series on the chart. `fill()` references those names and shades the area between them.

### Example

```python
    def calculate(self, df):
        close = np.asarray(df["close"], dtype=np.float64)
        fast = ta.sma(close, self.fast_period)
        slow = ta.sma(close, self.slow_period)

        plot(fast, "Fast SMA", color=self.fast_color, linewidth=2)
        plot(slow, "Slow SMA", color=self.slow_color, linewidth=2)

        # fill takes the two plot names; color is hex with optional alpha
        fill("Fast SMA", "Slow SMA", color=self.bull_fill)
```

### Notes

- The `name` you pass to `plot()` is what `fill()` references. Names must be unique within an indicator.
- Single-color fill is the simplest path. To get a regime-switching fill (green when bull, red when bear), call `fill()` twice with two pairs of bookkeeping plots, or use [`bgcolor()`](https://quantchartsllc.com/docs/python/py-styling.md#bgcolor) on a per-bar basis. The single-color path is enough for this tutorial.

<a id="step-6-the-full-file"></a>
## Step 6. The full file

Save this as `dual_sma.py` under `your workspace/indicators/` and Quant Charts auto-loads it.

### Example

```python
import numpy as np
from quant_charts import indicator, input, ta, plot, fill


@indicator(
    name="Dual SMA",
    description="Fast/slow SMA with cross-direction fill.",
    overlay=True,
    data_mode="ohlc",
    required_columns=["close"],
)
class DualSMA:
    fast_period = input.int(20, "Fast Period", min=2, max=500)
    slow_period = input.int(50, "Slow Period", min=2, max=500)
    fast_color = input.color("#7aa2f7", "Fast Color")
    slow_color = input.color("#e0af68", "Slow Color")
    fill_color = input.color("#7aa2f733", "Fill")

    def calculate(self, df):
        close = np.asarray(df["close"], dtype=np.float64)
        fast = ta.sma(close, self.fast_period)
        slow = ta.sma(close, self.slow_period)

        plot(fast, "Fast SMA", color=self.fast_color, linewidth=2)
        plot(slow, "Slow SMA", color=self.slow_color, linewidth=2)
        fill("Fast SMA", "Slow SMA", color=self.fill_color)
```

<a id="step-7-run-it"></a>
## Step 7. Run it

Open the indicator in Quant Charts and attach it to a chart.

3. Open the Indicator panel from the chart top bar, click "Add", select "Dual SMA".
4. Tune `Fast Period` / `Slow Period` from the panel; the chart re-runs on every change.

### Notes

- Validation runs on save. If it fails, errors print to the in-app terminal panel.
- Adjusting an `input.color` from the panel respects the 8-char hex with alpha format.
