TickAtlas
Tutorial 13 min read · March 28, 2026

Building a Multi-Timeframe Trading Bot

Design and implement a trading bot that analyzes multiple timeframes simultaneously. Use higher timeframes for trend and lower timeframes for entry timing.

CG
By the TickAtlas team

The Multi-Timeframe Edge

Single-timeframe strategies have a fundamental weakness: they cannot distinguish between a pullback in a trend and a full reversal. A multi-timeframe approach solves this by using a higher timeframe for trend direction and a lower timeframe for entry timing.

D1

Trend direction

H4

Signal confirmation

H1

Entry timing

The Three-Timeframe Framework

python
import requests
from dataclasses import dataclass
from typing import Optional

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

@dataclass
class TimeframeAnalysis:
    timeframe: str
    trend: str         # "bullish", "bearish", "neutral"
    rsi: float
    macd_signal: str
    ema_trend: str     # price vs EMA relationship

@dataclass
class MultiTFSignal:
    symbol: str
    trend_tf: TimeframeAnalysis
    signal_tf: TimeframeAnalysis
    entry_tf: TimeframeAnalysis
    trade_direction: Optional[str]
    confidence: str

def analyze_timeframe(symbol: str, timeframe: str) -> TimeframeAnalysis:
    """Analyze a single timeframe."""
    headers = {"X-API-Key": API_KEY}

    # Fetch RSI
    rsi_resp = requests.get(f"{BASE_URL}/indicator", headers=headers,
        params={"symbol": symbol, "indicator": "RSI_14", "timeframe": timeframe})
    rsi_data = rsi_resp.json()["data"]

    # Fetch MACD
    macd_resp = requests.get(f"{BASE_URL}/indicator", headers=headers,
        params={"symbol": symbol, "indicator": "MACD", "timeframe": timeframe})
    macd_data = macd_resp.json()["data"]

    # Fetch EMA 50
    ema_resp = requests.get(f"{BASE_URL}/indicator", headers=headers,
        params={"symbol": symbol, "indicator": "EMA_50", "timeframe": timeframe})
    ema_data = ema_resp.json()["data"]

    close = rsi_data["ohlcv"]["close"]
    rsi = rsi_data["values"]["rsi"]
    macd_hist = macd_data["values"]["histogram"]
    ema = ema_data["values"]["value"]

    ema_trend = "above" if close > ema else "below"
    trend = "bullish" if ema_trend == "above" and macd_hist > 0 else \
            "bearish" if ema_trend == "below" and macd_hist < 0 else "neutral"

    return TimeframeAnalysis(
        timeframe=timeframe, trend=trend, rsi=rsi,
        macd_signal=macd_data.get("signal", "neutral"), ema_trend=ema_trend,
    )

The Decision Engine

python
def multi_tf_analysis(symbol: str) -> MultiTFSignal:
    """Run the three-timeframe analysis."""
    trend = analyze_timeframe(symbol, "D1")
    signal = analyze_timeframe(symbol, "H4")
    entry = analyze_timeframe(symbol, "H1")

    direction = None
    confidence = "low"

    # Rule 1: All three timeframes must agree on direction
    if trend.trend == "bullish" and signal.trend == "bullish":
        if entry.rsi < 40:  # Entry TF showing a pullback
            direction = "long"
            confidence = "high"
        elif entry.rsi < 55:
            direction = "long"
            confidence = "medium"

    elif trend.trend == "bearish" and signal.trend == "bearish":
        if entry.rsi > 60:  # Entry TF showing a pullback
            direction = "short"
            confidence = "high"
        elif entry.rsi > 45:
            direction = "short"
            confidence = "medium"

    return MultiTFSignal(
        symbol=symbol, trend_tf=trend, signal_tf=signal,
        entry_tf=entry, trade_direction=direction, confidence=confidence,
    )

# Scan the watchlist
for symbol in ["EURUSD", "GBPUSD", "USDJPY", "XAUUSD"]:
    result = multi_tf_analysis(symbol)
    if result.trade_direction:
        print(f"{symbol}: {result.trade_direction} ({result.confidence})")
        print(f"  D1: {result.trend_tf.trend}, H4: {result.signal_tf.trend}")
        print(f"  H1 RSI: {result.entry_tf.rsi:.1f}")

Adding Risk Management

python
def calculate_trade(signal: MultiTFSignal) -> Optional[dict]:
    """Calculate entry, stop, and target using ATR."""
    if not signal.trade_direction:
        return None

    # Get ATR from the signal timeframe (H4)
    atr_resp = requests.get(f"{BASE_URL}/indicator",
        headers={"X-API-Key": API_KEY},
        params={"symbol": signal.symbol, "indicator": "ATR_14", "timeframe": "H4"})
    atr = atr_resp.json()["data"]["values"]["atr"]

    # Get current price from entry timeframe
    price_resp = requests.get(f"{BASE_URL}/indicator",
        headers={"X-API-Key": API_KEY},
        params={"symbol": signal.symbol, "indicator": "RSI_14", "timeframe": "H1"})
    close = price_resp.json()["data"]["ohlcv"]["close"]

    multiplier = 1.5 if signal.confidence == "high" else 2.0
    rr_ratio = 2.0 if signal.confidence == "high" else 1.5

    if signal.trade_direction == "long":
        stop = close - (atr * multiplier)
        target = close + (atr * multiplier * rr_ratio)
    else:
        stop = close + (atr * multiplier)
        target = close - (atr * multiplier * rr_ratio)

    return {
        "symbol": signal.symbol,
        "direction": signal.trade_direction,
        "entry": round(close, 5),
        "stop_loss": round(stop, 5),
        "take_profit": round(target, 5),
        "confidence": signal.confidence,
    }

Key Principles

Trade in the direction of the highest timeframe

If D1 is bullish, only look for longs on H4 and H1. Fighting the major trend is the fastest way to blow up.

Use pullbacks on the entry timeframe

The best entries come when the entry timeframe shows a temporary pullback against the higher-timeframe trend. This gives you a better entry price and tighter stop.

Timeframe ratio of 4-6x between levels

D1/H4/H1 is a 6x and 4x ratio. This gives enough separation that each level provides unique information without too much overlap.

Related Reading

Try this with live data

Every account gets $2.50 in free PAYG credits. No card required — paste your API key and run the code above against live broker data.