How to Build a Crypto Trading Bot in 2026 (Complete Guide)

The crypto trading bot landscape in 2026 looks nothing like it did three years ago. Exchange APIs have matured, data providers have commoditized basic market data, and the edge has shifted from having a bot to having a bot that adapts to market conditions.

This guide walks you through building a crypto trading bot from scratch — from choosing your exchange API to adding regime-aware logic that keeps your bot from trading a bull-market strategy in a bear market. We'll use both Python and TypeScript examples so you can follow along in whichever language you prefer.

Step 1: Choose Your Exchange and Data Stack

Your bot needs two things: market data (prices, candles, order book) and execution (placing orders). These can come from the same exchange or different providers.

Exchange APIs for Execution

| Exchange | Best For | Maker Fee | API Limits |

|----------|----------|-----------|------------|

| Binance.US | US-based spot trading | 0.1% | 1200 req/min |

| Coinbase Advanced | Institutional, fiat on-ramp | 0.05% | 10 req/sec |

| Drift (Solana) | Perpetual futures, DeFi | 0.02% | No hard limit |

| Hyperliquid | Perpetual futures (non-US) | 0.02% | 1200 req/min |

For this guide, we'll use Binance.US for data and keep execution modular so you can swap in any venue.

Market Data Providers

Basic price data is free from any exchange. The differentiator in 2026 is derived data — regime classification, funding rates, open interest, liquidation flows, and macro correlations. This is where specialized APIs like Regime come in.

Step 2: Set Up Your Project

Python Setup

``python

requirements.txt

ccxt==4.2.0 # Exchange abstraction

pandas==2.2.0 # Data manipulation

requests==2.31.0 # HTTP client

ta==0.11.0 # Technical indicators

` `python

import ccxt

import pandas as pd

Initialize exchange connection

exchange = ccxt.binanceus({

'apiKey': 'YOUR_API_KEY',

'secret': 'YOUR_SECRET',

'sandbox': True, # Start with testnet

})

Fetch 4-hour candles

ohlcv = exchange.fetch_ohlcv('BTC/USDT', '4h', limit=200)

df = pd.DataFrame(ohlcv, columns=['ts', 'open', 'high', 'low', 'close', 'volume'])

df['ts'] = pd.to_datetime(df['ts'], unit='ms')

print(df.tail())

`

TypeScript Setup

`bash

mkdir crypto-bot && cd crypto-bot

npm init -y

npm install ccxt node-fetch

npm install -D typescript @types/node tsx

` `typescript

import ccxt from 'ccxt';

const exchange = new ccxt.binanceus({

apiKey: process.env.BINANCE_API_KEY,

secret: process.env.BINANCE_SECRET,

sandbox: true,

});

const candles = await exchange.fetchOHLCV('BTC/USDT', '4h', undefined, 200);

console.log(Fetched ${candles.length} candles);

console.log('Latest close:', candles[candles.length - 1][4]);

`

Step 3: Implement a Basic Strategy

The SMA (Simple Moving Average) crossover is the starting point for most systematic traders. It's not exotic, but when combined with regime awareness and proper sizing, it's remarkably effective.

Strategy: Buy when the 50-period SMA crosses above the 200-period SMA. Sell when it crosses below.

Python Implementation

`python

import pandas as pd

import ta

def sma_crossover_signals(df: pd.DataFrame) -> pd.DataFrame:

"""Generate SMA 50/200 crossover signals."""

df['sma_50'] = ta.trend.sma_indicator(df['close'], window=50)

df['sma_200'] = ta.trend.sma_indicator(df['close'], window=200)

df['signal'] = 0

df.loc[df['sma_50'] > df['sma_200'], 'signal'] = 1 # Long

df.loc[df['sma_50'] < df['sma_200'], 'signal'] = -1 # Short

# Detect crossover points (signal changes)

df['crossover'] = df['signal'].diff().abs() > 0

return df

signals = sma_crossover_signals(df)

latest = signals.iloc[-1]

print(f"Current signal: {'LONG' if latest['signal'] == 1 else 'SHORT'}")

print(f"SMA 50: {latest['sma_50']:.2f}, SMA 200: {latest['sma_200']:.2f}")

`

TypeScript Implementation

`typescript

function calculateSMA(closes: number[], period: number): number[] {

const sma: number[] = [];

for (let i = 0; i < closes.length; i++) {

if (i < period - 1) { sma.push(NaN); continue; }

const slice = closes.slice(i - period + 1, i + 1);

sma.push(slice.reduce((a, b) => a + b, 0) / period);

}

return sma;

}

function getSignal(closes: number[]): 'LONG' | 'SHORT' | 'NEUTRAL' {

const sma50 = calculateSMA(closes, 50);

const sma200 = calculateSMA(closes, 200);

const latest50 = sma50[sma50.length - 1];

const latest200 = sma200[sma200.length - 1];

if (isNaN(latest50) || isNaN(latest200)) return 'NEUTRAL';

return latest50 > latest200 ? 'LONG' : 'SHORT';

}

`

Step 4: Backtest Before You Risk a Single Dollar

The most common mistake new bot builders make is going live without backtesting. A strategy that feels right can easily lose money over hundreds of trades.

`python

def backtest_sma(df: pd.DataFrame) -> dict:

"""Simple SMA crossover backtest."""

signals = sma_crossover_signals(df)

signals['returns'] = signals['close'].pct_change()

signals['strategy_returns'] = signals['signal'].shift(1) * signals['returns']

cumulative = (1 + signals['strategy_returns']).cumprod()

total_return = cumulative.iloc[-1] - 1

# Calculate max drawdown

peak = cumulative.expanding().max()

drawdown = (cumulative - peak) / peak

max_dd = drawdown.min()

return {

'total_return': f"{total_return:.1%}",

'max_drawdown': f"{max_dd:.1%}",

'sharpe': signals['strategy_returns'].mean() / signals['strategy_returns'].std() (252*0.5),

}

results = backtest_sma(df)

print(f"Return: {results['total_return']}, Max DD: {results['max_drawdown']}")

`

What Our Backtests Show

We backtested SMA 50/200 across 302,000+ candles (March 2023 to March 2026) on BTC, ETH, and SOL:

| Asset | Timeframe | Return | Max Drawdown |

|-------|-----------|--------|--------------|

| SOL | 4h | +586% | -34% |

| ETH | 1d | +166% | -28% |

| BTC | 1d | +41% | -18% |

These returns are with 5x leverage and no stop loss. More on why stop losses hurt in the next section.

Step 5: Add Regime Detection (The Edge)

Here's where your bot goes from "basic tutorial project" to "production trading system." A raw SMA crossover doesn't know if the market is trending or chopping. In a choppy market, it generates false signals that bleed your account through fees and slippage.

Regime detection solves this. By checking the current market regime before acting on a signal, you can:

  • Reduce position size in chop (where SMA crossovers whipsaw)
  • Go full size in clean trends (where SMA crossovers print money)
  • Avoid counter-trend trades (don't short in a high-confidence bull regime)

Using the Regime API

`python

import requests

def get_regime():

"""Fetch current market regime from Regime API."""

resp = requests.get('https://getregime.com/api/v1/market/regime')

data = resp.json()

return {

'regime': data['regime'], # 'bull', 'bear', or 'chop'

'confidence': data['confidence'], # 0.0 to 1.0

'signals': data['signals'], # individual signal breakdown

}

regime = get_regime()

print(f"Current regime: {regime['regime']} ({regime['confidence']:.0%} confidence)")

`

TypeScript with the Regime SDK

`bash

npm install getregime

` `typescript

import { RegimeClient } from 'getregime';

const regime = new RegimeClient(); // Free tier, no API key needed

async function getRegimeAwareSignal(closes: number[]) {

const smaSignal = getSignal(closes);

const market = await regime.getRegime();

// Regime-aware position sizing

let sizeFactor = 1.0;

if (market.regime === 'chop') {

sizeFactor = 0.3; // Cut size 70% in choppy markets

} else if (market.regime === 'bear' && smaSignal === 'LONG') {

sizeFactor = 0.5; // Reduce longs in bear markets

} else if (market.confidence > 0.85) {

sizeFactor = 1.0; // Full size in high-confidence trends

}

return { signal: smaSignal, regime: market.regime, sizeFactor };

}

`

Combining Regime with Strategy Logic

The most effective pattern we've found is using regime as a sizing filter, not a trade blocker. Our backtests showed that completely blocking trades in unfavorable regimes hurts performance — you miss the early moves of regime transitions. Instead, scale down:

`python

def regime_adjusted_size(base_size: float, regime: dict) -> float:

"""Adjust position size based on market regime."""

confidence = regime['confidence']

if regime['regime'] == 'chop':

return base_size * 0.3 # Choppy = small positions

if regime['regime'] == 'bull':

# Scale with confidence: 50% at low confidence, 100% at high

return base_size (0.5 + 0.5 confidence)

if regime['regime'] == 'bear':

# Bear regime: full size for shorts, reduced for longs

return base_size * confidence # Higher confidence = bigger short

return base_size

`

Step 6: Build the Execution Loop

Now let's wire everything together into a loop that runs on a schedule.

`python

import time

import ccxt

def run_bot():

exchange = ccxt.binanceus({

'apiKey': 'YOUR_KEY',

'secret': 'YOUR_SECRET',

})

position = None # Track current position

while True:

try:

# 1. Fetch latest candles

ohlcv = exchange.fetch_ohlcv('BTC/USDT', '4h', limit=200)

df = pd.DataFrame(ohlcv, columns=['ts','open','high','low','close','volume'])

# 2. Generate signal

signals = sma_crossover_signals(df)

signal = signals.iloc[-1]['signal']

# 3. Check regime

regime = get_regime()

# 4. Calculate position size

base_size = 1000 # $1000 base

size = regime_adjusted_size(base_size, regime)

# 5. Execute if signal changed

if signal == 1 and position != 'LONG':

amount = size / df.iloc[-1]['close']

exchange.create_market_buy_order('BTC/USDT', amount)

position = 'LONG'

print(f"LONG BTC | size=${size:.0f} | regime={regime['regime']}")

elif signal == -1 and position != 'SHORT':

if position == 'LONG':

# Close long first

exchange.create_market_sell_order('BTC/USDT', amount)

position = 'SHORT'

print(f"SHORT BTC | size=${size:.0f} | regime={regime['regime']}")

# 6. Sleep until next candle

time.sleep(4 60 60) # 4 hours

except Exception as e:

print(f"Error: {e}")

time.sleep(60) # Retry after 1 min

if __name__ == '__main__':

run_bot()

`

Step 7: Go Live (Safely)

Before switching from paper to live trading:

  • Paper trade for at least 2 weeks. Compare your bot's paper results to what a manual execution would have done. This catches bugs in order sizing, timing, and signal logic.
  • Start with 10% of your intended capital. If your target allocation is $10,000, start with $1,000. Scale up after 30+ live trades confirm the backtest.
  • Monitor regime transitions. The most dangerous periods for systematic strategies are regime changes — when the market shifts from bull to bear or vice versa. The Regime API's confidence score helps here: when confidence drops below 50%, your bot should automatically reduce exposure.
  • Set operational alerts, not just trading alerts. You need to know when your bot crashes, when the exchange API is down, and when your account balance drops below a threshold.
  • Never use fixed stop losses with leveraged trend-following. Our backtesting across 300K+ candles proved that a 3% stop loss with 5x leverage destroys SMA crossover returns. The strategy's own signal flip (SMA cross) is a better exit than any fixed stop loss.
  • Common Mistakes to Avoid

    Over-fitting to historical data. If your strategy has 15 parameters, it's curve-fit to the past. SMA 50/200 has exactly 2 parameters and works across multiple assets and timeframes.

    Ignoring fees. A strategy that makes 0.1% per trade sounds great until you realize you're paying 0.1% in taker fees. Use limit orders (maker fees) wherever possible.

    No regime awareness. This is the single biggest differentiator between bots that survive and bots that blow up. A bot that trades the same way in a bull market and a bear market is a bot that loses money.

    Trading too many pairs. Start with BTC. Add ETH once you're profitable. Add SOL once ETH is working. Complexity is the enemy of reliability.

    What's Next

    This guide gives you a working bot with regime-aware sizing. To take it further:


    Ready to add regime intelligence to your bot? Get started with the free tier at getregime.com/quickstart — no API key required for basic regime data, and you'll be making regime-aware trades in under 60 seconds.