Published: May 4, 2026 | Difficulty: Beginner to Intermediate | Reading Time: 15 minutes

Table of Contents

Introduction

I remember the frustration of staring at empty notebooks, wanting to backtest crypto trading strategies but having no idea where to find reliable market data. That changed when I discovered how accessible historical tick data has become through services like Tardis.dev. In this hands-on guide, I will walk you through every single step—from creating your first API request to running a simple moving average crossover backtest on OKX perpetual futures. No prior API experience required.

Throughout this tutorial, we will download real-time and historical tick data from OKX perpetual contracts, process it efficiently, and apply a basic algorithmic trading strategy. By the end, you will have a working Python script that fetches data, calculates indicators, and simulates trades with realistic fee structures.

Prerequisites

Setting Up Your Environment

First, create a dedicated project folder and set up a virtual environment. This keeps your dependencies isolated and prevents version conflicts.

# Create project folder
mkdir okx-backtest
cd okx-backtest

Create virtual environment

python -m venv venv

Activate it (Linux/Mac)

source venv/bin/activate

On Windows, use: venv\Scripts\activate

Install required libraries

pip install requests pandas numpy

Getting Your Tardis.dev API Keys

Tardis.dev provides consolidated market data from over 30 exchanges including Binance, Bybit, OKX, and Deribit. Their API gives you access to trades, order book snapshots, liquidations, and funding rates with sub-second latency.

To get started:

  1. Visit tardis.dev and create a free account
  2. Navigate to Dashboard → API Keys
  3. Generate a new API key
  4. Copy the key (you will not see it again)

The free tier includes 100,000 messages per month—perfect for learning and small backtests.

Making Your First API Call

Let us start with the simplest possible request: fetching recent trades for the OKX BTC/USDT perpetual contract.

import requests
import json

Your Tardis.dev API key

TARDIS_API_KEY = "your_tardis_api_key_here"

Base URL for Tardis.dev historical API

BASE_URL = "https://api.tardis.dev/v1"

Fetch recent trades for OKX BTC/USDT perpetual

exchange = "okx" symbol = "BTC-USDT-PERPETUAL" url = f"{BASE_URL}/exchanges/{exchange}/symbols/{symbol}/trades" params = { "from": "2026-05-01", # Start date "to": "2026-05-02", # End date "limit": 1000 # Max 1000 records per request } headers = { "Authorization": f"Bearer {TARDIS_API_KEY}" } response = requests.get(url, params=params, headers=headers) print(f"Status Code: {response.status_code}") print(f"Records Returned: {len(response.json())}") print("\nSample trade:") print(json.dumps(response.json()[0], indent=2))

Understanding the Data Structure

Each trade record from Tardis.dev contains the following key fields:

For OKX perpetual futures specifically:

Downloading Historical Tick Data

Now let us build a more robust data fetcher that can handle pagination and save data to CSV for later analysis.

import requests
import pandas as pd
from datetime import datetime, timedelta
import time

class TardisDataFetcher:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.tardis.dev/v1"
        
    def get_trades(self, exchange, symbol, start_date, end_date, max_pages=10):
        """
        Fetch historical trades with automatic pagination
        """
        all_trades = []
        from_ts = int(datetime.fromisoformat(start_date).timestamp() * 1000)
        to_ts = int(datetime.fromisoformat(end_date).timestamp() * 1000)
        
        for page in range(max_pages):
            url = f"{self.base_url}/exchanges/{exchange}/symbols/{symbol}/trades"
            params = {
                "from": from_ts,
                "to": to_ts,
                "limit": 5000,
                "page": page + 1
            }
            
            headers = {"Authorization": f"Bearer {self.api_key}"}
            response = requests.get(url, params=params, headers=headers)
            
            if response.status_code != 200:
                print(f"Error: {response.status_code} - {response.text}")
                break
                
            trades = response.json()
            if not trades:
                break
                
            all_trades.extend(trades)
            print(f"Page {page + 1}: Retrieved {len(trades)} trades")
            
            # Rate limiting - wait between requests
            time.sleep(0.5)
            
            # Move time window forward
            from_ts = trades[-1]['timestamp'] + 1
            
        return all_trades
    
    def to_dataframe(self, trades):
        """Convert trades list to pandas DataFrame"""
        df = pd.DataFrame(trades)
        df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
        df['price'] = df['price'].astype(float)
        df['amount'] = df['amount'].astype(float)
        df['cost'] = df['price'] * df['amount']
        return df

Usage example

fetcher = TardisDataFetcher("your_tardis_api_key_here") trades = fetcher.get_trades( exchange="okx", symbol="BTC-USDT-PERPETUAL", start_date="2026-04-01", end_date="2026-04-02", max_pages=5 ) df = fetcher.to_dataframe(trades) print(f"\nTotal records: {len(df)}") print(df.head(10)) print(f"\nPrice range: {df['price'].min():.2f} - {df['price'].max():.2f}") print(f"Total volume: {df['cost'].sum():,.2f} USDT")

Save to CSV for later use

df.to_csv("okx_btc_trades.csv", index=False) print("\nData saved to okx_btc_trades.csv")

Building Your First Backtest

With our data in hand, let us implement a simple Moving Average Crossover Strategy. This classic approach buys when the fast MA crosses above the slow MA and sells on the reverse signal.

import pandas as pd
import numpy as np

class SimpleBacktester:
    def __init__(self, df, initial_capital=10000, fee_rate=0.0007):
        self.df = df.copy()
        self.initial_capital = initial_capital
        self.fee_rate = fee_rate  # 0.07% taker fee for OKX
        
    def add_indicators(self, fast_period=10, slow_period=50):
        """Calculate moving averages"""
        self.df = self.df.set_index('timestamp')
        self.df = self.df.sort_index()
        
        # Resample to 1-minute candles for faster backtesting
        ohlcv = self.df.groupby(pd.Grouper(freq='1min')).agg({
            'price': ['first', 'high', 'low', 'last'],
            'amount': 'sum',
            'cost': 'sum'
        })
        ohlcv.columns = ['open', 'high', 'low', 'close', 'volume']
        ohlcv = ohlcv.dropna()
        
        # Calculate SMAs
        ohlcv['sma_fast'] = ohlcv['close'].rolling(fast_period).mean()
        ohlcv['sma_slow'] = ohlcv['close'].rolling(slow_period).mean()
        
        self.ohlcv = ohlcv.dropna().reset_index()
        return self
        
    def run_backtest(self):
        """Execute the MA crossover strategy"""
        df = self.ohlcv.copy()
        df['position'] = 0
        df.loc[df['sma_fast'] > df['sma_slow'], 'position'] = 1
        df.loc[df['sma_fast'] < df['sma_slow'], 'position'] = -1
        df['position'] = df['position'].shift(1).fillna(0)  # Avoid look-ahead bias
        
        # Calculate returns
        df['strategy_returns'] = df['position'] * df['close'].pct_change()
        df['strategy_returns'] = df['strategy_returns'].fillna(0)
        
        # Apply fees on trade execution
        df['trade'] = df['position'].diff().abs()
        df['fees'] = df['trade'] * self.fee_rate
        df['strategy_returns'] = df['strategy_returns'] - df['fees']
        
        # Calculate cumulative returns
        df['cumulative_returns'] = (1 + df['strategy_returns']).cumprod()
        df['equity'] = self.initial_capital * df['cumulative_returns']
        
        self.results = df
        return self
        
    def get_metrics(self):
        """Calculate performance metrics"""
        df = self.results
        
        total_return = (df['equity'].iloc[-1] / self.initial_capital - 1) * 100
        num_trades = df['trade'].sum() // 2
        
        # Calculate max drawdown
        df['peak'] = df['equity'].cummax()
        df['drawdown'] = (df['equity'] - df['peak']) / df['peak']
        max_drawdown = df['drawdown'].min() * 100
        
        # Annualized metrics (assuming 365 days)
        days = (df['timestamp'].iloc[-1] - df['timestamp'].iloc[0]).days or 1
        annual_return = ((1 + total_return/100) ** (365/days) - 1) * 100
        
        return {
            'Total Return': f"{total_return:.2f}%",
            'Annualized Return': f"{annual_return:.2f}%",
            'Max Drawdown': f"{max_drawdown:.2f}%",
            'Number of Trades': int(num_trades),
            'Final Equity': f"${df['equity'].iloc[-1]:,.2f}"
        }

Run the backtest

backtester = SimpleBacktester(df, initial_capital=10000) backtester.add_indicators(fast_period=10, slow_period=50) backtester.run_backtest() metrics = backtester.get_metrics() print("=" * 50) print("BACKTEST RESULTS - MA Crossover Strategy") print("=" * 50) for key, value in metrics.items(): print(f"{key}: {value}") print("=" * 50)

Save detailed results

backtester.results.to_csv("backtest_results.csv", index=False) print("\nDetailed results saved to backtest_results.csv")

Common Errors and Fixes

1. Authentication Error: "Invalid API Key"

Error Message:

{"error": "Invalid API key", "code": 401}

Causes and Solutions:

# Correct API key handling
TARDIS_API_KEY = "your_key_here".strip()
headers = {"Authorization": f"Bearer {TARDIS_API_KEY}"}

Verify key works

test_response = requests.get( "https://api.tardis.dev/v1/status", headers=headers ) print(test_response.json())

2. Rate Limiting: "Too Many Requests"

Error Message:

{"error": "Rate limit exceeded. Retry after 60 seconds.", "code": 429}

Solution: Implement exponential backoff and respect rate limits.

import time
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def create_session_with_retries(api_key, max_retries=3):
    """Create a requests session with automatic retry logic"""
    session = requests.Session()
    session.headers.update({"Authorization": f"Bearer {api_key}"})
    
    retry_strategy = Retry(
        total=max_retries,
        backoff_factor=2,  # Wait 2, 4, 8 seconds between retries
        status_forcelist=[429, 500, 502, 503, 504]
    )
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("https://", adapter)
    
    return session

Usage

session = create_session_with_retries("your_api_key") response = session.get(url)

3. Date Format Error: Invalid Date Range

Error Message:

{"error": "Invalid date format. Use ISO 8601 or Unix timestamp.", "code": 400}

Solution: Ensure dates are in correct format with timezone awareness.

from datetime import datetime, timezone

Correct date formats for Tardis.dev API

start_date = "2026-05-01T00:00:00Z" # ISO 8601 with UTC end_date = "2026-05-02T23:59:59Z"

Or use Unix timestamps (milliseconds)

start_ts = int(datetime(2026, 5, 1, tzinfo=timezone.utc).timestamp() * 1000) end_ts = int(datetime(2026, 5, 2, tzinfo=timezone.utc).timestamp() * 1000)

Verify your timestamps

print(f"Start: {start_ts}") print(f"End: {end_ts}") print(f"Range: {(end_ts - start_ts) / 86400000:.1f} days")

4. Symbol Not Found Error

Error Message:

{"error": "Symbol BTC/USDT not found", "code": 404}

Solution: OKX uses different symbol naming conventions. Check available symbols first.

# List available OKX symbols
response = requests.get(
    "https://api.tardis.dev/v1/exchanges/okx/symbols",
    headers=headers
)
symbols = response.json()

Filter for perpetual futures

perpetuals = [s for s in symbols if 'PERPETUAL' in s or 'swap' in s.lower()] print("Sample OKX perpetuals:") for s in perpetuals[:10]: print(f" - {s['symbol']}: {s.get('description', 'N/A')}")

Common correct formats for OKX:

"BTC-USDT-PERPETUAL" (Tardis.dev format)

"BTC-USDT-SWAP" (alternative)

Pricing Comparison: Tardis.dev vs Alternatives

When evaluating crypto data providers for algorithmic trading and backtesting, cost efficiency is critical. Here is how Tardis.dev compares to other market data solutions:

Provider Free Tier Pay-as-you-go Pro Plan Exchanges Supported Latency
Tardis.dev 100K messages/month $0.00001/msg $299/month 30+ <100ms
CCXT Pro None Exchange fees + 0.5% $75/month/exchange 100+ Real-time
Binance API 1200 req/min Free (rate limited) N/A Binance only <50ms
CoinAPI 100 req/day $8-75/plan $399/month 300+ Variable
Algoseek None $500+/month Custom pricing 50+ <10ms

Cost Analysis: For a typical backtesting project downloading 10 million tick data points:

Who It Is For / Not For

This Guide Is Perfect For:

This Guide May Not Be Ideal For:

Pricing and ROI

For serious algorithmic traders, data costs are a fraction of potential returns. Consider:

Recommendation: Start with Tardis.dev's free tier to validate your strategies, then upgrade to Pro ($299/month) for unlimited backtesting as your trading operation scales.

Why Choose HolySheep

While Tardis.dev provides excellent market data, you will eventually need AI capabilities to enhance your trading—think natural language strategy descriptions, automated pattern recognition, or intelligent risk analysis.

HolySheep AI offers the most cost-effective AI inference available:

For example, running 100,000 token analysis on your backtest results would cost:

Combine Tardis.dev's market data with HolySheep's AI analysis to build sophisticated quant systems without enterprise budgets.

Conclusion and Next Steps

In this guide, I covered the complete workflow for downloading OKX perpetual futures tick data from Tardis.dev and implementing a basic moving average crossover backtest. You learned how to:

The foundation is now in place for more advanced strategies—mean reversion, momentum, pairs trading, or machine learning-based approaches. Experiment with different parameters, add more sophisticated risk management, and scale up your data collection as your strategies mature.

Ready to supercharge your trading AI? HolySheep offers the industry's most affordable inference with $1 = ¥1 pricing, supporting WeChat Pay, Alipay, and international cards. Experience sub-50ms latency with free credits on registration.

👉 Sign up for HolySheep AI — free credits on registration