DEV Community

Time Flies
Time Flies

Posted on

How to Build a Crypto Trading Bot with CoinGlass API

Building a crypto trading bot is not just about writing a script that buys when price goes up and sells when price goes down.

That kind of bot may work for a simple demo, but it is usually not enough for real crypto markets, especially if the bot trades futures, perpetual contracts, or leveraged positions.

Crypto markets are heavily influenced by derivatives. A large price move may be caused by spot buying, but it may also be caused by:

  • Open interest expansion
  • Funding rate imbalance
  • Short liquidations
  • Long liquidations
  • Leverage build-up
  • Forced deleveraging
  • Order book liquidity gaps
  • Aggressive taker buy or sell pressure
  • Cross-exchange positioning

This is why a serious crypto trading bot needs more than price data. It needs market structure data.

CoinGlass API is useful in this context because it provides access to key crypto derivatives data such as open interest, funding rates, liquidations, long/short ratios, order book data, options data, ETF flow data, and other market indicators. CoinGlass API V4 is the current recommended version, while V1–V3 are deprecated and mainly kept for backward compatibility. ([coinglass][1])

In this guide, we will walk through how to build a crypto trading bot using CoinGlass API as a market intelligence layer. The goal is not to build a perfect money-printing machine. The goal is to design a more market-aware trading bot that can understand when a signal is strong, when risk is elevated, and when it is better to stay out.


1. What Kind of Trading Bot Are We Building?

Before writing code, we need to define the bot.

A crypto trading bot can mean many different things:

Bot Type Description Data Needed
Price alert bot Sends alerts when price reaches a level Price data
Trend-following bot Buys breakouts and sells breakdowns Price, volume, OI
Grid bot Places buy and sell orders in a range Price, volatility, risk filters
Funding rate bot Monitors funding rate opportunities Funding rate, OI, exchange data
Liquidation alert bot Detects abnormal liquidation events Liquidation data
Risk management bot Reduces exposure during extreme market stress OI, funding, liquidations
Quant strategy bot Uses multiple factors to generate signals Historical and real-time data

In this article, we will build a derivatives-aware trading bot framework.

The bot will:

  1. Fetch market data from CoinGlass API
  2. Read open interest, funding rate, and liquidation data
  3. Convert raw data into trading features
  4. Generate market state signals
  5. Combine derivatives signals with a simple price signal
  6. Output a trading decision
  7. Adjust position size based on market risk

This is a realistic architecture because CoinGlass API is best used as a market intelligence layer, while exchange APIs are usually used as the execution layer.


2. CoinGlass API vs Exchange API

Many developers ask:

Why not just use Binance API, OKX API, Bybit API, or another exchange API?
Enter fullscreen mode Exit fullscreen mode

The answer is simple: exchange APIs and CoinGlass API solve different problems.

Function Exchange API CoinGlass API
Place orders Yes No
Manage account balance Yes No
Read current position Yes No
Get exchange-specific price Yes Sometimes, depending on endpoint
Compare multiple exchanges Manual work needed More suitable
Get aggregated derivatives data Harder Designed for this
Get funding rate context Limited to exchange Broader derivatives view
Get liquidation data Exchange-specific or limited More useful as an aggregated market signal
Build market structure signals Requires heavy engineering More direct
Execute trades Yes No

A good trading system usually uses both:

Exchange API = execution layer
CoinGlass API = market intelligence layer
Enter fullscreen mode Exit fullscreen mode

The exchange API answers:

Can I place this order?
What is my account balance?
What is my current position?
Enter fullscreen mode Exit fullscreen mode

CoinGlass API helps answer:

Should I place this order?
Is the market crowded?
Is leverage building up?
Are traders being liquidated?
Is this breakout healthy?
Enter fullscreen mode Exit fullscreen mode

That distinction is important. A trading bot should not only know how to trade. It should also know when not to trade.


3. Why CoinGlass API Is Useful for Trading Bots

CoinGlass provides a broad set of crypto market data. According to CoinGlass public API materials, its crypto API covers derivatives, spot, options, ETF markets, order book data, and market snapshot data such as price, volume, open interest, and funding rates. ([coinglass][2])

For trading bots, the most useful data categories are:

Data Type Why It Matters
Open Interest Shows whether leveraged capital is entering or leaving
Funding Rate Measures long/short cost and crowding
Liquidations Shows forced position closures
Long/Short Ratio Helps detect positioning imbalance
Taker Buy/Sell Volume Shows aggressive buying or selling pressure
Order Book Data Helps analyze liquidity and execution risk
Liquidation Heatmap Helps identify potential liquidation zones
ETF Flow Data Useful for broader market flow analysis
Options Data Useful for volatility and institutional positioning

CoinGlass API official materials list futures modules such as open interest, funding rate, liquidation data, global long/short ratios, top trader long/short ratios, and taker buy/sell volume. Some example endpoint categories include /api/futures/openInterest/ohlc-history, /api/futures/fundingRate/oi-weight-ohlc-history, and /api/futures/liquidation/aggregated-history. ([coinglass][3])

These data points help the bot understand market state instead of only reacting to price.


4. Trading Bot Architecture

A simple crypto trading bot architecture looks like this:

Data Layer
    ↓
Feature Layer
    ↓
Signal Layer
    ↓
Risk Layer
    ↓
Execution Layer
    ↓
Logging / Monitoring Layer
Enter fullscreen mode Exit fullscreen mode

Let’s define each layer.

Layer Purpose
Data Layer Fetch raw market data from APIs
Feature Layer Convert raw data into useful indicators
Signal Layer Generate buy, sell, or hold signals
Risk Layer Adjust position size and filter bad trades
Execution Layer Place orders through exchange API
Logging Layer Store decisions, errors, and performance data

CoinGlass API sits mainly in the Data Layer, but its output is used heavily in the Feature, Signal, and Risk layers.

A practical bot architecture may look like this:

CoinGlass API
  - Open Interest
  - Funding Rate
  - Liquidation Data
  - Long/Short Ratio
  - Order Flow

Exchange API
  - Price Candles
  - Account Balance
  - Order Placement
  - Position Management

Trading Bot
  - Feature Engineering
  - Signal Generation
  - Risk Filtering
  - Position Sizing
  - Execution
Enter fullscreen mode Exit fullscreen mode

This article will focus on the CoinGlass API side and the strategy framework. You can connect the final decision output to your preferred exchange API.


5. Core Strategy Idea

A common beginner bot does this:

If price breaks above moving average → BUY
If price breaks below moving average → SELL
Enter fullscreen mode Exit fullscreen mode

This is simple, but it ignores derivatives market structure.

A better bot does this:

If price gives BUY signal
AND funding rate is not extremely high
AND open interest supports the move
AND liquidation risk is not extreme
THEN allow the trade

Otherwise, reduce size or skip the trade
Enter fullscreen mode Exit fullscreen mode

This is more realistic.

The bot does not simply ask:

Did price go up?
Enter fullscreen mode Exit fullscreen mode

It asks:

Is this price move healthy?
Is leverage supporting it?
Is one side too crowded?
Is the market entering a liquidation cascade?
Enter fullscreen mode Exit fullscreen mode

That is the main difference between a basic bot and a market-aware bot.


6. Key CoinGlass Data for the Bot

For this trading bot, we will focus on three core data types:

  1. Open Interest
  2. Funding Rate
  3. Liquidation Data

These three are enough to build a strong first version.

6.1 Open Interest

Open Interest, or OI, measures the total open futures or perpetual contract positions.

It helps answer:

Is new leverage entering the market?
Enter fullscreen mode Exit fullscreen mode
Price Open Interest Possible Interpretation
Price up OI up New positions support the move
Price up OI down Shorts may be closing
Price down OI up New shorts may be entering
Price down OI down Longs may be closing or liquidated
Price flat OI up Leverage is building before a possible breakout

For bots, OI is useful because it helps confirm whether a price movement is supported by new market participation.

Example:

BTC price rises 3%.
Open interest also rises.
Funding rate remains neutral.
Enter fullscreen mode Exit fullscreen mode

This may be a healthier bullish trend.

But:

BTC price rises 3%.
Open interest rises sharply.
Funding rate becomes extremely positive.
Long/short ratio becomes heavily long.
Enter fullscreen mode Exit fullscreen mode

This may be a crowded long environment.


6.2 Funding Rate

Funding rate is the periodic payment between longs and shorts in perpetual futures.

Funding Rate Meaning
Positive Longs pay shorts
Negative Shorts pay longs
Extremely positive Longs may be crowded
Extremely negative Shorts may be crowded
Neutral Market is more balanced

Funding rate is not a direct buy or sell signal.

A common mistake is:

Funding high = short
Funding low = long
Enter fullscreen mode Exit fullscreen mode

That is too simple.

A better interpretation is:

Funding high = long side may be crowded
Funding low = short side may be crowded
Enter fullscreen mode Exit fullscreen mode

Funding rate is best used as a filter.

Examples:

Price Signal Funding Rate Bot Action
BUY Neutral Allow trade
BUY Extremely positive Reduce size or skip
SELL Neutral Allow trade
SELL Extremely negative Reduce size or skip
BUY Negative + short liquidations rising Possible short squeeze setup

6.3 Liquidation Data

Liquidation data shows forced position closures.

It helps answer:

Which side is being forced out?
Enter fullscreen mode Exit fullscreen mode
Liquidation Event Interpretation
Long liquidation spike Longs are being forced out
Short liquidation spike Shorts are being squeezed
Both sides liquidated Extreme volatility
Liquidations decline Leverage pressure may be cooling
Liquidation spike + price recovery Possible false breakdown or liquidity sweep

Liquidation data is extremely useful for risk management.

A bot can use it to:

  • Avoid trading during liquidation cascades
  • Detect short squeeze conditions
  • Detect long squeeze conditions
  • Reduce position size during market stress
  • Avoid buying or selling after a move is already exhausted

7. API Setup

CoinGlass API V4 uses the following base URL in public documentation and examples:

https://open-api-v4.coinglass.com
Enter fullscreen mode Exit fullscreen mode

Requests generally use the CG-API-KEY header for authentication. CoinGlass documentation states that API V4 brings improved performance, faster response times, and optimized data retrieval. ([coinglass][4])

First, install the required Python packages:

pip install requests pandas python-dotenv
Enter fullscreen mode Exit fullscreen mode

Create a .env file:

COINGLASS_API_KEY=your_api_key_here
Enter fullscreen mode Exit fullscreen mode

Then create a Python file named:

bot.py
Enter fullscreen mode Exit fullscreen mode

Basic API client:

import os
import requests
import pandas as pd
from dotenv import load_dotenv

load_dotenv()

BASE_URL = "https://open-api-v4.coinglass.com"
API_KEY = os.getenv("COINGLASS_API_KEY")

if not API_KEY:
    raise RuntimeError("Missing COINGLASS_API_KEY environment variable")

HEADERS = {
    "CG-API-KEY": API_KEY,
    "Accept": "application/json"
}


def request_coinglass(endpoint, params=None):
    """
    Send a GET request to CoinGlass API.
    """
    url = f"{BASE_URL}{endpoint}"

    response = requests.get(
        url,
        headers=HEADERS,
        params=params,
        timeout=10
    )

    response.raise_for_status()
    return response.json()
Enter fullscreen mode Exit fullscreen mode

This function will be reused for all CoinGlass API requests.


8. Add Safe Request Handling

Production bots should not crash because one API request fails.

Let’s improve the request function with retries:

import time


def safe_request_coinglass(endpoint, params=None, retries=3, sleep_seconds=2):
    """
    Send a GET request with retry logic.
    """
    last_error = None

    for attempt in range(retries):
        try:
            return request_coinglass(endpoint, params)

        except requests.RequestException as error:
            last_error = error
            print(f"Request failed: attempt {attempt + 1}/{retries}: {error}")
            time.sleep(sleep_seconds)

    raise last_error
Enter fullscreen mode Exit fullscreen mode

This is important because live bots must handle:

  • Network errors
  • Timeout errors
  • Temporary API issues
  • Rate limits
  • Invalid responses
  • Missing data

A trading bot should fail safely, not blindly continue with broken data.


9. Fetch Open Interest Data

CoinGlass public API materials list Open Interest endpoints such as:

/api/futures/openInterest/ohlc-history
/api/futures/openInterest/aggregated-history
/api/futures/openInterest/exchange-list
Enter fullscreen mode Exit fullscreen mode

For this example, we will use an OHLC-style history endpoint. Always verify endpoint names and parameters with the latest official documentation before production use.

def fetch_open_interest(symbol="BTC", interval="1h", limit=100):
    endpoint = "/api/futures/openInterest/ohlc-history"

    params = {
        "symbol": symbol,
        "interval": interval,
        "limit": limit
    }

    return safe_request_coinglass(endpoint, params)
Enter fullscreen mode Exit fullscreen mode

Convert the response into a DataFrame:

def to_dataframe(raw):
    """
    Convert a CoinGlass API response to a pandas DataFrame.
    Adjust this function based on the actual response shape.
    """
    data = raw.get("data", [])

    if isinstance(data, dict):
        # Some APIs may return nested structures.
        # Adjust based on actual response.
        rows = data.get("list", [])
    else:
        rows = data

    df = pd.DataFrame(rows)

    if df.empty:
        return df

    if "time" in df.columns:
        df["time"] = pd.to_datetime(df["time"], unit="ms", errors="coerce")

    return df
Enter fullscreen mode Exit fullscreen mode

Add OI features:

def add_open_interest_features(df):
    data = df.copy()

    # Adjust field names based on actual API response.
    if "close" in data.columns:
        data["oi_close"] = pd.to_numeric(data["close"], errors="coerce")
    elif "openInterest" in data.columns:
        data["oi_close"] = pd.to_numeric(data["openInterest"], errors="coerce")
    else:
        raise ValueError("No recognizable open interest field found")

    data["oi_change"] = data["oi_close"].pct_change()
    data["oi_change_24"] = data["oi_close"].pct_change(24)

    return data
Enter fullscreen mode Exit fullscreen mode

10. Fetch Funding Rate Data

CoinGlass public materials list funding rate endpoints such as:

/api/futures/fundingRate/ohlc-history
/api/futures/fundingRate/oi-weight-ohlc-history
/api/futures/fundingRate/exchange-list
Enter fullscreen mode Exit fullscreen mode

A bot can use funding rate to detect crowded long or short conditions.

def fetch_funding_rate(symbol="BTC", interval="1h", limit=100):
    endpoint = "/api/futures/fundingRate/oi-weight-ohlc-history"

    params = {
        "symbol": symbol,
        "interval": interval,
        "limit": limit
    }

    return safe_request_coinglass(endpoint, params)
Enter fullscreen mode Exit fullscreen mode

Add funding rate features:

def zscore(series, window=24):
    mean = series.rolling(window).mean()
    std = series.rolling(window).std()
    return (series - mean) / std


def add_funding_features(df, window=24):
    data = df.copy()

    # Adjust field name based on actual API response.
    if "close" in data.columns:
        data["funding_close"] = pd.to_numeric(data["close"], errors="coerce")
    elif "fundingRate" in data.columns:
        data["funding_close"] = pd.to_numeric(data["fundingRate"], errors="coerce")
    else:
        raise ValueError("No recognizable funding rate field found")

    data["funding_z"] = zscore(data["funding_close"], window)

    return data
Enter fullscreen mode Exit fullscreen mode

Interpretation:

Funding Z-score Meaning
Above 2 Extremely positive funding
1 to 2 Moderately positive funding
-1 to 1 Neutral
-2 to -1 Moderately negative funding
Below -2 Extremely negative funding

This lets the bot detect whether the market is unusually long-heavy or short-heavy.


11. Fetch Liquidation Data

CoinGlass public materials list liquidation endpoints such as:

/api/futures/liquidation/history
/api/futures/liquidation/aggregated-history
/api/futures/liquidation/heatmap/model2
Enter fullscreen mode Exit fullscreen mode

For this example:

def fetch_liquidation_history(
    symbol="BTC",
    exchanges="Binance,OKX,Bybit",
    interval="1h",
    limit=100
):
    endpoint = "/api/futures/liquidation/aggregated-history"

    params = {
        "exchange_list": exchanges,
        "symbol": symbol,
        "interval": interval,
        "limit": limit
    }

    return safe_request_coinglass(endpoint, params)
Enter fullscreen mode Exit fullscreen mode

Add liquidation features:

def add_liquidation_features(df, window=24):
    data = df.copy()

    rename_map = {
        "longLiquidation": "long_liquidation",
        "shortLiquidation": "short_liquidation",
        "long_liq": "long_liquidation",
        "short_liq": "short_liquidation"
    }

    data = data.rename(columns=rename_map)

    if "long_liquidation" not in data.columns:
        raise ValueError("No long liquidation field found")

    if "short_liquidation" not in data.columns:
        raise ValueError("No short liquidation field found")

    data["long_liquidation"] = pd.to_numeric(
        data["long_liquidation"],
        errors="coerce"
    )

    data["short_liquidation"] = pd.to_numeric(
        data["short_liquidation"],
        errors="coerce"
    )

    data["long_liq_z"] = zscore(data["long_liquidation"], window)
    data["short_liq_z"] = zscore(data["short_liquidation"], window)

    data["total_liquidation"] = (
        data["long_liquidation"] + data["short_liquidation"]
    )

    data["total_liq_z"] = zscore(data["total_liquidation"], window)

    return data
Enter fullscreen mode Exit fullscreen mode

Interpretation:

Liquidation Signal Meaning
Long liquidation z-score > 2 Longs are being liquidated unusually
Short liquidation z-score > 2 Shorts are being liquidated unusually
Total liquidation z-score > 3 Extreme risk environment
Liquidation z-score near 0 Normal environment

12. Merge Data into One Market Table

Now we need to merge open interest, funding rate, and liquidation data by timestamp.

def prepare_time_column(df):
    data = df.copy()

    if "time" not in data.columns:
        raise ValueError("Missing time column")

    data = data.dropna(subset=["time"])
    data = data.sort_values("time")

    return data


def merge_market_data(oi_df, funding_df, liquidation_df):
    oi = prepare_time_column(oi_df)
    funding = prepare_time_column(funding_df)
    liquidation = prepare_time_column(liquidation_df)

    merged = pd.merge_asof(
        oi,
        funding,
        on="time",
        direction="nearest",
        tolerance=pd.Timedelta("10min"),
        suffixes=("_oi", "_funding")
    )

    merged = pd.merge_asof(
        merged,
        liquidation,
        on="time",
        direction="nearest",
        tolerance=pd.Timedelta("10min")
    )

    return merged
Enter fullscreen mode Exit fullscreen mode

The merged table may include:

Column Meaning
time Timestamp
oi_close Open interest value
oi_change Short-term OI change
oi_change_24 24-period OI change
funding_close Funding rate
funding_z Funding rate z-score
long_liquidation Long liquidation amount
short_liquidation Short liquidation amount
long_liq_z Long liquidation z-score
short_liq_z Short liquidation z-score
total_liq_z Total liquidation z-score

This table becomes the bot’s derivatives market state table.


13. Build a Simple Price Signal

CoinGlass API can provide market data, but many bots still combine derivatives data with price signals.

For simplicity, let’s create a moving average signal.

In production, you may get price candles from:

  • Your exchange API
  • CoinGlass spot or futures market endpoints
  • Your own database
  • Another market data provider

Example:

def generate_price_signal(price_df):
    """
    Simple moving average signal.
    price_df must contain a 'close' column.
    """
    data = price_df.copy()

    data["close"] = pd.to_numeric(data["close"], errors="coerce")
    data["ma_fast"] = data["close"].rolling(20).mean()
    data["ma_slow"] = data["close"].rolling(60).mean()

    latest = data.iloc[-1]

    if latest["ma_fast"] > latest["ma_slow"]:
        return "BUY"

    if latest["ma_fast"] < latest["ma_slow"]:
        return "SELL"

    return "HOLD"
Enter fullscreen mode Exit fullscreen mode

This signal is intentionally simple. The purpose of the article is not to create a perfect price model, but to show how CoinGlass API data can improve the bot’s market awareness.


14. Build a Derivatives Signal

Now let’s convert the merged derivatives data into a market state signal.

def classify_derivatives_state(row):
    funding_z = row.get("funding_z", 0)
    long_liq_z = row.get("long_liq_z", 0)
    short_liq_z = row.get("short_liq_z", 0)
    total_liq_z = row.get("total_liq_z", 0)
    oi_change = row.get("oi_change", 0)

    if pd.isna(funding_z):
        funding_z = 0
    if pd.isna(long_liq_z):
        long_liq_z = 0
    if pd.isna(short_liq_z):
        short_liq_z = 0
    if pd.isna(total_liq_z):
        total_liq_z = 0
    if pd.isna(oi_change):
        oi_change = 0

    if total_liq_z > 3:
        return "RISK_OFF"

    if funding_z > 2 and oi_change > 0:
        return "LONG_CROWDED"

    if funding_z < -2 and oi_change > 0:
        return "SHORT_CROWDED"

    if short_liq_z > 2 and funding_z < 0:
        return "SHORT_SQUEEZE"

    if long_liq_z > 2 and funding_z > 0:
        return "LONG_SQUEEZE"

    return "NEUTRAL"
Enter fullscreen mode Exit fullscreen mode

Signal meanings:

Signal Meaning
NEUTRAL Market structure is normal
LONG_CROWDED Long side may be overcrowded
SHORT_CROWDED Short side may be overcrowded
SHORT_SQUEEZE Shorts may be getting forced out
LONG_SQUEEZE Longs may be getting forced out
RISK_OFF Extreme liquidation environment

This is the core logic of a derivatives-aware bot.


15. Combine Price Signal and Derivatives Signal

Now combine the basic price signal with the CoinGlass-driven market state.

def final_trading_decision(price_signal, derivatives_state):
    """
    Combine price signal with derivatives market state.
    """

    if derivatives_state == "RISK_OFF":
        return "HOLD"

    if price_signal == "BUY" and derivatives_state == "LONG_CROWDED":
        return "HOLD"

    if price_signal == "SELL" and derivatives_state == "SHORT_CROWDED":
        return "HOLD"

    if price_signal == "BUY" and derivatives_state == "SHORT_SQUEEZE":
        return "BUY"

    if price_signal == "SELL" and derivatives_state == "LONG_SQUEEZE":
        return "SELL"

    if derivatives_state in ["NEUTRAL", "SHORT_SQUEEZE", "LONG_SQUEEZE"]:
        return price_signal

    return "HOLD"
Enter fullscreen mode Exit fullscreen mode

Examples:

Price Signal Derivatives State Final Decision
BUY NEUTRAL BUY
BUY LONG_CROWDED HOLD
SELL SHORT_CROWDED HOLD
BUY SHORT_SQUEEZE BUY
SELL LONG_SQUEEZE SELL
BUY RISK_OFF HOLD
SELL RISK_OFF HOLD

This logic helps the bot avoid bad trades.

It does not guarantee profit, but it can reduce the likelihood of chasing crowded or unstable market conditions.


16. Add Position Sizing

A trading bot should not only decide whether to trade. It should also decide how much to trade.

def position_size_multiplier(derivatives_state):
    multipliers = {
        "NEUTRAL": 1.0,
        "SHORT_SQUEEZE": 1.0,
        "LONG_SQUEEZE": 1.0,
        "LONG_CROWDED": 0.4,
        "SHORT_CROWDED": 0.4,
        "RISK_OFF": 0.0
    }

    return multipliers.get(derivatives_state, 0.5)
Enter fullscreen mode Exit fullscreen mode

Example:

base_position_size = 1000  # USDT notional
derivatives_state = "LONG_CROWDED"

final_size = base_position_size * position_size_multiplier(derivatives_state)

print(final_size)
Enter fullscreen mode Exit fullscreen mode

If the market is neutral, the bot may use normal size.

If the market is crowded, it may reduce size.

If the market is in risk-off mode, it does not trade.

This is often more useful than simply changing buy/sell signals.


17. Full Bot Framework Example

Below is a simplified full example.

It does not place real orders. Instead, it outputs a final trading decision. This is safer for demonstration and easier to adapt.

import os
import time
import requests
import pandas as pd
from dotenv import load_dotenv

load_dotenv()

BASE_URL = "https://open-api-v4.coinglass.com"
API_KEY = os.getenv("COINGLASS_API_KEY")

if not API_KEY:
    raise RuntimeError("Missing COINGLASS_API_KEY environment variable")

HEADERS = {
    "CG-API-KEY": API_KEY,
    "Accept": "application/json"
}


def request_coinglass(endpoint, params=None):
    url = f"{BASE_URL}{endpoint}"

    response = requests.get(
        url,
        headers=HEADERS,
        params=params,
        timeout=10
    )

    response.raise_for_status()
    return response.json()


def safe_request_coinglass(endpoint, params=None, retries=3, sleep_seconds=2):
    last_error = None

    for attempt in range(retries):
        try:
            return request_coinglass(endpoint, params)

        except requests.RequestException as error:
            last_error = error
            print(f"Request failed: attempt {attempt + 1}/{retries}: {error}")
            time.sleep(sleep_seconds)

    raise last_error


def to_dataframe(raw):
    data = raw.get("data", [])

    if isinstance(data, dict):
        rows = data.get("list", [])
    else:
        rows = data

    df = pd.DataFrame(rows)

    if df.empty:
        return df

    if "time" in df.columns:
        df["time"] = pd.to_datetime(df["time"], unit="ms", errors="coerce")

    return df


def zscore(series, window=24):
    mean = series.rolling(window).mean()
    std = series.rolling(window).std()
    return (series - mean) / std


def fetch_open_interest(symbol="BTC", interval="1h", limit=100):
    endpoint = "/api/futures/openInterest/ohlc-history"

    params = {
        "symbol": symbol,
        "interval": interval,
        "limit": limit
    }

    return safe_request_coinglass(endpoint, params)


def fetch_funding_rate(symbol="BTC", interval="1h", limit=100):
    endpoint = "/api/futures/fundingRate/oi-weight-ohlc-history"

    params = {
        "symbol": symbol,
        "interval": interval,
        "limit": limit
    }

    return safe_request_coinglass(endpoint, params)


def fetch_liquidation_history(
    symbol="BTC",
    exchanges="Binance,OKX,Bybit",
    interval="1h",
    limit=100
):
    endpoint = "/api/futures/liquidation/aggregated-history"

    params = {
        "exchange_list": exchanges,
        "symbol": symbol,
        "interval": interval,
        "limit": limit
    }

    return safe_request_coinglass(endpoint, params)


def add_open_interest_features(df):
    data = df.copy()

    if "close" in data.columns:
        data["oi_close"] = pd.to_numeric(data["close"], errors="coerce")
    elif "openInterest" in data.columns:
        data["oi_close"] = pd.to_numeric(data["openInterest"], errors="coerce")
    else:
        raise ValueError("No recognizable open interest field found")

    data["oi_change"] = data["oi_close"].pct_change()
    data["oi_change_24"] = data["oi_close"].pct_change(24)

    return data


def add_funding_features(df, window=24):
    data = df.copy()

    if "close" in data.columns:
        data["funding_close"] = pd.to_numeric(data["close"], errors="coerce")
    elif "fundingRate" in data.columns:
        data["funding_close"] = pd.to_numeric(
            data["fundingRate"],
            errors="coerce"
        )
    else:
        raise ValueError("No recognizable funding rate field found")

    data["funding_z"] = zscore(data["funding_close"], window)

    return data


def add_liquidation_features(df, window=24):
    data = df.copy()

    data = data.rename(columns={
        "longLiquidation": "long_liquidation",
        "shortLiquidation": "short_liquidation",
        "long_liq": "long_liquidation",
        "short_liq": "short_liquidation"
    })

    if "long_liquidation" not in data.columns:
        raise ValueError("No long liquidation field found")

    if "short_liquidation" not in data.columns:
        raise ValueError("No short liquidation field found")

    data["long_liquidation"] = pd.to_numeric(
        data["long_liquidation"],
        errors="coerce"
    )

    data["short_liquidation"] = pd.to_numeric(
        data["short_liquidation"],
        errors="coerce"
    )

    data["long_liq_z"] = zscore(data["long_liquidation"], window)
    data["short_liq_z"] = zscore(data["short_liquidation"], window)

    data["total_liquidation"] = (
        data["long_liquidation"] + data["short_liquidation"]
    )

    data["total_liq_z"] = zscore(data["total_liquidation"], window)

    return data


def prepare_time_column(df):
    data = df.copy()

    if "time" not in data.columns:
        raise ValueError("Missing time column")

    data = data.dropna(subset=["time"])
    data = data.sort_values("time")

    return data


def merge_market_data(oi_df, funding_df, liquidation_df):
    oi = prepare_time_column(oi_df)
    funding = prepare_time_column(funding_df)
    liquidation = prepare_time_column(liquidation_df)

    merged = pd.merge_asof(
        oi,
        funding,
        on="time",
        direction="nearest",
        tolerance=pd.Timedelta("10min"),
        suffixes=("_oi", "_funding")
    )

    merged = pd.merge_asof(
        merged,
        liquidation,
        on="time",
        direction="nearest",
        tolerance=pd.Timedelta("10min")
    )

    return merged


def classify_derivatives_state(row):
    funding_z = row.get("funding_z", 0)
    long_liq_z = row.get("long_liq_z", 0)
    short_liq_z = row.get("short_liq_z", 0)
    total_liq_z = row.get("total_liq_z", 0)
    oi_change = row.get("oi_change", 0)

    values = [funding_z, long_liq_z, short_liq_z, total_liq_z, oi_change]
    values = [0 if pd.isna(v) else v for v in values]

    funding_z, long_liq_z, short_liq_z, total_liq_z, oi_change = values

    if total_liq_z > 3:
        return "RISK_OFF"

    if funding_z > 2 and oi_change > 0:
        return "LONG_CROWDED"

    if funding_z < -2 and oi_change > 0:
        return "SHORT_CROWDED"

    if short_liq_z > 2 and funding_z < 0:
        return "SHORT_SQUEEZE"

    if long_liq_z > 2 and funding_z > 0:
        return "LONG_SQUEEZE"

    return "NEUTRAL"


def final_trading_decision(price_signal, derivatives_state):
    if derivatives_state == "RISK_OFF":
        return "HOLD"

    if price_signal == "BUY" and derivatives_state == "LONG_CROWDED":
        return "HOLD"

    if price_signal == "SELL" and derivatives_state == "SHORT_CROWDED":
        return "HOLD"

    if price_signal == "BUY" and derivatives_state == "SHORT_SQUEEZE":
        return "BUY"

    if price_signal == "SELL" and derivatives_state == "LONG_SQUEEZE":
        return "SELL"

    if derivatives_state in ["NEUTRAL", "SHORT_SQUEEZE", "LONG_SQUEEZE"]:
        return price_signal

    return "HOLD"


def position_size_multiplier(derivatives_state):
    multipliers = {
        "NEUTRAL": 1.0,
        "SHORT_SQUEEZE": 1.0,
        "LONG_SQUEEZE": 1.0,
        "LONG_CROWDED": 0.4,
        "SHORT_CROWDED": 0.4,
        "RISK_OFF": 0.0
    }

    return multipliers.get(derivatives_state, 0.5)


def run_bot_once(symbol="BTC"):
    oi_raw = fetch_open_interest(symbol=symbol, interval="1h", limit=100)
    funding_raw = fetch_funding_rate(symbol=symbol, interval="1h", limit=100)
    liquidation_raw = fetch_liquidation_history(
        symbol=symbol,
        interval="1h",
        limit=100
    )

    oi_df = add_open_interest_features(to_dataframe(oi_raw))
    funding_df = add_funding_features(to_dataframe(funding_raw))
    liquidation_df = add_liquidation_features(to_dataframe(liquidation_raw))

    market_df = merge_market_data(oi_df, funding_df, liquidation_df)

    if market_df.empty:
        raise ValueError("Merged market data is empty")

    latest = market_df.iloc[-1]

    # Example only. Replace this with your real price signal.
    price_signal = "BUY"

    derivatives_state = classify_derivatives_state(latest)
    final_decision = final_trading_decision(price_signal, derivatives_state)

    base_position_size = 1000
    multiplier = position_size_multiplier(derivatives_state)
    final_position_size = base_position_size * multiplier

    result = {
        "symbol": symbol,
        "time": latest["time"],
        "price_signal": price_signal,
        "derivatives_state": derivatives_state,
        "final_decision": final_decision,
        "position_size": final_position_size
    }

    return result


if __name__ == "__main__":
    result = run_bot_once("BTC")

    print("Bot result:")
    for key, value in result.items():
        print(f"{key}: {value}")
Enter fullscreen mode Exit fullscreen mode

This bot framework does not place orders. That is intentional.

Before connecting any strategy to real trading, you should:

  • Backtest it
  • Paper trade it
  • Add exchange execution logic
  • Add risk limits
  • Add error handling
  • Add logs
  • Add monitoring
  • Start with small position size

18. How to Add Exchange Execution

Once the CoinGlass API side works, you can connect the decision output to an exchange API.

The execution layer should be separate from the signal layer.

Example structure:

def execute_trade(decision, symbol, position_size):
    """
    Placeholder execution function.
    Replace with your exchange API logic.
    """

    if decision == "BUY":
        print(f"Place BUY order for {symbol}, size={position_size}")

    elif decision == "SELL":
        print(f"Place SELL order for {symbol}, size={position_size}")

    else:
        print(f"No trade for {symbol}")
Enter fullscreen mode Exit fullscreen mode

Then:

result = run_bot_once("BTC")

execute_trade(
    decision=result["final_decision"],
    symbol=result["symbol"],
    position_size=result["position_size"]
)
Enter fullscreen mode Exit fullscreen mode

In production, the execution function should handle:

  • Order type
  • Quantity precision
  • Minimum notional
  • Slippage
  • Leverage
  • Margin mode
  • Reduce-only orders
  • Stop loss
  • Take profit
  • Position checking
  • Order status confirmation

Never connect a strategy directly to live execution without safety checks.


19. Risk Management Rules

A trading bot without risk management is not a trading system. It is just an automated order sender.

At minimum, your bot should include:

Rule Purpose
Max position size Prevent oversized trades
Max daily loss Stop trading after major losses
Max open positions Avoid overexposure
Stop loss Limit downside
Take profit Lock gains
Cooldown period Avoid overtrading
Risk-off mode Stop trading during extreme events
API failure mode Avoid trading with stale data

Example risk filter:

def risk_filter(account_state, market_state):
    """
    Example risk control logic.
    """

    if account_state["daily_loss_pct"] < -3:
        return "STOP_TRADING"

    if account_state["open_positions"] >= account_state["max_positions"]:
        return "NO_NEW_POSITIONS"

    if market_state["derivatives_state"] == "RISK_OFF":
        return "NO_NEW_POSITIONS"

    return "ALLOW"
Enter fullscreen mode Exit fullscreen mode

Risk management should override signal generation.

A good rule is:

Signal decides what you want to do.
Risk decides whether you are allowed to do it.
Enter fullscreen mode Exit fullscreen mode

20. Backtesting the Strategy

Before using real money, backtest the bot.

A proper backtest should include:

  • Price candles
  • Open interest history
  • Funding rate history
  • Liquidation history
  • Trading fees
  • Slippage
  • Funding payments
  • Latency assumptions
  • Position size rules
  • Stop loss logic
  • Market regime changes

A basic backtest loop may look like this:

def backtest(market_df, price_signals):
    equity = 10000
    position = 0
    trades = []

    for i in range(len(market_df)):
        row = market_df.iloc[i]
        price_signal = price_signals[i]

        derivatives_state = classify_derivatives_state(row)
        decision = final_trading_decision(price_signal, derivatives_state)
        size_multiplier = position_size_multiplier(derivatives_state)

        trades.append({
            "time": row["time"],
            "price_signal": price_signal,
            "derivatives_state": derivatives_state,
            "decision": decision,
            "size_multiplier": size_multiplier
        })

    return pd.DataFrame(trades)
Enter fullscreen mode Exit fullscreen mode

This is only a skeleton. A real backtest must calculate:

  • Entry price
  • Exit price
  • PnL
  • Fees
  • Funding payments
  • Drawdown
  • Sharpe ratio
  • Win rate
  • Average profit/loss
  • Maximum loss
  • Exposure time

Do not skip this step.

A strategy that looks good in a single example may fail across different market regimes.


21. Production Checklist

Before putting a CoinGlass API-powered trading bot into production, use this checklist.

Checklist Item Why It Matters
Confirm latest API endpoints API routes and parameters may change
Confirm response fields Field names may differ by endpoint
Store API key securely Prevent credential leaks
Add request retries Handle temporary errors
Add timeout handling Prevent stuck processes
Add data validation Avoid trading on broken data
Add stale data checks Avoid using old signals
Add rate limit handling Prevent API blocking
Add logs Debug strategy behavior
Add paper trading Validate real-time behavior
Add risk limits Prevent catastrophic loss
Add monitoring alerts Detect failures quickly
Add manual shutdown Stop bot during emergencies
Start small Reduce deployment risk

Example stale data check:

def check_data_freshness(latest_time, max_age_minutes=90):
    now = pd.Timestamp.utcnow()

    if latest_time.tzinfo is None:
        latest_time = latest_time.tz_localize("UTC")

    age = now - latest_time

    if age > pd.Timedelta(minutes=max_age_minutes):
        raise ValueError(f"Data is stale: latest data age is {age}")

    return True
Enter fullscreen mode Exit fullscreen mode

22. Common Mistakes

Mistake 1: Using Funding Rate as a Standalone Signal

Funding rate should not be used alone.

Wrong:

Funding high → short
Funding low → long
Enter fullscreen mode Exit fullscreen mode

Better:

Funding high → long side may be crowded
Funding low → short side may be crowded
Confirm with OI, price, and liquidation data
Enter fullscreen mode Exit fullscreen mode

Mistake 2: Entering Immediately After Liquidations

A liquidation spike can mean a reversal, but it can also mean trend continuation.

You need to check:

  • Did price reclaim the level?
  • Did open interest drop?
  • Did funding normalize?
  • Did volume fade?
  • Did the liquidation cascade continue?

Mistake 3: Ignoring Open Interest

Price without OI context can be misleading.

Price Move OI Move Possible Meaning
Up Up New longs or new positions support the move
Up Down Shorts closing
Down Up New shorts entering
Down Down Longs closing or liquidating

Mistake 4: Overfitting Too Many Indicators

Start simple.

A practical first version can use:

Price signal
+ OI confirmation
+ Funding filter
+ Liquidation risk filter
+ Position sizing rule
Enter fullscreen mode Exit fullscreen mode

Then test improvements one by one.

Mistake 5: No Kill Switch

Every live bot needs a manual and automatic kill switch.

Examples:

Stop trading if API data is stale.
Stop trading if daily loss exceeds threshold.
Stop trading if liquidation risk is extreme.
Stop trading if exchange order API behaves unexpectedly.
Enter fullscreen mode Exit fullscreen mode

23. Strategy Templates You Can Build with CoinGlass API

Template 1: Trend Confirmation Bot

Goal:

Trade only when price trend is supported by derivatives data.
Enter fullscreen mode Exit fullscreen mode

Rules:

Condition Requirement
Price Breakout or moving average trend
Open Interest Rising gradually
Funding Rate Not extreme
Liquidations No abnormal opposite-side risk
Decision Allow trade

Best for:

  • BTC trend strategies
  • ETH momentum strategies
  • Multi-asset futures bots

Template 2: Short Squeeze Bot

Goal:

Detect possible short squeeze setups.
Enter fullscreen mode Exit fullscreen mode

Rules:

Condition Requirement
Funding Rate Negative
Open Interest High or rising
Price Breaks resistance
Short Liquidations Rising
Decision Allow long signal

Best for:

  • Breakout bots
  • Momentum bots
  • Event-driven strategies

Template 3: Long Squeeze Risk Filter

Goal:

Avoid long exposure when the long side is overcrowded.
Enter fullscreen mode Exit fullscreen mode

Rules:

Condition Requirement
Funding Rate Very positive
Open Interest Rising
Price Breaks support
Long Liquidations Rising
Decision Exit, reduce, or avoid longs

Best for:

  • Grid bots
  • Long-biased bots
  • Leveraged futures strategies

Template 4: Funding Rate Arbitrage Monitor

Goal:

Detect cross-exchange funding rate opportunities.
Enter fullscreen mode Exit fullscreen mode

Rules:

Condition Requirement
Funding difference Large enough
Liquidity Sufficient
Open Interest Stable
Liquidation risk Not extreme
Decision Alert or evaluate trade

Best for:

  • Market-neutral strategies
  • Funding capture bots
  • Arbitrage dashboards

Template 5: Risk-Off Detector

Goal:

Pause trading during extreme derivatives stress.
Enter fullscreen mode Exit fullscreen mode

Rules:

Condition Trigger
Total liquidation z-score Above 3
Funding z-score Extreme
OI change Abnormal
Price volatility High
Decision Pause trading

Best for:

  • All leveraged bots
  • Portfolio risk systems
  • Multi-strategy trading platforms

24. How to Expand the Bot

After building the first version, you can expand it with more CoinGlass API data.

Add Long/Short Ratio

Use long/short ratio to detect market sentiment imbalance.

Possible rule:

If long/short ratio is extremely long
AND funding is positive
AND OI is rising
THEN reduce long exposure.
Enter fullscreen mode Exit fullscreen mode

Add Taker Buy/Sell Volume

Use taker buy/sell volume to confirm aggressive flow.

Possible rule:

If price breaks resistance
AND taker buy volume rises
AND short liquidations rise
THEN confirm short squeeze.
Enter fullscreen mode Exit fullscreen mode

Add Liquidation Heatmap

Use liquidation heatmap data to identify zones where forced liquidations may occur.

Possible rule:

If price approaches large liquidation cluster
THEN reduce leverage or tighten stop.
Enter fullscreen mode Exit fullscreen mode

Add Order Book Data

Use order book data for execution quality.

Possible rule:

If order book depth is thin
THEN reduce order size or use limit orders.
Enter fullscreen mode Exit fullscreen mode

Add ETF Flow Data

For BTC and ETH, ETF flow data can help detect broader market demand.

Possible rule:

If ETF inflows are strong
AND derivatives data is not overheated
THEN increase bullish confidence.
Enter fullscreen mode Exit fullscreen mode

25. Final Thoughts

Building a crypto trading bot is not just about coding buy and sell rules.

A strong bot needs to understand market conditions.

Price tells you what happened.

Derivatives data helps explain why it happened.

CoinGlass API can help trading bots move from simple price-based automation to a more complete market-aware system by providing data such as:

  • Open interest
  • Funding rate
  • Liquidations
  • Long/short ratio
  • Taker buy/sell volume
  • Order book data
  • Liquidation heatmap
  • ETF flow data
  • Options data

A basic bot might say:

Price is above the moving average, so buy.
Enter fullscreen mode Exit fullscreen mode

A better bot says:

Price is above the moving average.
Open interest is rising.
Funding rate is neutral.
Liquidations are normal.
Market structure is healthy.
Buy signal is allowed.
Enter fullscreen mode Exit fullscreen mode

Or:

Price is above the moving average.
But funding is extremely positive.
Open interest is rising too fast.
Long side is crowded.
Do not chase.
Enter fullscreen mode Exit fullscreen mode

That is the difference between a simple script and a more intelligent trading system.

No API can guarantee profitable trading. CoinGlass API does not replace strategy design, risk management, execution quality, or backtesting.

But it can provide the derivatives market intelligence that many trading bots are missing.

If you are building a crypto futures or perpetual trading bot, CoinGlass API is one of the most practical ways to add market structure awareness to your system.

Top comments (0)