Tutorial 10 min read

Parabolic SAR: Trailing Stop-Loss Automation

How to implement automated trailing stop losses using Parabolic SAR data from the TickAtlas API, with Python code and backtesting considerations.

7Sections
5Code samples
10Min read
TutorialCategory

What Is Parabolic SAR?

Parabolic SAR (Stop and Reverse), developed by J. Welles Wilder, is a trend-following indicator that provides potential entry and exit points. On a chart, it appears as dots above or below price: dots below price indicate an uptrend, dots above indicate a downtrend.

What makes it especially useful for automation is that it gives you a specific price level at each bar -- not a vague "overbought" signal, but an exact stop-loss value that tightens over time as the trend progresses.

Dots Below Price

Uptrend active. The SAR value is your trailing stop for a long position. It only moves up, never down.

Dots Above Price

Downtrend active. The SAR value is your trailing stop for a short position. It only moves down, never up.

How SAR Is Calculated

The SAR uses an acceleration factor (AF) that starts at 0.02 and increases by 0.02 each time a new extreme point is reached, up to a maximum of 0.20. This creates the "parabolic" curve that accelerates toward price as the trend matures.

Shell How SAR Is Calculated
SAR(next) = SAR(current) + AF * (EP - SAR(current))

Where:
  AF = Acceleration Factor (starts 0.02, max 0.20)
  EP = Extreme Point (highest high in uptrend, lowest low in downtrend)

When price crosses SAR, the trend reverses:
  - New SAR = most extreme EP from the previous trend
  - AF resets to 0.02

Fetching Parabolic SAR from the API

Shell Fetching Parabolic SAR from the API
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://tickatlas.com/v1/indicator?symbol=GBPUSD&indicator=SAR&timeframe=H1"

Response:

JSON Fetching Parabolic SAR from the API
{
  "success": true,
  "data": {
    "symbol": "GBPUSD",
    "timeframe": "H1",
    "indicator": "SAR",
    "value": 1.2934,
    "bid": 1.2957,
    "ask": 1.2958,
    "updated_at": 1787229614,
    "server_time": "2026-08-20T12:40:14+00:00"
  }
}

The SAR is at 1.2934, below the close of 1.2957. Signal is "bullish" -- dots are below price. This SAR value is your trailing stop if you are long GBPUSD.

Python: Automated SAR Trailing Stop

Python Python: Automated SAR Trailing Stop
import requests
import time
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("sar_trailing")

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://tickatlas.com/v1"

class SARTrailingStop:
    """Manage a trailing stop using Parabolic SAR."""

    def __init__(self, symbol: str, timeframe: str, direction: str):
        self.symbol = symbol
        self.timeframe = timeframe
        self.direction = direction
        self.current_stop = None
        self.is_active = True

    def fetch_sar(self) -> dict:
        resp = requests.get(
            f"{BASE_URL}/indicator",
            headers={"X-API-Key": API_KEY},
            params={
                "symbol": self.symbol,
                "indicator": "SAR",
                "timeframe": self.timeframe,
            },
        )
        resp.raise_for_status()
        return resp.json()["data"]

    def update(self) -> dict:
        """Check SAR and update trailing stop."""
        data = self.fetch_sar()
        sar = data["value"]
        close = data["bid"]  # current bid as the reference price
        signal = "bullish" if close > sar else "bearish"  # SAR below price = uptrend

        result = {
            "sar": sar,
            "close": close,
            "signal": signal,
            "stop_hit": False,
            "stop_level": self.current_stop,
        }

        if self.direction == "long":
            # SAR flipped above price = exit signal
            if signal == "bearish":
                result["stop_hit"] = True
                self.is_active = False
                logger.info(f"SAR flipped bearish. Exit long at {close}")
                return result

            # Update trailing stop (only moves up)
            if self.current_stop is None or sar > self.current_stop:
                self.current_stop = sar
                result["stop_level"] = sar
                logger.info(f"Trailing stop updated to {sar}")

        elif self.direction == "short":
            if signal == "bullish":
                result["stop_hit"] = True
                self.is_active = False
                logger.info(f"SAR flipped bullish. Exit short at {close}")
                return result

            if self.current_stop is None or sar < self.current_stop:
                self.current_stop = sar
                result["stop_level"] = sar
                logger.info(f"Trailing stop updated to {sar}")

        return result

# Usage
trailing = SARTrailingStop("GBPUSD", "H1", "long")

# In a real bot, this runs on a timer matching the timeframe
while trailing.is_active:
    status = trailing.update()
    print(f"Close: {status['close']}, Stop: {status['stop_level']}")
    if status["stop_hit"]:
        print("Position closed by SAR reversal.")
        break
    time.sleep(3600)  # Check every hour for H1

SAR + ATR Hybrid Approach

A common improvement is to use SAR for the trailing mechanism but set the initial stop with ATR. This prevents the early SAR values (which start very close to price) from triggering a premature exit:

Python SAR + ATR Hybrid Approach
def hybrid_stop(symbol: str, timeframe: str, direction: str) -> dict:
    """Use ATR for initial stop, SAR for trailing."""
    # Fetch both indicators
    sar_data = requests.get(
        f"{BASE_URL}/indicator",
        headers={"X-API-Key": API_KEY},
        params={"symbol": symbol, "indicator": "SAR", "timeframe": timeframe},
    ).json()["data"]

    atr_data = requests.get(
        f"{BASE_URL}/indicator",
        headers={"X-API-Key": API_KEY},
        params={"symbol": symbol, "indicator": "ATR_14", "timeframe": timeframe},
    ).json()["data"]

    close = sar_data["bid"]  # current bid as the reference price
    sar = sar_data["value"]
    atr = atr_data["value"]

    if direction == "long":
        atr_stop = close - (atr * 2.0)
        # Use whichever is further from price (more conservative)
        initial_stop = min(sar, atr_stop)
    else:
        atr_stop = close + (atr * 2.0)
        initial_stop = max(sar, atr_stop)

    return {
        "sar_stop": sar,
        "atr_stop": round(atr_stop, 5),
        "selected_stop": round(initial_stop, 5),
        "method": "ATR" if initial_stop != sar else "SAR",
    }

When SAR Works Best (and Worst)

Best: strong trending markets

SAR excels in sustained trends where the parabolic curve locks in profits as the trend accelerates. XAUUSD trend days are ideal.

Worst: ranging/choppy markets

In sideways markets, SAR flips frequently, generating costly whipsaw entries and exits. Always filter with a trend indicator like ADX.

Tip: Use ADX > 25 as a trend filter

Only use SAR for trailing stops when ADX confirms a strong trend. In low-ADX environments, switch to a fixed ATR stop instead.

Related Reading

Run this against live data.

Every account starts pay-as-you-go with $2.50 of credit and no card. Paste the key into the samples above and the requests work unchanged.

Prefer the API to the article

Build it instead of reading about it.

Create a key, run the first request, then extend one layer at a time. Every account starts pay-as-you-go with $2.50 of credit — no card required.