DEV Community

Market Masters
Market Masters

Posted on

Building a Real-Time Crypto Trading Bot with Python, WebSockets, and React

Building a Real-Time Crypto Trading Bot with Python, WebSockets, and React

Real-time price feeds separate serious trading tools from delayed dashboards. If you have built bots that poll every few seconds, you already know the lag problem: missed entries, stale signals, and slippage that eats your edge. This tutorial shows how to build a streaming trading bot that ingests live data, calculates simple indicators, and surfaces signals through a minimal React frontend.

We will use Binance's WebSocket streams for sub-second updates, a lightweight Python backend with python-binance, and a React dashboard that renders live prices and basic signals. The goal is a working skeleton you can extend with your own strategies.

Why WebSockets beat polling

HTTP polling creates three problems for trading bots:

  • Latency: your bot learns about price moves 500ms-2s after they happen
  • Rate limits: Binance caps REST requests; WebSockets bypass that ceiling
  • Resource waste: constant reconnects and JSON parsing overhead

WebSocket streams deliver ticker, depth, and trade updates as they arrive on the exchange. The connection stays open. Your bot reacts in real time.

Project structure

trading-bot/
├── backend/
│   ├── main.py
│   ├── ws_client.py
│   └── requirements.txt
├── frontend/
│   ├── src/
│   │   ├── App.jsx
│   │   └── components/
│   └── package.json
└── README.md
Enter fullscreen mode Exit fullscreen mode

Backend: streaming prices into memory

Install the dependencies first.

pip install python-binance fastapi uvicorn
Enter fullscreen mode Exit fullscreen mode

Create backend/ws_client.py. This module subscribes to Binance's mini-ticker stream for BTCUSDT and ETHUSDT.

import asyncio
from binance import AsyncClient, BinanceSocketManager
from collections import defaultdict

class LivePriceFeed:
    def __init__(self):
        self.prices = defaultdict(dict)
        self.callbacks = []

    async def start(self):
        client = await AsyncClient.create()
        bm = BinanceSocketManager(client)
        ts = bm.miniticker_socket()
        async with ts as tscm:
            while True:
                res = await tscm.recv()
                for item in res:
                    symbol = item['s']
                    self.prices[symbol] = {
                        'price': float(item['c']),
                        'volume': float(item['v']),
                        'change': float(item['P'])
                    }
                for cb in self.callbacks:
                    await cb(self.prices)

    def on_update(self, callback):
        self.callbacks.append(callback)
Enter fullscreen mode Exit fullscreen mode

The LivePriceFeed class stores the latest price in memory and fires callbacks whenever an update arrives. No database round-trips in the hot path.

Adding a simple signal

For a tutorial we keep the logic basic: a 30-second moving average calculated from a rolling buffer. When price crosses the MA, emit a signal.

from collections import deque
import time

class SimpleMASignal:
    def __init__(self, window_seconds=30):
        self.buffer = deque(maxlen=300)  # ~1 update per 100ms
        self.last_cross = None

    def update(self, price, timestamp):
        self.buffer.append((timestamp, price))
        if len(self.buffer) < 2:
            return None

        # Simple MA: average of last N samples
        ma = sum(p for _, p in self.buffer) / len(self.buffer)
        current = price

        if current > ma and (self.last_cross is None or self.last_cross < 0):
            self.last_cross = 1
            return {'signal': 'BUY', 'price': current, 'ma': round(ma, 2)}
        if current < ma and (self.last_cross is None or self.last_cross > 0):
            self.last_cross = -1
            return {'signal': 'SELL', 'price': current, 'ma': round(ma, 2)}
        return None
Enter fullscreen mode Exit fullscreen mode

Hook the signal generator into the price feed callback and you have a minimal bot that prints trade ideas to stdout (or pushes them to Telegram, a webhook, a database, etc.).

Exposing signals via FastAPI

Create backend/main.py so a frontend can poll or connect to the same data.

from fastapi import FastAPI
from ws_client import LivePriceFeed
import asyncio

app = FastAPI()
feed = LivePriceFeed()

@app.on_event("startup")
async def startup():
    asyncio.create_task(feed.start())

@app.get("/prices")
async def get_prices():
    return feed.prices
Enter fullscreen mode Exit fullscreen mode

Run the backend:

uvicorn main:app --reload --port 8000
Enter fullscreen mode Exit fullscreen mode

React frontend: live price table

Scaffold a Vite React app and install a minimal WebSocket client (or just use fetch on a 1-second interval for the tutorial).

npm create vite@latest frontend -- --template react
cd frontend && npm install
Enter fullscreen mode Exit fullscreen mode

Replace src/App.jsx:

import { useEffect, useState } from 'react'

function App() {
  const [prices, setPrices] = useState({})

  useEffect(() => {
    const interval = setInterval(async () => {
      const res = await fetch('http://localhost:8000/prices')
      const data = await res.json()
      setPrices(data)
    }, 1000)
    return () => clearInterval(interval)
  }, [])

  return (
    <div style={{ padding: 20, fontFamily: 'monospace' }}>
      <h1>Live Prices</h1>
      <table>
        <thead>
          <tr>
            <th>Symbol</th>
            <th>Price</th>
            <th>24h %</th>
          </tr>
        </thead>
        <tbody>
          {Object.entries(prices).map(([sym, d]) => (
            <tr key={sym}>
              <td>{sym}</td>
              <td>{d.price}</td>
              <td style={{ color: d.change >= 0 ? 'green' : 'red' }}>
                {d.change}%
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      <p style={{ marginTop: 20, fontSize: 12 }}>
        Signals would appear here once you wire the MA logic into the API response.
      </p>
    </div>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

Start the frontend with npm run dev. You now have a dashboard that updates every second with live prices from Binance.

Next steps and production notes

  • Replace the in-memory buffer with Redis or TimescaleDB if you need persistence across restarts.
  • Add API keys and order placement via python-binance when you are ready to execute.
  • Rate-limit your own endpoints; Binance WebSocket streams are generous, but your frontend is not.
  • Run the backend behind a TLS reverse proxy (Caddy or Nginx) before exposing it publicly.

The full repository with Docker Compose, additional pairs, and a Telegram alert hook lives at github.com/marketmasters/trading-bot-tutorial (example path; replace with your actual repo).

CTA

If you ship a version of this bot, share a screenshot or your GitHub link in the comments. Market Masters runs a free tier that includes real-time screeners and 5 Telegram alerts per month; the Premium plan adds the full AI strategy suite and 125x paper trading. Try it at marketmasters.ai. Questions? Drop them below.

(Word count: 812)

Top comments (0)