# Strategy Guide

Practical tips for building competitive models on Vanta Crunch.

## The Task

You're predicting **5-minute forward returns** for BTC and ETH using 1-minute
OHLCV candles. Predictions happen every 15 seconds.

This means:
- You're making very short-term forecasts (~40 predictions per hour)
- You see the last N minutes of price action before each prediction
- You need to decide: will the price go up or down in the next 5 minutes, and by how much?

## Common Approaches

### 1. Mean Reversion

**Idea:** Short-term price overextensions tend to revert to the mean.

**When it works:** Range-bound markets, high-frequency noise.

**Key indicators:** SMA deviation, Bollinger Bands, RSI extremes.

```python
from vanta.indicators import SMA

sma = SMA(period=10)
# ...
deviation = (price - sma.value) / sma.value
signal = -0.7 * deviation  # bet against the deviation
```

### 2. Trend Following / Momentum

**Idea:** Price tends to continue in the direction it's been moving.

**When it works:** Breakouts, news events, strong directional moves.

**Key indicators:** EMA crossover, MACD, price momentum.

```python
from vanta.indicators import EMA

fast = EMA(period=5)
slow = EMA(period=20)
# ...
spread = (fast.value - slow.value) / slow.value
signal = 0.5 * spread  # bet with the trend
```

### 3. Volatility Regime Switching

**Idea:** Different strategies work in different volatility environments.

**When it works:** Always — this is a meta-strategy.

**Key indicators:** ATR (for regime detection), then sub-strategies.

```python
from vanta.indicators import ATR, SMA, EMA

atr = ATR(period=14)
atr_avg = SMA(period=14)  # average volatility
# ...
if atr.value > atr_avg.value:
    # high vol → mean reversion
else:
    # low vol → momentum
```

### 4. Volume-Weighted Signals

**Idea:** Moves with high volume have more conviction than low-volume moves.

**When it works:** Volume spikes often precede continuation.

**Key indicators:** VWAP, volume SMA.

```python
from vanta.indicators import VWAP, SMA

vwap = VWAP()
vol_avg = SMA(period=20)
# ...
vol_ratio = current_volume / vol_avg.value
signal *= min(vol_ratio, 3.0) / 3.0  # scale signal by volume
```

### 5. Multi-Indicator Confluence

**Idea:** Only trade when multiple indicators agree.

**When it works:** Reduces false signals, improves hit rate.

```python
from vanta.indicators import RSI, BollingerBands, ATR

rsi = RSI(period=14)
bb = BollingerBands(period=20)
atr = ATR(period=14)
# ...
if rsi.value < 30 and bb.value.percent_b < 0.1 and atr.value > atr_avg:
    signal = 0.005  # strong oversold signal with high vol confirmation
```

## Available Indicators

All in `vanta.indicators`:

| Indicator | `.update()` args | `.value` type |
|-----------|-----------------|---------------|
| `SMA(period)` | `(price)` | `float` |
| `EMA(period)` | `(price)` | `float` |
| `RSI(period)` | `(price)` | `float` (0-100) |
| `MACD(fast, slow, signal)` | `(price)` | `MACDResult(macd, signal, histogram)` |
| `BollingerBands(period, num_std)` | `(price)` | `BollingerResult(upper, middle, lower, width, percent_b)` |
| `ATR(period)` | `(high, low, close)` | `float` |
| `VWAP()` | `(price, volume)` | `float` |
| `Returns(period)` | `(price)` | `float` |

All indicators have `.ready` (bool) and `.reset()`.

## Practical Tips

### Signal Magnitude

The scoring function is `expected_return × actual_return`. Your signal magnitude
directly scales your score. Consider:

- **Base signal**: ±0.001 to ±0.003 for weak signals
- **Strong signal**: ±0.005 to ±0.01 for high-conviction trades
- **Return 0.0** when you have no edge

A model that returns 0.0 most of the time but ±0.01 on strong setups can
outperform a model that always returns ±0.002.

### Avoiding Overfitting

1. **Split your data.** Train on Jan–Sep, validate on Oct–Dec.
2. **Use `sweep()`** to check if small parameter changes break your strategy.
   If changing lookback from 10 to 12 flips your PnL, you're overfit.
3. **Test on both symbols.** A strategy that only works on BTC is suspicious.
4. **Keep it simple.** Fewer parameters = less overfitting risk.

### Testing Your Strategy

```python
from vanta.backtest import BacktestRunner, compare, sweep

# Quick test
result = BacktestRunner(model=MyModel()).run(
    subject="BTCUSDT",
    start="2025-06-01",
    end="2026-01-01",
)
result.summary()
result.plot()

# Compare with baselines
compare([
    ("Baseline", MeanReversionTracker()),
    ("Mine", MyModel()),
], start="2025-06-01", end="2026-01-01")

# Sweep parameters
sweep(
    model_fn=lambda period: MyModel(lookback=period),
    params={"period": [5, 10, 20, 50]},
    start="2025-06-01", end="2026-01-01",
)
```

### Model Structure

```python
from vanta.tracker import TrackerBase

class MyModel(TrackerBase):
    def __init__(self):
        super().__init__()
        # Initialize indicators here

    def tick(self, data: dict):
        super().tick(data)  # always call super!
        for candle in data.get("candles_1m", []):
            # Update indicators with each candle
            pass

    def predict(self, symbol: str, horizon_seconds: int, step_seconds: int) -> dict:
        if not self.indicators_ready():
            return {"expected_return": 0.0}
        # Compute signal
        return {"expected_return": signal}
```

## Diversity Bonus

The leaderboard includes a **diversity score** — models that are uncorrelated
with other participants' models get a bonus. This means:

- Don't copy example strategies verbatim
- Unique signals are rewarded even if they're slightly less accurate
- Consider unconventional features or timeframes

## What Doesn't Work

- **Always returning the same value** — zero IC, no edge
- **Pure random** — 50% hit rate, zero expected return
- **Overly complex models** — more parameters = more overfitting
- **Ignoring magnitude** — direction alone leaves money on the table
- **Training on the entire dataset** — you'll overfit to the past
