Intermediate ~30 min ShellPython Build guide

How to Build a Trading Bot

Build a complete automated trading system using Python and the TickAtlas API. This guide covers strategy design, signal generation, risk management, and deployment.

5 sections 6 copy-paste code samples Shell · Python
5Guide sections
6Code samples
IntermediateLevel
30 minTime

What you need before you start.

Python 3.8+ installed
TickAtlas API key (get one free)
Basic understanding of technical indicators
A broker account for live trading (optional for backtesting)
Scope: TickAtlas supplies market data, indicators and analytics. It does not place orders or hold funds — any broker or execution layer stays inside your own application.

Project Setup

Shell Project Setup
pip install requests schedule python-dotenv

Create a .env file:

Shell Project Setup
TICKATLAS_API_KEY=your_api_key_here

Create the API Client

Python Create the API Client
import os
import requests
from dotenv import load_dotenv

load_dotenv()

class TickAtlasClient:
    def __init__(self):
        self.base_url = "https://tickatlas.com/v1"
        self.headers = {"X-API-Key": os.getenv("TICKATLAS_API_KEY")}

    def get_indicator(self, symbol, indicator, timeframe):
        """Get a single indicator value."""
        params = {"symbol": symbol, "indicator": indicator, "timeframe": timeframe}
        response = requests.get(
            f"{self.base_url}/indicator",
            headers=self.headers,
            params=params
        )
        response.raise_for_status()
        return response.json()

    def get_summary(self, symbol, timeframe):
        """Get the rule-based market analysis."""
        params = {"symbol": symbol, "timeframe": timeframe}
        response = requests.get(
            f"{self.base_url}/summary",
            headers=self.headers,
            params=params
        )
        response.raise_for_status()
        return response.json()

    def get_multi(self, symbols, indicators, timeframe):
        """Get multiple indicators for multiple symbols."""
        params = {
            "symbols": ",".join(symbols),
            "indicators": ",".join(indicators),
            "timeframe": timeframe
        }
        response = requests.get(
            f"{self.base_url}/multi",
            headers=self.headers,
            params=params
        )
        response.raise_for_status()
        return response.json()

Define Your Strategy

We'll implement a simple RSI + MACD confluence strategy:

Buy signal
RSI < 35 AND MACD histogram turning positive
Sell signal
RSI > 65 AND MACD histogram turning negative
Risk management
2× ATR stop loss, 3× ATR take profit
Python Define Your Strategy
class TradingStrategy:
    def __init__(self, client):
        self.client = client

    def analyze(self, symbol, timeframe="H1"):
        """Analyze a symbol and return a trading signal."""
        rsi = self.client.get_indicator(symbol, "RSI_14", timeframe)
        macd = self.client.get_indicator(symbol, "MACD_hist", timeframe)
        atr = self.client.get_indicator(symbol, "ATR_14", timeframe)

        # Every success response is {"success": true, "data": {...}}.
        rsi_value = rsi["data"]["value"]
        macd_value = macd["data"]["value"]
        atr_value = atr["data"]["value"]

        signal = "HOLD"

        if rsi_value < 35 and macd_value > 0:
            signal = "BUY"
        elif rsi_value > 65 and macd_value < 0:
            signal = "SELL"

        return {
            "symbol": symbol,
            "signal": signal,
            "rsi": rsi_value,
            "macd_hist": macd_value,
            "atr": atr_value,
            "stop_loss": atr_value * 2,
            "take_profit": atr_value * 3,
        }

Create the Bot Loop

Python Create the Bot Loop
import schedule
import time
import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(message)s")

def run_bot():
    client = TickAtlasClient()
    strategy = TradingStrategy(client)

    symbols = ["EURUSD", "GBPUSD", "USDJPY", "XAUUSD"]

    for symbol in symbols:
        try:
            result = strategy.analyze(symbol)

            if result["signal"] != "HOLD":
                logging.info(
                    f"📊 {result['signal']} {symbol} | "
                    f"RSI: {result['rsi']:.1f} | "
                    f"MACD: {result['macd_hist']:.5f} | "
                    f"SL: {result['stop_loss']:.5f} | "
                    f"TP: {result['take_profit']:.5f}"
                )
                # Here you would execute the trade via your broker API
            else:
                logging.debug(f"No signal for {symbol}")

        except Exception as e:
            logging.error(f"Error analyzing {symbol}: {e}")

# Run every hour at the top of the hour
schedule.every().hour.at(":01").do(run_bot)

logging.info("🤖 Trading bot started. Checking every hour...")
run_bot()  # Run immediately on start

while True:
    schedule.run_pending()
    time.sleep(30)

Confirm with the Market Summary

Use the /v1/summary endpoint for a rule-based confirmation:

Python Confirm with the Market Summary
def analyze_with_summary(self, symbol, timeframe="H1"):
    """Enhanced analysis using the rule-based market summary."""
    basic = self.analyze(symbol, timeframe)

    if basic["signal"] != "HOLD":
        summary = self.client.get_summary(symbol, timeframe)["data"]
        bias = summary["bias"]            # "bullish", "bearish" or "neutral"
        confidence = summary.get("confidence", 0)   # 0-1 fraction, e.g. 0.78

        # Only trade when the summary's bias agrees with your own signal
        if basic["signal"] == "BUY" and bias == "bullish" and confidence > 0.6:
            basic["confirmed"] = True
            basic["summary_confidence"] = confidence
        elif basic["signal"] == "SELL" and bias == "bearish" and confidence > 0.6:
            basic["confirmed"] = True
            basic["summary_confidence"] = confidence
        else:
            basic["confirmed"] = False
            basic["signal"] = "HOLD"  # Override - the summary disagrees

    return basic

Production hardening

The code above is the happy path. These are the concerns that decide whether it survives contact with a real deployment.

Keep the key server-side. The API key authenticates with the X-API-Key header and must never reach a browser bundle. Proxy it, or use a public widget key, which is domain-scoped and revocable. Authentication
Handle 429 before you need to. Rate limits are per key and per minute. Back off on 429 rather than retrying immediately, and read the X-RateLimit-* headers on every response. Rate limits
Branch on the error code, not the message. Errors carry a stable machine-readable code; the human-readable text can change. Codes were unified in v3.15. Error handling
Expect gaps, and do not invent values. Markets close, feeds stall, and a retention window can reject a request outright. Surface an explicit unavailable state rather than substituting a zero or the last known price. Troubleshooting
Cache what you poll. Responses are already cached briefly upstream, so polling faster than the data changes spends credits without improving freshness. Cache on your side and poll on the cadence your timeframe actually updates. Pricing and credits
Watch retention per timeframe. History depth is set per timeframe, never per plan, so a request that works on D1 can fall outside the window on M1. Check the published windows before backfilling. Timeframes
Rotate keys and scope them. Issue a separate key per deployment so one can be revoked without taking the others down, and rotate on a schedule rather than after an incident. Authentication
Log the request, not the key. Record endpoint, parameters, status and latency so a failure is reproducible. Never log the key itself, and scrub it from error reports.

Next Steps

Everything this guide touches, linked directly — so it never dead-ends.

Build against live market data

Start with the data layer already solved.

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