Engineering 18 min read

How to Build Your Own Trading Bot in 2026 (Python, End-to-End)

A practical guide to wiring up market data, news, a backtest engine, IBKR paper trading, and a momentum data source into your own Python trading bot.

How to Build Your Own Trading Bot in 2026 (Python, End-to-End)
Engineering · Educational illustration · Not a real chart

Read this first

This is a practical, end-to-end guide to building your own trading bot in 2026. It is educational. It is not financial advice, it is not a copy-paste production template, and no part of it suggests that the bot you build will be profitable. Most retail trading bots — including the ones based on perfectly reasonable strategies — lose money. Paper trade for at least 30 days before any live capital, and only trade with money you can afford to lose.

The framing matters. BullAlert is a data tool — we surface positive-momentum small-caps, publish catch% and peak% per session, and stop there. The bot you build in this guide is a separate system that may consume BullAlert (or any of several alternatives) as one input and adds everything BullAlert does not: a strategy, a backtester, a paper-trading discipline, a broker connection, and a risk layer. We are going to walk through the whole stack.

The architecture: five layers

Every retail trading bot, regardless of strategy or asset class, decomposes into the same five layers. Building the bot is choosing a tool for each layer and wiring them together honestly.

  1. Data ingestion — bars, quotes, trades, news, fundamentals.
  2. Strategy / signals — the rules that turn data into "act now / don't act".
  3. Backtest — historical replay of the strategy against the data, with realistic fills and slippage.
  4. Execution / broker — the layer that places orders, paper or live.
  5. Risk + monitoring — position sizing, stops, drawdown circuit breakers, observability.

Most bots that fail do so because one layer was treated as an afterthought. The code below is a sketch of each layer; the linked deep-dives go further on each.

Layer 1 — Market data

Your bot needs three kinds of equity data: real-time bars (or trades and quotes), recent historical bars for indicators, and deep historical bars for the backtester. The providers that handle all three at retail price points are Polygon, Alpaca, and Finnhub; we covered the trade-offs in detail in best stock market data APIs (2026).

The short version:

  • For a US small-cap momentum bot, Alpaca's $99/mo Algo Trader Plus or Polygon's Advanced tier are the two reasonable choices. Both ship full SIP, which you need.
  • For a multi-asset bot, Polygon's breadth (options, futures, forex, crypto) wins.
  • Skip free tiers for live trading. The data quality gap is real.

Whichever you pick, write a thin abstraction over the API. Your strategy code should depend on a `MarketData` interface, not on Polygon's SDK directly. When you migrate providers — and you will, eventually — that abstraction is what saves the rewrite.

Layer 2 — News and catalysts

News is optional for some strategies (mean-reversion, technical-only setups) and essential for others (catalyst-driven momentum, earnings-day moves). If your strategy cares about catalysts, the providers worth comparing are Benzinga, Polygon News, Marketaux, and Tiingo News. The full breakdown is in best stock news APIs (2026).

The minimum useful catalyst pipeline:

  1. Subscribe to a news WebSocket from your provider.
  2. Extract tickers from each headline (the vendor's tagging plus your own NER for coverage on long-tail names).
  3. Classify each headline (earnings / FDA / contract / M&A / partnership / dilution / other).
  4. Score sentiment with a finance-tuned model — not a generic one.
  5. Gate signals on a maximum age (typically 5–60 minutes) so stale catalysts don't fire live trades.

Layer 3 — Strategy and signals

The strategy layer is where most retail bots either succeed or fail, and it has two distinct sub-decisions:

Build your own from raw data, or consume a curated signal source? The "build your own" path means writing the entire pipeline that turns bars and headlines into "should we act now". You own everything, including the bugs. The "consume a curated signal source" path means using a data tool — BullAlert is one such option, alongside Trade Ideas, Black Box Stocks, and a handful of others — that publishes a curated set of signals you then route into your own strategy decision.

Neither path is universally better. Building your own teaches you the most and gives you the most flexibility; consuming a curated source saves the months of pipeline work and lets you focus on the entry-rules / exit-rules / sizing problem. A common hybrid: use a curated signal as a candidate filter (what universe should I look at right now?) and your own strategy code as the actual decision (am I going to act on this candidate?).

BullAlert fits the latter slot specifically — it is the data layer you might point your bot at, not the bot itself. The engineering deep-dive covers what the data layer publishes; what you do with it inside your strategy is up to you.

Wiring BullAlert into an AI agent (REST or MCP)

BullAlert exposes the same primitives our own in-app chatbot uses. Two integration paths:

  • RESTGET https://api.bullalert.ai/v1/watchlist?session=current&limit=25 with an x-ba-api-key header. Mint the key on your dashboard. Supports session (pre_market / market / after_hours / all / current), limit (1–100), and as_of (UTC ISO8601 for historical replay). Full reference at /api.
  • MCP — drop one JSON block into Claude Desktop, Cursor, or any MCP-capable agent and the watchlist becomes a native tool. Same key, same data, zero glue code.

If you're building an AI-agent-style bot (LLM in the loop, not a pure rules engine), the persona is the moat. Because the agent only has one tool wired up (list_watchlist), you don't need a long refusal manifesto — the surface area is already small. A short starter prompt is plenty:

You help traders understand the BullAlert watchlist
(US small-cap momentum picks under $20, ranked live by a
proprietary algorithm and refreshed every minute).

Persona: <e.g. "warrior-style scalper — emphasize float,
pre-market high, fresh catalyst; bias toward higher-score names">

Cite specific rows by ticker + score when relevant.
Explain the data; never make buy / sell / hold calls.
Close every answer with the disclaimer your jurisdiction requires.

Fork it, swap the persona line, and bolt on whatever extra rules your strategy needs. Treat the prompt as the spec for your bot — not a template to copy verbatim.

Three persona-flavors that work well as starting points. Each is a starter prompt — fork it, sharpen the persona line for your style, and bolt on your own rules.

Momentum-style bot

Intraday continuation persona. Asks for the regular-hours watchlist, focuses on the strongest names by score, and frames every answer as study material rather than action.

You help an intraday momentum trader study the BullAlert watchlist.
Call list_watchlist(session="market", limit=10) when asked
"what's running right now" or "show me the top names today".

Persona: continuation-focused. Highlight names with the highest scores,
note when the top of the watchlist is stacked vs spread out, and
suggest study rather than buys.

Cite ticker + score on every name. Skip the rest unless asked.
Close every answer with the disclaimer your jurisdiction requires.

Warrior-style bot

Pre-market scalper persona. Pulls a wider pre-market list so the user can narrow down at the open, with explicit prompts to pull float / news / chart context from elsewhere.

You help a small-cap scalper prep for the open using the
BullAlert pre-market watchlist.
Call list_watchlist(session="pre_market", limit=25) when asked
"what's gapping" or "what's hot pre-market".

Persona: scalper. Surface the highest-score names, then explicitly
remind the user to pull float, news, and a chart from their own
sources before the open. Score is one input, not a setup.

Cite ticker + score per name. Never recommend a buy.
Close every answer with the disclaimer your jurisdiction requires.

Fundamental DD bot

Uses the watchlist as a candidate filter; the heavy lifting happens in tools outside BullAlert (SEC filings, dilution checks, sector analysis). Pairs our edge with your research stack.

You help a fundamental-DD-first investor turn the BullAlert
watchlist into a daily research queue.
Call list_watchlist(session="current", limit=25) to get today's
candidate set; the watchlist is the filter, not the answer.

Persona: deep DD. For each name surfaced, hand off to your other
tools (SEC filings, dilution history, sector context, news) and
synthesize. Score is a momentum gate — the thesis is yours to build.

Cite ticker + score, then call your DD tools.
Close every answer with the disclaimer your jurisdiction requires.

Layer 4 — Backtest

Before any live capital, the strategy has to backtest honestly. The choice of engine is covered in detail in best Python backtest engines (2026). Recommended for a serious 2026 project: NautilusTrader, because of the realistic fill modelling and the "same code in backtest and live" property. Acceptable alternatives: Backtrader (mature, EOL but stable) and VectorBT (vectorised speed, soft on fills).

The backtest discipline that matters more than the engine choice:

  • No look-ahead. Every feature your strategy uses must be computable from data available before the bar's close. This includes sneaky look-ahead like using session-end VWAP in a midday signal.
  • Walk-forward, not in-sample. Tune parameters on one window, evaluate on the next. Anything that only works in-sample is not real.
  • Realistic slippage. For small caps, assume 0.5%–2% slippage per side, depending on the average daily volume of the names in your universe. Vectorised backtests that assume mid-price fills will systematically lie.
  • Commission and tick math. Your broker's per-share or per-trade fees compound fast at retail size. Model them.

Layer 5 — Broker / execution

For US equities, the realistic broker choices for retail bots are Interactive Brokers (IBKR) and Alpaca. Both support paper accounts; both have Python integrations.

IBKR Gateway setup (IB Gateway / TWS)

IBKR is the heavyweight. Multi-asset, global, deep liquidity, professional execution quality — and the most painful onboarding of any retail broker by a wide margin. The short version:

  1. Open a paper account at IBKR (no funding required for paper). The DUQ-prefixed paper account number arrives by email.
  2. Install IB Gateway (or TWS — either will host the API). Java is required; install it first.
  3. Configure IB Gateway: enable API, set the socket port (default 4002 for paper, 4001 for live), allow ActiveX and Socket clients, set Trusted IPs to localhost.
  4. Choose a Python client. The mature options in 2026 are:
    • ib_async — the actively maintained successor to ib_insync. Async/await, decent type hints, sensible defaults.
    • ib_insync — the original. Still works, no longer actively maintained.
    • IBKR's official ibapi — verbose, callback-based, painful for retail but the only fully-supported option.
  5. Wire it up. A minimal connection sketch with ib_async looks like the snippet below.
from ib_async import IB, Stock, MarketOrder

ib = IB()
ib.connect('127.0.0.1', 4002, clientId=1)  # 4002 = paper, 4001 = live

stock = Stock('AAPL', 'SMART', 'USD')
ib.qualifyContracts(stock)

order = MarketOrder('BUY', 100)
trade = ib.placeOrder(stock, order)

ib.sleep(5)
print(trade.fills)  # check status
ib.disconnect()

The IB Gateway has to be running and logged in for any of this to work. There is no headless mode for retail accounts; you'll either run Gateway on a desktop you keep logged in or on a small VPS with VNC.

Alpaca broker API

Alpaca is the simpler choice. Sign up, get an API key, point your bot at the paper endpoint, you're done. No Java, no Gateway, no daily login dance. The trade-off is asset coverage (US equities and crypto only) and the fact that the execution simulation is less battle-tested than IBKR's.

from alpaca.trading.client import TradingClient
from alpaca.trading.requests import MarketOrderRequest
from alpaca.trading.enums import OrderSide, TimeInForce

client = TradingClient('PAPER_API_KEY', 'PAPER_SECRET', paper=True)

req = MarketOrderRequest(
    symbol='AAPL',
    qty=100,
    side=OrderSide.BUY,
    time_in_force=TimeInForce.DAY,
)
order = client.submit_order(req)
print(order)

Paper trading: where to start

Paper trade for at least 30 days. 90 days is materially better. Treat the paper account exactly as you would treat real money — same position sizing, same stop discipline, same tracking. The most expensive lesson in trading-bot-building is the realisation that the paper-to-live transition exposes assumptions the paper environment let you skate over.

The classic paper-to-live failures:

  • Optimistic fills. Paper systems often fill at the bar mid-price; live systems give you the actual ask (or worse, on illiquid names). Build slippage into paper too.
  • Commission drag. Paper accounts often skip fees; live ones don't. Model commissions in paper.
  • Order rejections. Live brokers reject orders for a hundred reasons (margin requirements, locate failures on shorts, halted symbols, exchange-specific rules) that paper doesn't simulate. Build defensive code for rejection paths.

Reference architecture: signal → backtest → paper → live

The discipline that separates working bots from blow-ups is the staged-release pipeline:

  1. Strategy idea. Hypothesis in plain English: "When X happens, Y is more likely than chance, with risk profile Z."
  2. Backtest. Walk-forward on at least one full year of data. Realistic slippage, commissions, position sizing. Kill the idea here if it doesn't survive.
  3. Parameter perturbation. Vary every numeric parameter by ±20% and re-run. If the strategy only works in a narrow parameter band, it is overfit.
  4. Paper trade for 30+ days. Same code that backtests and will live-trade — no separate paper-only branch. Reconcile paper P&L against the backtest's expectation; investigate any divergence.
  5. Live with minimum size. Smallest position size your broker allows. Run for 30 more days. Reconcile against paper; the gap should be slippage and commissions only.
  6. Scale gradually. Double size only after a month of stable live results at the previous tier. There is no good reason to scale faster.

Common failure modes

  • Look-ahead bias. The single most expensive bug in retail backtests. Anything that uses end-of-bar information to make a within-bar decision is broken.
  • Survivorship bias. Universe lists that exclude delisted names produce backtests that look much better than reality. For small caps this is severe — the bottom half of the universe gets delisted often.
  • Curve fitting. The strategy that needs exactly 23.7-period RSI to work will not work tomorrow. If the parameter grid has a single hot spot, the strategy is overfit to noise.
  • Ignored microstructure. Backtests that fill at mid-price assume infinite liquidity. On small caps, your order is part of the liquidity.
  • No risk circuit breaker. Bots without a daily-loss cutoff can lose catastrophically when the strategy regime changes. Always have a "stop trading for the day at -X% drawdown" rule.
  • No monitoring. Bots that fail silently are common. Send a heartbeat every cycle; alert on missing heartbeats, stale data, broker disconnects, or unexpected position state.

A complete code skeleton

Below is a sketch of all five layers connected. It's intentionally simple — production code adds error handling, retries, monitoring, and idempotent state — but the shape is correct. Strategy here: long the open of any small-cap that crosses your signal feed during pre-market with a momentum tier of S, exit at session end. Educational only.

Note on data source: the BullAlert /v1/watchlist endpoint is live today — one API key minted from your dashboard works for both the REST endpoint and the MCP server. The sketch below polls the REST endpoint directly; for an AI-agent bot, wire the MCP server instead (see /api). BullAlert is a data and information tool; you make every final decision.

# main.py — educational sketch only, not production-ready. Trading involves risk.
# BullAlert provides the watchlist data; you make every final decision.
import os, time
from datetime import datetime, timezone
from alpaca.trading.client import TradingClient
from alpaca.trading.requests import MarketOrderRequest
from alpaca.trading.enums import OrderSide, TimeInForce
import requests

# --- 1. Data ingestion: poll the BullAlert watchlist for top momentum picks ---
def fetch_watchlist() -> list[dict]:
    r = requests.get(
        'https://api.bullalert.ai/v1/watchlist',  # top momentum picks, live
        headers={'x-ba-api-key': os.environ['BULLALERT_API_KEY']},
        params={'session': 'current', 'limit': 10},
        timeout=10,
    )
    r.raise_for_status()
    # Response shape: {"data": {"watchlist": [...]}, "meta": {...}}
    # Each row in v1: { rank, ticker, score } only. Score is a 0-100 composite
    # blending static promotion strength (the 20 BullAlert gates) with live
    # momentum evidence from our proprietary algorithm. Recomputed per request,
    # cached ~60s server-side.
    return r.json()['data']['watchlist']

# --- 2. Strategy: only act on the top-N by score ---
def is_actionable(row: dict) -> bool:
    # Use BullAlert's ranking as a candidate filter; you add your own
    # confirmation logic (chart, news, float, your strategy's rules)
    # before ever placing an order. Score >= 80 is "tight confluence".
    return row.get('score', 0) >= 80

# --- 4. Execution: place a small market-on-open order via Alpaca paper ---
client = TradingClient(os.environ['ALPACA_KEY'], os.environ['ALPACA_SECRET'], paper=True)

def place_order(ticker: str):
    req = MarketOrderRequest(
        symbol=ticker, qty=10, side=OrderSide.BUY,
        time_in_force=TimeInForce.OPG,  # market-on-open
    )
    return client.submit_order(req)

# --- 5. Risk: daily loss circuit breaker ---
def daily_drawdown_ok() -> bool:
    account = client.get_account()
    todays_pnl_pct = (float(account.equity) - float(account.last_equity)) / float(account.last_equity)
    return todays_pnl_pct > -0.02  # stop trading at -2% for the day

# --- main loop (paper only — verify your own logic before risking live capital) ---
seen = set()
while True:
    if not daily_drawdown_ok():
        time.sleep(60); continue
    watchlist = fetch_watchlist()
    for row in watchlist:
        if row['ticker'] in seen:
            continue
        if is_actionable(row):
            place_order(row['ticker'])
            seen.add(row['ticker'])
    time.sleep(60)  # endpoint cache is ~60s; polling faster won't get fresher data

What's missing from this sketch: the backtest, the paper-trading discipline, slippage modelling, partial-fill handling, broker disconnect retry, monitoring, idempotency keys, rate limiting, position-sizing logic that respects buying power, exit logic that handles stops and time-of-day, and a hundred small things you'll discover the first week the bot runs unsupervised. Take the staged-release pipeline above seriously.

Where BullAlert fits — and where it doesn't

To restate it cleanly: BullAlert is the data layer in the architecture above, slot 3a ("curated signals as a candidate filter"). It surfaces small-cap momentum signals with catch% and peak% per session. It does not own the strategy decision, the backtest, the paper-trading discipline, the broker connection, or the risk layer. Your bot owns all of that. Always.

If you don't want a curated signal layer at all, every alternative is documented in the market-data and news guides above — Polygon plus Benzinga plus your own scoring is a reasonable competing stack, and so is "Alpaca SIP plus Polygon News plus your own NER pipeline". There is no single right answer; there is the right answer for the strategy you are building.

Frequently asked questions

Do I need to know Rust or C++ to build a trading bot in 2026?

No. Modern Python frameworks (NautilusTrader uses a Rust core internally but exposes a clean Python API) cover almost everything a retail-quant project needs. The exception is ultra-low-latency strategies where the round-trip from Python to broker is the bottleneck — but if that describes you, you already know whether you need C++.

Can I use BullAlert as my signal source?

Yes. BullAlert ships a public REST endpoint at /v1/watchlist plus an MCP server at /v1/mcp — one API key minted from your dashboard works for both. The REST endpoint takes session / limit / as_of params; the MCP exposes the same primitive as a native tool any MCP-capable agent (Claude Desktop, Cursor, ChatGPT custom GPTs) can call. Full reference at /api. We are still not the trading system: we are the data + tools layer. Your bot owns the strategy, the backtest, the paper-trading discipline, and every execution decision.

What's the right amount of paper-trading time before going live?

A bare minimum of 30 days of paper trading on the actual strategy you intend to run, with realistic slippage assumptions and the broker you intend to use. 90 days is meaningfully better. Most bots that fail in production fail because the paper-to-live transition exposed a gap that 30 days of testing would not have caught (commission models, partial fills, broker-side rejection logic).

Should I use IBKR or Alpaca for paper trading?

Either works. IBKR (via IB Gateway) has more realistic execution simulation and broader instrument coverage; Alpaca paper is simpler to integrate and has a generous free tier. For US-equities-only strategies, Alpaca paper is usually the faster path to a working bot. For options or non-US instruments, IBKR is the better choice.

What size account do I need to go live?

Enough to absorb realistic position sizing on your strategy without single-name risk eating you alive. For small-cap momentum specifically, the $5k–$25k range is the typical retail starting band — below it, commission drag and minimum tick sizes distort the strategy; above it, single-name positions get large enough to influence the tape on illiquid names.

How does this differ from BullAlert as a product?

BullAlert is a data tool: it surfaces catch% and peak% per session for small-cap momentum, scored through 20 internal gates, with a transparent methodology and a public alert history. The product makes no execution decisions and produces no trade recommendations. The bot you build in this guide consumes data sources like BullAlert (or any of the alternatives in /blog/best-stock-market-data-apis-2026) and adds the strategy, backtest, paper, and live layers that turn data into action — all of which you own and operate yourself.

Related reading

All posts →