Tutorial 13 min read

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.

6Sections
3Code samples
13Min read
TutorialCategory

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 The Three-Timeframe Framework
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 histogram
    macd_resp = requests.get(f"{BASE_URL}/indicator", headers=headers,
        params={"symbol": symbol, "indicator": "MACD_hist", "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["bid"]  # current bid as the reference price
    rsi = rsi_data["value"]
    macd_hist = macd_data["value"]
    ema = ema_data["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="bullish" if macd_hist > 0 else "bearish", ema_trend=ema_trend,
    )

The Decision Engine

Python The Decision Engine
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 Adding Risk Management
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"]["value"]

    # 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

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.