Back to Blog

How I Built an Autonomous Trading Bot That Runs 24/7

I used to spend hours watching charts. Refreshing price feeds, waiting for setups, second-guessing entries because I stepped away for coffee. It's exhausting — and it doesn't scale. So I stopped watching and built something that watches for me.

This is a technical walkthrough of the autonomous trading system I built and run in production. It scans markets continuously, evaluates signals against a defined strategy, executes trades, and sends me a summary every morning. I don't babysit it. Here's how it works.

The Problem With Manual Trading

Manual trading has three fundamental bottlenecks: attention, emotion, and time zones. You can only watch so many instruments at once. You'll hesitate on a valid signal because your last trade was a loss. And markets don't care that it's 3 AM where you live.

Automation solves all three — but only if the system is built correctly. A bot that crashes, misses fills, or silently fails is worse than no bot at all. The engineering challenge isn't the strategy logic. It's making the whole thing reliable enough to trust with real money.

The Architecture

The system has four distinct layers. Each one has a single job and communicates with the others through well-defined interfaces. This separation makes debugging tractable and lets me swap out any layer without touching the others.

Market Scannerpolls price data
Strategy Engineevaluates signals
Execution Layerplaces & manages orders
Monitoring Dashboardlogging & alerts

Layer 1: Market Scanner

The scanner runs on a cron schedule — every 5 minutes during market hours — and pulls OHLCV data from the broker's REST API. It normalizes the response into a consistent internal format and writes it to a lightweight SQLite database. That database is the single source of truth for every downstream layer.

The key decision here was polling over streaming. WebSocket feeds are tempting, but they require persistent connections and complex reconnect logic. For a strategy that operates on 5-minute candles, polling is simpler and more than fast enough. Save complexity for where it matters.

scanner.py — data fetch loop
def fetch_candles(symbol, interval="5m", limit=200):
    resp = broker_client.get_klines(
        symbol=symbol,
        interval=interval,
        limit=limit
    )
    return normalize_ohlcv(resp)   # → [{open, high, low, close, vol, ts}]

def run_scan():
    for symbol in WATCHLIST:
        candles = fetch_candles(symbol)
        db.upsert_candles(symbol, candles)
        # scanner doesn't make decisions — just writes data

Layer 2: Strategy Engine

After each scan cycle, the strategy engine reads the freshest candles and runs them through the signal logic. In my case this is a trend-following system: a combination of EMA crossover, RSI filter, and ATR-based volatility gate. The engine outputs a signal object — BUY, SELL, or HOLD — with an attached confidence score and position sizing recommendation.

The strategy layer is pure functions — no side effects, no database writes, no API calls. This makes it trivially testable. I can replay any historical period and verify the signal output before running it live. That feedback loop is what makes strategy iteration fast.

Layer 3: Execution Layer

The executor receives a signal and translates it into broker API calls. It handles order placement, partial fills, stop-loss management, and position tracking. This is the layer that actually touches money, so it has the most defensive code.

Every order goes through a pre-flight check: account balance, existing positions, open orders, and a hard daily loss limit. If any check fails, the signal is rejected and logged. The system will miss trades before it'll take one that violates risk parameters.

executor.py — pre-flight checks
def pre_flight(signal, account):
    checks = [
        account.balance >= MIN_TRADE_BALANCE,
        account.daily_pnl > -MAX_DAILY_LOSS,
        not has_open_position(signal.symbol),
        is_market_open(),
    ]
    if not all(checks):
        log.warning(f"Signal rejected: {signal.symbol} — pre-flight failed")
        return False
    return True

Layer 4: Monitoring Dashboard

Every significant event — scan completion, signal generated, order placed, order filled, error caught — writes a structured log entry to a central log file. A lightweight FastAPI dashboard reads those logs and surfaces them as a simple web UI. I check it once in the morning.

The dashboard also runs a daily health check at midnight: it verifies the scanner is alive, the broker API is reachable, and the account balance is above the minimum threshold. If anything fails, it sends me a Telegram message. That's the only time I hear from the bot unprompted.

What Makes It Actually Work in Production

The architecture above is table stakes. Here's what actually separates a bot that runs reliably from one that quietly explodes at 2 AM:

  • Idempotent order logic. Every execution step checks state before acting. If the bot restarts mid-trade, it reconciles with the broker before doing anything new.
  • Structured logging everywhere. Not print statements — structured JSON logs with timestamps, symbol, signal type, and order IDs. When something goes wrong, the timeline is immediately reconstructable.
  • Graceful error handling, not silent failures. Every API call is wrapped in a try/except with explicit retry logic and exponential backoff. Errors are logged with full context, never swallowed.
  • A kill switch. A single environment variable — TRADING_ENABLED=false — halts all order execution without stopping the scanner or logging. I can pause trading without losing visibility.
scheduler setup (APScheduler)
from apscheduler.schedulers.blocking import BlockingScheduler

scheduler = BlockingScheduler()

# scan every 5 minutes during market hours
scheduler.add_job(run_scan, 'cron',
    day_of_week='mon-fri',
    hour='9-16',
    minute='*/5',
    misfire_grace_time=30
)

# daily health check at midnight
scheduler.add_job(health_check, 'cron', hour=0, minute=0)

scheduler.start()  # runs until killed — deployed on a VPS

The Result

The system has been running for several months. It averages roughly 3–5 trades per week across the instruments I watch. I review the morning summary, occasionally tweak strategy parameters, and otherwise leave it alone. The biggest ROI isn't the trading returns — it's the hours I stopped spending staring at screens.

The hardest part of building this wasn't the code. It was building enough trust in the system to actually let it run. That trust comes from good logging, good testing, and conservative risk controls. Get those right first. The strategy logic is almost secondary.

Work with us

Want something like this built for your business?

Whether it's a trading system, a data pipeline, or an AI-powered workflow — I build autonomous systems that run while you focus on what matters.

Start a Project