DEV Community

Timevolt
Timevolt

Posted on

Building Your First Crypto Trading Bot: A Neo-Level Quest

The Quest Begins (The "Why")

Honestly, I was tired of staring at charts while my coffee got cold. I’d spend hours trying to catch a 2 % swing on Bitcoin, only to miss it because I stepped away to grab a snack. It felt like I was playing a never‑ending game of whack‑a‑mole, and the mole kept winning. One night, after yet another missed opportunity, I thought: What if I could automate the boring parts and let the bot do the heavy lifting while I focus on strategy? That was the dragon I wanted to slay – the constant manual monitoring that drained my time and sanity.

The Revelation (The Insight)

The big “aha!” moment came when I realized a trading bot isn’t some mystical black box; it’s just a loop that checks market data, decides if a condition is met, and places an order. The magic isn’t in complex AI (though you can add that later); it’s in disciplined, repeatable logic. Once I stripped away the hype, the core became simple:

  1. Fetch the latest ticker.
  2. Evaluate a rule (e.g., “price crossed above the 20‑period moving average”).
  3. Act – send a market or limit order if the rule fires.

Everything else – error handling, rate‑limit respect, logging – is just polishing the sword.

Wielding the Power (Code & Examples)

The Struggle (Before)

My first attempt was a naive while True loop with a hard‑coded sleep. I forgot to check the exchange’s rate limits and ended up getting banned for a few minutes. Here’s what that looked like:

import time
import ccxt

exchange = ccxt.binance({'enableRateLimit': True})  # oops, I missed this at first
symbol = 'BTC/USDT'

while True:
    ticker = exchange.fetch_ticker(symbol)
    price = ticker['last']
    # super naive rule: buy if price drops 1% from last check
    if price < self.last_price * 0.99:
        exchange.create_market_buy_order(symbol, 0.001)
    self.last_price = price
    time.sleep(5)          # <-- fixed sleep, no respect for rate limits
Enter fullscreen mode Exit fullscreen mode

Trap #1: No exception handling. A network glitch would crash the loop.

Trap #2: Fixed sleep – Binance allows ~1200 requests/minute; hammering it like this gets you throttled.

The Victory (After)

I rewrote it with proper error handling, used the exchange’s built‑in rate limiter, and added a simple moving‑average crossover. The code now feels like assembling my own Iron Man suit – each piece clicks into place, and I can fly.

import ccxt
import ta  # technical analysis library
import logging
from time import sleep

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')

exchange = ccxt.binance({
    'enableRateLimit': True,   # let ccxt respect the exchange limits
    'options': {'defaultType': 'future'}  # adjust if you trade spot/futures
})

symbol = 'BTC/USDT'
timeframe = '1m'
limit = 50   # enough candles for a 20‑period MA

def get_data():
    """Fetch OHLCV and return a list of closes."""
    ohlcv = exchange.fetch_ohlcv(symbol, timeframe, limit=limit)
    closes = [c[4] for c in ohlcv]   # c[4] is close price
    return closes

def should_buy(closes):
    """Simple MA crossover: fast MA (5) > slow MA (20)."""
    if len(closes) < 20:
        return False
    ma_fast = ta.trend.sma_indicator(closes, window=5).iloc[-1]
    ma_slow = ta.trend.sma_indicator(closes, window=20).iloc[-1]
    return ma_fast > ma_slow

def execute_trade():
    try:
        closes = get_data()
        if should_buy(closes):
            logging.info('Signal: BUY')
            # Calculate a modest position size – 0.001 BTC for demo
            amount = 0.001
            order = exchange.create_market_buy_order(symbol, amount)
            logging.info(f'Order placed: {order["id"]}')
        else:
            logging.debug('No signal – holding')
    except ccxt.NetworkError as e:
        logging.warning(f'Network issue: {e}. Retrying in 10s...')
        sleep(10)
    except ccxt.ExchangeError as e:
        logging.error(f'Exchange error: {e}. Check parameters or balances.')
    except Exception as e:
        logging.exception(f'Unexpected error: {e}')

# Main loop – respects rate limits via ccxt built‑in throttling
while True:
    execute_trade()
    sleep(5)   # 5 s is safe now; ccxt will pace the internal calls
Enter fullscreen mode Exit fullscreen mode

What changed?

  • enableRateLimit: True lets ccxt automatically pause when we’re near the limit.
  • Wrapped the call in a try/except block to catch network hiccups and exchange‑specific errors.
  • Used the ta library to compute moving averages cleanly – no manual loops.
  • Added logging so you can see what’s happening without staring at a console.

Common Mistakes to Avoid

  1. Ignoring the exchange’s rate limits – you’ll get HTTP 429 responses and possibly a temporary ban. Always enable enableRateLimit or implement your own sleep based on the exchange’s docs.
  2. Trading without checking balances – placing an order larger than your available funds throws an error and can spam the logs. A quick exchange.fetch_balance() before ordering saves you headaches.

Why This New Power Matters

Now that you have a skeleton bot, you can iterate fast. Want to add stop‑loss? Plug in a trailing‑stop order. Curious about sentiment analysis? Pull in Twitter data and tweak the rule. The bot handles the grind; you handle the strategy. It’s like leveling up from a novice coder to a developer who can ship real‑world tools that actually move money (responsibly, of course!).

The best part? You own the code. No black‑box subscriptions, no hidden fees – just you, your logic, and the market.


Your turn: Fork this bot, tweak the moving‑average windows, or replace the MA rule with something you’ve read about in a crypto forum. Run it on a testnet first, watch the logs, and see how it behaves when the market gets wild. What’s the first feature you’ll add? Share your results – I’d love to hear how your quest unfolds! 🚀

Top comments (0)