How to Build a dYdX Perpetuals Trading Bot with CoinMarketCap API
CoinMarketCap API DIY

How to Build a dYdX Perpetuals Trading Bot with CoinMarketCap API

6m
Created 1w ago, last updated 1d ago

Build a production-aware perpetuals trading system that uses CoinMarketCap API for market signals and dYdX for long and short execution.

How to Build a dYdX Perpetuals Trading Bot with CoinMarketCap API

Table of Contents

Introduction

Perpetual trading is not only about placing orders quickly.

It is about choosing the right market, direction, and risk level before execution.

dYdX provides perpetual trading infrastructure for long and short positions, order management, and leveraged exposure. CoinMarketCap API provides the market intelligence layer that helps decide what to trade and when.

In this guide, you will build a dYdX Perpetuals Trading Bot with CoinMarketCap API, where:
  • CoinMarketCap API powers market selection and signal generation
  • dYdX handles perpetual trade execution
  • your system applies regime, momentum, and risk filters before placing trades

This guide is educational. It is not financial advice.

Why Combine CoinMarketCap and dYdX?

A dYdX bot needs more than an execution connection.

It needs a decision engine.

CoinMarketCap API provides:

  • price and volume data
  • momentum metrics
  • market-wide screening
  • Fear & Greed context
  • Altcoin Season context
  • historical snapshots for backtesting

dYdX provides:

  • perpetual markets
  • long and short execution
  • market, limit, stop, and take-profit order types
  • position management

CoinMarketCap decides the signal. dYdX executes the position.

System Architecture

CoinMarketCap API (Signal Layer)

├─ Quotes Latest

├─ Listings Latest

├─ Fear & Greed

├─ Altcoin Season Index

├─ Historical Quotes


Signal Engine

Long / Short / No Trade

dYdX (Execution Layer)

├─ Market Orders

├─ Limit Orders

├─ Stop Loss

├─ Take Profit

Important:

CoinMarketCap is not an execution feed. It provides structured market context. dYdX handles account state, orders, positions, and fills.

Project Setup

import os
import time
import requests
import pandas as pd

CMC_API_KEY = os.getenv("CMC_API_KEY")
CMC_BASE_URL = "https://pro-api.coinmarketcap.com"

HEADERS = {
"Accept": "application/json",
"X-CMC_PRO_API_KEY": CMC_API_KEY,
}

Step 1: Fetch Core Market Quotes

Use CoinMarketCap quotes to pull the assets you want to trade on dYdX.

Endpoint:

/v3/cryptocurrency/quotes/latest

Example:

def fetch_quotes(ids="1,1027,5426"):
url = f"{CMC_BASE_URL}/v3/cryptocurrency/quotes/latest"

params = {
"id": ids  # BTC, ETH, SOL
}

r = requests.get(url, headers=HEADERS, params=params, timeout=15)
r.raise_for_status()

return r.json()["data"]

This endpoint supports batch requests using comma-separated CoinMarketCap IDs.

The quote field in v3 responses is a list of currency objects. Find the USD entry by matching on symbol:

quotes = asset.get("quote", [])usd = next((q for q in quotes if q.get("symbol") == "USD"), {})

Useful fields inside the USD object:

  • price
  • volume_24h
  • percent_change_1h
  • percent_change_24h
  • percent_change_7d
  • market_cap

Step 2: Flatten quote -> USD

Do not read price or momentum fields from the top level.

Flatten the quote object before generating signals.

def normalize_asset(asset):
quotes = asset.get("quote", [])
usd = next((q for q in quotes if q.get("symbol") == "USD"), {})

return {
"id": asset.get("id"),
"symbol": asset.get("symbol"),
"price": usd.get("price"),
"volume_24h": usd.get("volume_24h"),
"percent_change_1h": usd.get("percent_change_1h"),
"percent_change_24h": usd.get("percent_change_24h"),
"percent_change_7d": usd.get("percent_change_7d"),
"market_cap": usd.get("market_cap"),
"fully_diluted_market_cap": usd.get("fully_diluted_market_cap"),
}

Some values can be null, especially for illiquid or newly listed assets. Always parse defensively.

Step 3: Build a Momentum Signal

Directional perpetual trading needs a clear long, short, or no-trade bias.

def momentum_signal(asset):
p1h = asset.get("percent_change_1h") or 0
p24h = asset.get("percent_change_24h") or 0
p7d = asset.get("percent_change_7d") or 0
volume = asset.get("volume_24h") or 0

if volume < 50_000_000:
return "NO_TRADE"

if p1h > 1 and p24h > 3 and p7d > 0:
return "LONG"

if p1h < -1 and p24h < -3 and p7d < 0:
return "SHORT"

return "NO_TRADE"

This is a simple educational signal.

In production, you should tune thresholds per market and test them historically.

Step 4: Screen Markets with Listings

Use listings to scan a wider universe of assets.

Endpoint:

/v3/cryptocurrency/listings/latest

Example:

def fetch_screened_assets(limit=200):
url = f"{CMC_BASE_URL}/v3/cryptocurrency/listings/latest"

params = {
"limit": limit,
"sort": "volume_24h",
"market_cap_min": 500_000_000,
"volume_24h_min": 50_000_000,
}

r = requests.get(url, headers=HEADERS, params=params, timeout=15)
r.raise_for_status()

return r.json()["data"]

Supported filters include:

  • market_cap_min
  • volume_24h_min
  • percent_change_24h_min

Supported sorts include:

  • volume_24h
  • market_cap
  • percent_change_24h

The maximum limit is 5000.

Listings works on the Basic plan and costs 1 credit plus 1 credit for every 200 assets returned.

Step 5: Add Market Regime Filters

Perpetuals are sensitive to market regime.

A long signal during Risk-Off conditions should be treated differently from a long signal during Risk-On conditions.

Fear & Greed

Endpoint:

/v3/fear-and-greed/latest
def fetch_fear_greed():
url = f"{CMC_BASE_URL}/v3/fear-and-greed/latest"

r = requests.get(url, headers=HEADERS, timeout=15)
r.raise_for_status()

data = r.json()["data"]

return {
"value": data["value"],
"classification": data["value_classification"],
"update_time": data["update_time"],
}

Altcoin Season Index

Endpoint:

/v1/altcoin-season-index/latest
def fetch_altcoin_index():
url = f"{CMC_BASE_URL}/v1/altcoin-season-index/latest"

r = requests.get(url, headers=HEADERS, timeout=15)
r.raise_for_status()

return r.json()["data"]["altcoin_index"]

Official thresholds:

  • altcoin_index > 75 → Altcoin Season
  • altcoin_index < 25 → Bitcoin Season

Step 6: Combine Signal and Regime

Use market context to control position aggression.

def regime_filter(signal, fear_greed_value, altcoin_index):

if signal == "NO_TRADE":
return "NO_TRADE"

if fear_greed_value < 25 and signal == "LONG":
return "REDUCED_LONG"

if fear_greed_value > 85 and signal == "LONG":
return "CAUTIOUS_LONG"

if altcoin_index < 25 and signal != "BTC_LONG":
return "REDUCE_ALT_EXPOSURE"

return signal

The goal is not to predict perfectly. The goal is to avoid taking high-risk trades when macro conditions are weak or overheated.

Step 7: Add Production Momentum Discovery (Paid Feature)

Endpoint:

/v1/cryptocurrency/trending/gainers-losers

This endpoint can help detect fast rotation and sharp directional moves.

It requires a paid plan.

HTTP 403 Forbidden
Error 1006: Plan Not Authorized

Fallback for Basic plans:

Use /v3/cryptocurrency/listings/latest with:

params = {
"sort": "percent_change_24h",
"volume_24h_min": 50_000_000,
}

Trending updates every 10 minutes.

Step 8: Generate Trade Intent

The output of the CoinMarketCap layer should be an execution intent, not an order.

def generate_trade_intent(asset, fear_greed, altcoin_index):
normalized = normalize_asset(asset)
signal = momentum_signal(normalized)
filtered_signal = regime_filter(signal, fear_greed["value"], altcoin_index)

return {
"symbol": normalized["symbol"],
"signal": filtered_signal,
"price": normalized["price"],
"volume_24h": normalized["volume_24h"],
}

Example output:

{
"symbol": "ETH",
"signal": "LONG",
"price": 3200,
"volume_24h": 18500000000,
}

Your dYdX execution layer can then map the signal to:

  • market order
  • limit order
  • stop loss
  • take profit
  • position size
  • leverage

Step 9: Execute on dYdX

CoinMarketCap does not execute trades.

dYdX handles execution.

A dYdX integration should:

  • connect to dYdX using the official SDK or API
  • map the CoinMarketCap asset to the matching dYdX perpetual market
  • check account balances and open positions
  • place an order if the signal passes risk checks
  • attach stop loss and take profit rules

Common order types on dYdX include:

  • market orders
  • limit orders
  • stop market orders
  • stop limit orders
  • take profit market orders
  • take profit limit orders

Use CoinMarketCap for the decision layer and dYdX for order placement.

Step 10: Backtest Your Signals

For momentum backtests, use:

/v3/cryptocurrency/quotes/historical

This endpoint supports intervals such as:

  • 5m
  • 15m
  • 1h
  • daily

For OHLCV candlestick systems, use:

/v2/cryptocurrency/ohlcv/historical

OHLCV is better when your strategy depends on exact open, high, low, and close candles.

Historical data costs 1 credit per 100 data points returned. Avoid calling historical endpoints inside a tight polling loop.

Production pattern:

  • load historical data once at startup
  • store it locally
  • update live state with quotes/latest

Rate Limit Strategy

Recommended polling:

  • Quotes/latest → 30 to 60 seconds
  • Listings/latest → 30 to 60 seconds
  • Fear & Greed → 15 minutes
  • Altcoin Season → 15 minutes
  • Trending → 10 minutes

Best practices:

  • batch quote requests with comma-separated IDs
  • cache macro signals
  • use exponential backoff for HTTP 429
  • avoid polling historical endpoints repeatedly
def safe_get(url, params=None, timeout=15):
for attempt in range(5):
try:
r = requests.get(url, headers=HEADERS, params=params, timeout=timeout)
r.raise_for_status()

return r.json()
except requests.HTTPError as e:
if e.response is not None and e.response.status_code == 429:
time.sleep(2 ** attempt)
continue
raise

Common Mistakes

Ignoring quote -> USD Nesting

Financial fields live inside quote -> USD. Flatten before using them.

Treating CoinMarketCap as an Execution Feed

CoinMarketCap generates signals. dYdX executes trades.

Polling Too Aggressively

Quotes and listings update every 60 seconds. Macro indicators update every 15 minutes.

Overusing Historical Endpoints

Historical data is credit-intensive. Load it once and cache it.

Ignoring Null Fields

Some assets may have missing volume_24h or percent_change_* values. Parse defensively.

Skipping Regime Filters

Perpetual leverage magnifies losses. Momentum signals should be filtered by market conditions.

Minimal End-to-End Flow

quotes = fetch_quotes("1,1027,5426")
fear_greed = fetch_fear_greed()
alt_index = fetch_altcoin_index()

for asset_id, asset in quotes.items():
intent = generate_trade_intent(asset, fear_greed, alt_index)

if intent["signal"] != "NO_TRADE":
print("Trade intent:", intent)

This produces trade intent objects that can be consumed by a dYdX execution module.

Final Thoughts

A strong dYdX bot needs more than an order function.

It needs a market-aware decision layer.

CoinMarketCap API provides the signals, screening, sentiment, and historical data needed to build that layer.

dYdX provides the execution environment for perpetual trades.

Together, they let you build a structured perpetual trading system that is selective, risk-aware, and production-ready.

Next Steps

  • map CoinMarketCap IDs to dYdX markets
  • add position sizing rules
  • connect to the dYdX SDK
  • backtest signal thresholds
  • add stop loss and take profit logic
  • log all signals and execution outcomes

Better signals lead to better perpetual trading decisions.

0 people liked this article