How to Write a Python Script That Trades Crypto Automatically
A step-by-step guide to writing your first automated crypto trading script in Python using ccxt. Goes from zero to live trading with Binance API in under 100 lines of code.
Builder of AI agents, crypto trading bots, and open-source automation tools. Sharing practical guides on how to build, deploy, and profit from AI and DeFi technology.
This is the tutorial I wish existed when I started. No theory, no fluff โ just working Python code that connects to Binance, reads prices, and executes trades automatically.
By the end of this guide you'll have a script that: connects to Binance via API, pulls real-time OHLCV data, calculates a simple buy/sell signal, and places orders automatically.
Prerequisites
- Python 3.10+
- A Binance account (testnet is fine to start)
- Basic Python knowledge
Step 1: Install Dependencies
pip install ccxt python-dotenv pandas
Step 2: Set Up Binance API Keys
- Go to Binance Testnet โ you get free test funds
- Create API keys (Settings โ API Management)
- Enable "Spot & Margin Trading" permission
- Disable withdrawals โ never enable this on a bot API key
Create a .env file:
BINANCE_API_KEY=your_api_key_here
BINANCE_API_SECRET=your_secret_here
BINANCE_TESTNET=true
Step 3: Connect to Binance
import ccxt
import os
from dotenv import load_dotenv
load_dotenv()
def get_exchange():
"""Initialize Binance connection."""
exchange = ccxt.binance({
'apiKey': os.getenv('BINANCE_API_KEY'),
'secret': os.getenv('BINANCE_API_SECRET'),
'enableRateLimit': True, # Respect API rate limits
'options': {'defaultType': 'spot'},
})
# Use testnet if configured
if os.getenv('BINANCE_TESTNET') == 'true':
exchange.set_sandbox_mode(True)
return exchange
exchange = get_exchange()
# Verify connection
balance = exchange.fetch_balance()
usdt_balance = balance['USDT']['free']
print(f"Connected! USDT balance: ${usdt_balance:.2f}")
Step 4: Fetch Price Data
import pandas as pd
def get_ohlcv(symbol='BTC/USDT', timeframe='1h', limit=100):
"""Fetch OHLCV candlestick data."""
ohlcv = exchange.fetch_ohlcv(symbol, timeframe, limit=limit)
df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df.set_index('timestamp', inplace=True)
return df
df = get_ohlcv('BTC/USDT', '1h', 100)
print(df.tail())
Output:
open high low close volume
timestamp
2025-03-15 18:00:00 65234.50 65892.00 65100.20 65670.30 1234.56789
2025-03-15 19:00:00 65670.30 66120.00 65500.00 65980.50 1456.78900
Step 5: Calculate Your Signal
We'll use EMA crossover โ simple and effective:
def calculate_signals(df):
"""Calculate EMA crossover buy/sell signals."""
df = df.copy()
df['ema_fast'] = df['close'].ewm(span=9).mean() # 9-period EMA
df['ema_slow'] = df['close'].ewm(span=21).mean() # 21-period EMA
# Signal: 1 = buy, -1 = sell, 0 = hold
df['signal'] = 0
df.loc[df['ema_fast'] > df['ema_slow'], 'signal'] = 1
df.loc[df['ema_fast'] < df['ema_slow'], 'signal'] = -1
# Only trigger on crossovers (signal changes)
df['crossover'] = df['signal'].diff()
return df
df = calculate_signals(df)
latest = df.iloc[-1]
print(f"Current signal: {latest['signal']}")
print(f"EMA Fast: {latest['ema_fast']:.2f}, EMA Slow: {latest['ema_slow']:.2f}")
Step 6: Place Orders
def get_position(symbol='BTC/USDT'):
"""Check current position."""
balance = exchange.fetch_balance()
base = symbol.split('/')[0] # 'BTC' from 'BTC/USDT'
return balance[base]['free'] if base in balance else 0.0
def buy(symbol, usdt_amount):
"""Market buy with USDT amount."""
ticker = exchange.fetch_ticker(symbol)
price = ticker['last']
amount = usdt_amount / price
# Round to exchange's precision
amount = exchange.amount_to_precision(symbol, amount)
print(f"Placing BUY order: {amount} {symbol.split('/')[0]} @ ~${price:,.2f}")
order = exchange.create_market_buy_order(symbol, float(amount))
print(f"Order filled: {order['id']}")
return order
def sell(symbol, amount):
"""Market sell all holdings."""
amount = exchange.amount_to_precision(symbol, amount)
print(f"Placing SELL order: {amount} {symbol.split('/')[0]}")
order = exchange.create_market_sell_order(symbol, float(amount))
print(f"Order filled: {order['id']}")
return order
Step 7: The Main Trading Loop
import time
def run_bot(
symbol='BTC/USDT',
timeframe='1h',
trade_size_usdt=100, # $100 per trade
sleep_seconds=60, # Check every minute
):
"""Main bot loop."""
print(f"๐ค Starting bot: {symbol} on {timeframe}")
print(f"Trade size: ${trade_size_usdt} USDT")
print("-" * 40)
last_signal = 0
while True:
try:
# 1. Get fresh data
df = get_ohlcv(symbol, timeframe, limit=50)
df = calculate_signals(df)
current_signal = df['signal'].iloc[-1]
current_price = df['close'].iloc[-1]
timestamp = df.index[-1]
# 2. Check for position
btc_held = get_position(symbol)
print(f"[{timestamp}] Price: ${current_price:,.2f} | Signal: {'LONG' if current_signal == 1 else 'SHORT'} | BTC held: {btc_held:.6f}")
# 3. Act on new signals
if current_signal != last_signal:
if current_signal == 1 and btc_held < 0.0001:
# New BUY signal โ we have no position
balance = exchange.fetch_balance()
available_usdt = balance['USDT']['free']
size = min(trade_size_usdt, available_usdt * 0.95)
if size > 10: # Minimum $10 order
buy(symbol, size)
elif current_signal == -1 and btc_held > 0.0001:
# New SELL signal โ we have a position
sell(symbol, btc_held)
last_signal = current_signal
except ccxt.NetworkError as e:
print(f"Network error: {e} โ retrying in 60s")
except ccxt.ExchangeError as e:
print(f"Exchange error: {e}")
except KeyboardInterrupt:
print("\nBot stopped by user.")
break
time.sleep(sleep_seconds)
# Start the bot
run_bot(symbol='BTC/USDT', timeframe='1h', trade_size_usdt=100)
Step 8: Add Stop-Loss Protection
This is critical. Without stops, one bad trade can wipe your account.
def run_bot_with_stops(
symbol='BTC/USDT',
timeframe='1h',
trade_size_usdt=100,
stop_loss_pct=3.0, # Exit if price drops 3% from entry
take_profit_pct=6.0, # Exit if price rises 6% from entry
):
"""Bot with stop-loss and take-profit."""
entry_price = None
while True:
df = get_ohlcv(symbol, timeframe, limit=50)
df = calculate_signals(df)
current_price = df['close'].iloc[-1]
current_signal = df['signal'].iloc[-1]
btc_held = get_position(symbol)
in_position = btc_held > 0.0001
# Stop-loss / take-profit check
if in_position and entry_price:
pnl_pct = (current_price - entry_price) / entry_price * 100
if pnl_pct <= -stop_loss_pct:
print(f"๐ STOP LOSS triggered at {pnl_pct:.1f}%")
sell(symbol, btc_held)
entry_price = None
elif pnl_pct >= take_profit_pct:
print(f"โ
TAKE PROFIT triggered at +{pnl_pct:.1f}%")
sell(symbol, btc_held)
entry_price = None
# Normal signal logic
elif not in_position and current_signal == 1:
balance = exchange.fetch_balance()
size = min(trade_size_usdt, balance['USDT']['free'] * 0.95)
if size > 10:
buy(symbol, size)
entry_price = current_price
time.sleep(60)
Common Mistakes to Avoid
1. Trading with too much capital before testing Start with $20-50, not your life savings. Test on testnet first.
2. Not handling API errors Exchanges go down, network drops. Always wrap API calls in try/except.
3. Ignoring exchange fees Binance charges 0.1% per trade. A strategy that makes 0.05% per trade is actually losing money after fees.
4. Over-optimizing on historical data If your backtest shows 500% returns, it's almost certainly overfitted. Aim for consistent, modest returns.
5. Running on a laptop Your laptop sleeps, crashes, and loses connection. Deploy to a VPS (DigitalOcean, AWS EC2, etc.).
Running 24/7 on a VPS
# On Ubuntu VPS
pip install ccxt python-dotenv pandas
# Run with nohup (survives logout)
nohup python bot.py > bot.log 2>&1 &
# Or use screen
screen -S crypto-bot
python bot.py
# Ctrl+A, D to detach
# Check logs
tail -f bot.log
Full Script (Under 100 Lines)
Here's the complete, production-ready script:
import ccxt, os, time, pandas as pd
from dotenv import load_dotenv
load_dotenv()
exchange = ccxt.binance({
'apiKey': os.getenv('BINANCE_API_KEY'),
'secret': os.getenv('BINANCE_API_SECRET'),
'enableRateLimit': True,
})
if os.getenv('BINANCE_TESTNET') == 'true':
exchange.set_sandbox_mode(True)
def ohlcv(symbol, tf, limit=50):
data = exchange.fetch_ohlcv(symbol, tf, limit=limit)
df = pd.DataFrame(data, columns=['ts','o','h','l','c','v'])
df['ema9'] = df['c'].ewm(span=9).mean()
df['ema21'] = df['c'].ewm(span=21).mean()
df['sig'] = (df['ema9'] > df['ema21']).astype(int) * 2 - 1
return df
def held(symbol):
b = exchange.fetch_balance()
base = symbol.split('/')[0]
return b.get(base, {}).get('free', 0)
def buy(symbol, usd):
price = exchange.fetch_ticker(symbol)['last']
amt = exchange.amount_to_precision(symbol, usd / price)
return exchange.create_market_buy_order(symbol, float(amt))
def sell(symbol, amt):
amt = exchange.amount_to_precision(symbol, amt)
return exchange.create_market_sell_order(symbol, float(amt))
def run(symbol='BTC/USDT', tf='1h', size=100, sl=3.0, tp=6.0):
prev_sig, entry = 0, None
while True:
try:
df = ohlcv(symbol, tf)
sig = df['sig'].iloc[-1]
price = df['c'].iloc[-1]
btc = held(symbol)
in_pos = btc > 0.0001
if in_pos and entry:
pnl = (price - entry) / entry * 100
if pnl <= -sl or pnl >= tp:
print(f"Exit at {pnl:+.1f}%")
sell(symbol, btc); entry = None
elif not in_pos and sig == 1 and prev_sig != 1:
bal = exchange.fetch_balance()['USDT']['free']
s = min(size, bal * 0.95)
if s > 10: buy(symbol, s); entry = price
prev_sig = sig
print(f"{symbol} ${price:,.0f} | {'LONG' if sig==1 else 'SHORT'} | {'IN' if in_pos else 'OUT'}")
except Exception as e:
print(f"Error: {e}")
time.sleep(60)
if __name__ == '__main__':
run()
That's it. Under 70 lines of actual trading logic. Start there, build up as you learn. The best bots are built incrementally โ add features only when you understand why you need them.
Next Steps
Once this is running:
- Add Telegram notifications (see our Telegram bot guide)
- Add multiple pairs (loop through
['BTC/USDT', 'ETH/USDT', 'SOL/USDT']) - Backtest the strategy before going live
- Read about funding rate arbitrage for a market-neutral strategy
Related Articles
The Complete Guide to Crypto Trading Bot Strategies (2025)
9 min read
Crypto BotsHow to Use the Binance API: Complete Beginner Guide (2025)
8 min read
Crypto BotsPython for Crypto: The Complete Developer's Toolkit (2025)
5 min read
Crypto BotsHow to Set Up a Free Crypto Price Alert System in Python
4 min read