VPS for Algorithmic Trading: Running Trading Bots, Data Feeds, and Backtesting 24/7

VPS for Algorithmic Trading: Running Trading Bots, Data Feeds, and Backtesting 24/7

Algorithmic trading bots need to run 24/7 — markets don’t sleep, and a bot running on a laptop gets interrupted by sleep mode, software updates, and power outages. A VPS provides always-on execution, low-latency connections to exchange APIs, and the compute for running backtests without occupying a local workstation. This guide covers the practical infrastructure for running trading algorithms on a VPS.

Why VPS for Trading

  • Always-on execution: Strategies execute 24/7 including overnight, weekends, and when you’re away
  • Low latency to exchanges: A USA VPS is typically 10–50ms from major US exchange APIs (Coinbase, Binance US, Kraken); a Japan VPS is 5–20ms from Bybit, OKX Asia, and Binance Japan
  • Stable network: Data center connections have redundancy that home internet lacks
  • Separation: Trading infrastructure separate from your daily-use machine reduces accident risk

Server Recommendations

  • 2 vCPU / 2–4 GB RAM for Python-based bots with real-time data feeds
  • NVMe storage for fast TimescaleDB time-series queries during backtests
  • USA West/East for US crypto exchanges; Japan for Asian exchanges (Bybit, OKX); Hong Kong for mainland China proximity

Step 1: Install TimescaleDB for Market Data

sudo apt update
sudo apt install -y python3.12 python3.12-venv postgresql postgresql-contrib

# Install TimescaleDB extension
sudo apt install -y timescaledb-2-postgresql-16
sudo timescaledb-tune --quiet --yes
sudo systemctl restart postgresql
sudo -u postgres psql
CREATE DATABASE trading;
\c trading

CREATE EXTENSION IF NOT EXISTS timescaledb;

-- OHLCV (candlestick) data table
CREATE TABLE ohlcv (
    time        TIMESTAMPTZ NOT NULL,
    symbol      TEXT NOT NULL,
    exchange    TEXT NOT NULL,
    timeframe   TEXT NOT NULL,
    open        DOUBLE PRECISION,
    high        DOUBLE PRECISION,
    low         DOUBLE PRECISION,
    close       DOUBLE PRECISION,
    volume      DOUBLE PRECISION
);

-- Convert to hypertable for optimized time-series storage
SELECT create_hypertable('ohlcv', 'time');

-- Fast queries by symbol and timeframe
CREATE INDEX ON ohlcv (symbol, timeframe, time DESC);

-- Individual trade ticks
CREATE TABLE trades (
    time        TIMESTAMPTZ NOT NULL,
    symbol      TEXT NOT NULL,
    exchange    TEXT NOT NULL,
    price       DOUBLE PRECISION,
    amount      DOUBLE PRECISION,
    side        TEXT   -- 'buy' or 'sell'
);
SELECT create_hypertable('trades', 'time');

\q

Step 2: Install Dependencies

mkdir -p /opt/trading && cd /opt/trading
python3.12 -m venv .venv
source .venv/bin/activate

pip install ccxt pandas sqlalchemy psycopg2-binary \
            python-dotenv schedule asyncio aiohttp \
            numpy scipy ta-lib

Step 3: Live Data Feed with CCXT

nano /opt/trading/data_feed.py
"""Collect live OHLCV data and store in TimescaleDB."""
import asyncio
import logging
import os
from datetime import datetime
import ccxt.async_support as ccxt
from sqlalchemy import create_engine, text

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

DB_URL = os.environ['DATABASE_URL']
engine = create_engine(DB_URL)

SYMBOLS = ['BTC/USDT', 'ETH/USDT', 'SOL/USDT']
TIMEFRAMES = ['1m', '5m', '1h']


async def fetch_and_store(exchange, symbol, timeframe):
    try:
        ohlcv = await exchange.fetch_ohlcv(symbol, timeframe, limit=100)
        rows = [
            {
                'time': datetime.utcfromtimestamp(c[0] / 1000),
                'symbol': symbol,
                'exchange': exchange.id,
                'timeframe': timeframe,
                'open': c[1], 'high': c[2], 'low': c[3],
                'close': c[4], 'volume': c[5],
            }
            for c in ohlcv
        ]
        with engine.connect() as conn:
            conn.execute(
                text("INSERT INTO ohlcv VALUES (:time,:symbol,:exchange,:timeframe,:open,:high,:low,:close,:volume) ON CONFLICT DO NOTHING"),
                rows
            )
            conn.commit()
        logger.info(f"Stored {len(rows)} candles for {symbol} {timeframe}")
    except Exception as e:
        logger.error(f"Error fetching {symbol} {timeframe}: {e}")


async def main():
    exchange = ccxt.binance({
        'apiKey': os.environ.get('EXCHANGE_API_KEY'),
        'secret': os.environ.get('EXCHANGE_SECRET'),
        'sandbox': os.environ.get('SANDBOX', 'true').lower() == 'true',
    })
    await exchange.load_markets()
    logger.info(f"Connected to {exchange.id} ({'sandbox' if exchange.sandbox else 'live'})")

    while True:
        tasks = [
            fetch_and_store(exchange, sym, tf)
            for sym in SYMBOLS for tf in TIMEFRAMES
        ]
        await asyncio.gather(*tasks, return_exceptions=True)
        await asyncio.sleep(60)

    await exchange.close()


if __name__ == '__main__':
    asyncio.run(main())

Step 4: Strategy Template (EMA Crossover)

nano /opt/trading/strategy.py
"""EMA crossover strategy — educational example only."""
import pandas as pd
import sqlalchemy as sa
import logging
import os

logger = logging.getLogger(__name__)


def get_ohlcv(symbol: str, timeframe: str, limit: int = 200) -> pd.DataFrame:
    engine = sa.create_engine(os.environ['DATABASE_URL'])
    with engine.connect() as conn:
        df = pd.read_sql(
            """SELECT time, open, high, low, close, volume
               FROM ohlcv WHERE symbol = %s AND timeframe = %s
               ORDER BY time DESC LIMIT %s""",
            conn, params=(symbol, timeframe, limit)
        )
    return df.sort_values('time').reset_index(drop=True)


def compute_signals(df: pd.DataFrame) -> pd.DataFrame:
    df = df.copy()
    df['ema_fast'] = df['close'].ewm(span=12, adjust=False).mean()
    df['ema_slow'] = df['close'].ewm(span=26, adjust=False).mean()
    df['signal'] = 0
    df.loc[df['ema_fast'] > df['ema_slow'], 'signal'] = 1    # Long
    df.loc[df['ema_fast'] < df['ema_slow'], 'signal'] = -1   # Short
    df['crossover'] = df['signal'].diff()
    return df


def run_once(symbol='BTC/USDT', timeframe='1h'):
    df = get_ohlcv(symbol, timeframe)
    df = compute_signals(df)
    latest = df.iloc[-1]
    price = latest['close']

    if latest['crossover'] == 2:
        logger.info(f"BUY signal: {symbol} @ {price:.2f}")
        # exchange.create_market_buy_order(symbol, amount)

    elif latest['crossover'] == -2:
        logger.info(f"SELL signal: {symbol} @ {price:.2f}")
        # exchange.create_market_sell_order(symbol, amount)

Step 5: Secure API Key Storage

nano /opt/trading/.env
DATABASE_URL=postgresql://trading:password@localhost:5432/trading
EXCHANGE_API_KEY=your_exchange_api_key
EXCHANGE_SECRET=your_exchange_secret
SANDBOX=true   # Set to false only for live trading
TELEGRAM_BOT_TOKEN=your_bot_token_for_alerts
TELEGRAM_CHAT_ID=your_chat_id
chmod 600 /opt/trading/.env
# Ensure only the trading user can read it
sudo chown trading:trading /opt/trading/.env

Step 6: systemd Services

sudo nano /etc/systemd/system/trading-feed.service
[Unit]
Description=Trading Data Feed
After=network.target postgresql.service

[Service]
User=trading
WorkingDirectory=/opt/trading
EnvironmentFile=/opt/trading/.env
ExecStart=/opt/trading/.venv/bin/python data_feed.py
Restart=on-failure
RestartSec=30s

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now trading-feed
sudo journalctl -u trading-feed -f

Telegram Alerts for Trades

import requests

def send_alert(message: str):
    token = os.environ['TELEGRAM_BOT_TOKEN']
    chat_id = os.environ['TELEGRAM_CHAT_ID']
    requests.post(
        f"https://api.telegram.org/bot{token}/sendMessage",
        json={'chat_id': chat_id, 'text': message, 'parse_mode': 'HTML'}
    )

# Usage in strategy:
send_alert(f"🟢 <b>BUY</b> {symbol} @ ${price:.2f}\nSignal: EMA crossover")

Getting Started

For US cryptocurrency exchanges, USA VPS plans at VPS.DO minimize API call round-trip time — 10–50ms to Coinbase, Kraken, and Binance US. For Asian exchanges, the Hong Kong VPS provides sub-30ms to most Asian exchange order books. NVMe storage keeps TimescaleDB backtests fast when processing years of tick data.

Conclusion

A VPS provides the always-on infrastructure, low-latency exchange connectivity, and stable network that algorithmic trading requires. The stack — TimescaleDB for time-series data, CCXT for exchange connectivity, systemd for process management, and Telegram for trade alerts — covers the infrastructure needs of most quantitative trading projects. Always start with paper trading (sandbox mode) before committing live capital to any automated strategy.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!