# 02. Your first Python strategy

A guided walkthrough that ends with `ema_cross.py`: a long-and-short EMA crossover strategy, with tags so the analyzer can split breakout-style entries from pullback-style entries in the per-tag stats panel.

You will use [`@strategy`](https://quantchartsllc.com/docs/python/py-decorators.md#strategy), define `entry_long` / `exit_long` / `entry_short` / `exit_short` boolean arrays, and attach tags via [`define_tag`](https://quantchartsllc.com/docs/python/py-styling.md#define_tag) plus return-dict keys. Steps 1-5 build the file. Step 6 is the full strategy you can paste; step 7 walks the run cycle.

<a id="step-1-the-mental-model"></a>
## Step 1. The mental model

A Python OHLC strategy returns four boolean arrays the same length as the bar series. The Rust backtest engine reads them and simulates trades.

`entry_long[i] = True` means: enter long at bar i if no long is currently open. `exit_long[i] = True` closes any open long at bar i. The same for short. Anything else you return in the dict (any non-canonical key) is treated as a tag boolean array; tags label trades in the analyzer for slicing PnL by setup or regime.

### Notes

- Conflict resolution: if `entry_long[i]` and `entry_short[i]` are both True on the same bar, the engine takes neither (logged as a conflict).
- Microstructure: long entry fills at ask, long exit fills at bid (and short reversed). The engine handles this; you only emit signals.

<a id="step-2-imports-and-decorator"></a>
## Step 2. Imports and decorator

Pull in the strategy decorator, signal helpers, and the timeframe enum.

### Example

```python
import numpy as np
from quant_charts import (
    strategy, input, ta, cross_above, cross_below,
    define_tag, Timeframe,
)


@strategy(
    name="EMA Cross",
    description="Long+short EMA crossover with directional tags.",
    overlay=True,
    timeframe=Timeframe.M5,
    data_mode="ohlc",
    required_columns=["close"],
    uses_sltp=False,
)
class EMACross:
    pass
```

### Notes

- `timeframe=Timeframe.M5` runs the strategy on 5-minute bars regardless of the chart's viewing timeframe.
- `uses_sltp=False` skips the SL/TP bracket bundle. Set to `True` and emit `sl`/`tp` arrays only when you want stops/targets.
- `required_columns=["close"]` only ships the close column to the worker; cuts sweep memory.

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

Two integer params for the periods, two color params for tag display in the analyzer.

### Example

```python
class EMACross:
    fast_period = input.int(9, "Fast EMA", min=2, max=200)
    slow_period = input.int(21, "Slow EMA", min=2, max=400)
```

### Notes

- These show up as both panel inputs and analyzer sweep ranges. Set sensible `min`/`max` so the analyzer's grid mode does not generate nonsense.

<a id="step-4-compute-crossovers"></a>
## Step 4. Compute crossovers

[`cross_above(a, b)`](https://quantchartsllc.com/docs/python/py-signals.md#cross_above) returns a boolean array, True on the bar where `a` crosses up through `b` (False elsewhere). [`cross_below`](https://quantchartsllc.com/docs/python/py-signals.md#cross_below) is the reverse.

### Example

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

        bull_cross = cross_above(fast, slow)
        bear_cross = cross_below(fast, slow)
```

### Notes

- `cross_above` / `cross_below` only fire on the exact bar of the cross. They do NOT stay True after.
- For "fast above slow" (a continuous condition, not a single-bar cross), use [`above(fast, slow)`](https://quantchartsllc.com/docs/python/py-signals.md#above).

<a id="step-5-tags-and-return-dict"></a>
## Step 5. Tags and return dict

Declare two tags for analyzer styling, then return the full signal/tag dict.

### Example

```python
        define_tag("bull_setup", "Long entry on bull cross", color="#22c55e")
        define_tag("bear_setup", "Short entry on bear cross", color="#ef4444")

        return {
            "entry_long":  bull_cross,
            "exit_long":   bear_cross,
            "entry_short": bear_cross,
            "exit_short":  bull_cross,
            # any non-canonical key is auto-classified as a tag bool array
            "bull_setup":  bull_cross,
            "bear_setup":  bear_cross,
        }
```

### Notes

- `define_tag(name, label, color)` is metadata for the UI. The actual tag boolean comes from the return dict key.
- Tag arrays must be the same length as the signal arrays.
- In the analyzer, click "Bull Setup" or "Bear Setup" in the per-tag stats panel to see PnL filtered to just those entries.

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

Save this as `ema_cross.py` under `your workspace/strategies/`.

### Example

```python
import numpy as np
from quant_charts import (
    strategy, input, ta, cross_above, cross_below,
    define_tag, Timeframe,
)


@strategy(
    name="EMA Cross",
    description="Long+short EMA crossover with directional tags.",
    overlay=True,
    timeframe=Timeframe.M5,
    data_mode="ohlc",
    required_columns=["close"],
    uses_sltp=False,
)
class EMACross:
    fast_period = input.int(9, "Fast EMA", min=2, max=200)
    slow_period = input.int(21, "Slow EMA", min=2, max=400)

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

        bull_cross = cross_above(fast, slow)
        bear_cross = cross_below(fast, slow)

        define_tag("bull_setup", "Long entry on bull cross", color="#22c55e")
        define_tag("bear_setup", "Short entry on bear cross", color="#ef4444")

        return {
            "entry_long":  bull_cross,
            "exit_long":   bear_cross,
            "entry_short": bear_cross,
            "exit_short":  bull_cross,
            "bull_setup":  bull_cross,
            "bear_setup":  bear_cross,
        }
```

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

Attach the strategy to a chart and inspect trades.

2. From the strategy top-bar pill, pick "EMA Cross". The chart re-runs immediately and renders trade brackets (entry, SL/TP if any, exit).
3. Hover a trade marker for entry/exit timestamps and PnL.
4. Open the Analyzer tab, choose Strategy mode, click Run; the per-tag panel will show separate stats for `bull_setup` vs `bear_setup`.

### Notes

- If trades do not appear, the strategy panel "Status" line shows the most recent error or zero-signal warning.
- For sweeps, set `min`/`max` on the params and pick a step size in the Analyzer's parameter form.
