---
title: "Crypto Futures"
description: "Some of the Crypto Futures data you can get include prices, margin interest rates, and indicator data."
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/historical-data/asset-classes/crypto-futures
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/historical-data/asset-classes/crypto-futures.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/20%20Historical%20Data/04%20Asset%20Classes/04%20Crypto%20Futures
modified: 2026-10-29
index: https://www.quantconnect.com/llms.txt
---

# Crypto Futures

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Writing Algorithms](https://www.quantconnect.com/docs/v2/writing-algorithms.md) > Historical Data > Asset Classes > Crypto Futures

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## Introduction

This page explains how to get historical data for Crypto Futures. Some of the data you can get include prices, margin interest rates, and indicator data.

## Trades

To get historical [trade data](https://www.quantconnect.com/docs/v2/writing-algorithms/securities/asset-classes/crypto-futures/handling-data.md#03-Trades), call the `history` method with the `TradeBar` type and a security's `Symbol`. This method returns a DataFrame with columns for the open, high, low, close, and volume.

```python
class CryptoFutureTradeBarHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        self.set_end_date(2024, 12, 31)
        # Get the Symbol of a security.
        symbol = self.add_crypto_future('BTCUSD').symbol
        # Get the 5 trailing daily TradeBar objects of the security in DataFrame format.
        history = self.history(TradeBar, symbol, 5, Resolution.DAILY)
```

| symbol | time | close | high | low | open | volume |
| --- | --- | --- | --- | --- | --- | --- |
| BTCUSD | 2024-12-15 | 101383.8 | 102683.9 | 100563.7 | 101403.8 | 16666498.0 |
| BTCUSD | 2024-12-16 | 104500.0 | 105323.5 | 101209.8 | 101383.9 | 26232316.0 |
| BTCUSD | 2024-12-17 | 106120.0 | 107872.1 | 103320.0 | 104500.0 | 44897036.0 |
| BTCUSD | 2024-12-18 | 106171.2 | 108496.9 | 105369.3 | 106120.0 | 41849071.0 |
| BTCUSD | 2024-12-19 | 100163.2 | 106550.2 | 99911.0 | 106171.2 | 65325574.0 |

```python
# Calculate the daily returns.
daily_returns = history.close.pct_change().iloc[1:]
```

```python
symbol  time
BTCUSD  2024-12-16    0.030737
        2024-12-17    0.015502
        2024-12-18    0.000482
        2024-12-19   -0.056588
Name: close, dtype: float64
```

If you intend to use the data in the DataFrame to create `TradeBar` objects, request that the history request returns the data type you need. Otherwise, LEAN consumes unnecessary computational resources populating the DataFrame. To get a list of `TradeBar` objects instead of a DataFrame, call the `history[TradeBar]` method.

```python
# Get the 5 trailing daily TradeBar objects of the security in TradeBar format.
history = self.history[TradeBar](symbol, 5, Resolution.DAILY)
# Iterate through the TradeBar objects and access their volumes.
for trade_bar in history:
    t = trade_bar.end_time
    volume = trade_bar.volume
```

## Quotes

To get historical [quote data](https://www.quantconnect.com/docs/v2/writing-algorithms/securities/asset-classes/crypto-futures/handling-data.md#04-Quotes), call the `history` method with the `QuoteBar` type and a security's `Symbol`. This method returns a DataFrame with columns for the open, high, low, close, and size of the bid and ask quotes. The columns that don't start with "bid" or "ask" are the mean of the quote prices on both sides of the market.

```python
class CryptoFutureQuoteBarHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        self.set_end_date(2024, 12, 31)
        # Get the Symbol of a security.
        symbol = self.add_crypto_future('BTCUSD').symbol
        # Get the 5 trailing minute QuoteBar objects of the security in DataFrame format.
        history = self.history(QuoteBar, symbol, 5, Resolution.MINUTE)
```

| symbol | time | askclose | askhigh | asklow | askopen | asksize | bidclose | bidhigh | bidlow | bidopen | bidsize | close | high | low | open |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| BTCUSD | 2024-12-19 04:56:00 | 100744.2 | 100753.9 | 100727.5 | 100753.9 | 6420.0 | 100744.1 | 100753.8 | 100727.1 | 100753.8 | 4237.0 | 100744.15 | 100753.85 | 100727.30 | 100753.85 |
| BTCUSD | 2024-12-19 04:57:00 | 100727.3 | 100744.2 | 100727.3 | 100744.2 | 5674.0 | 100727.2 | 100744.1 | 100727.2 | 100744.1 | 742.0 | 100727.25 | 100744.15 | 100727.25 | 100744.15 |
| BTCUSD | 2024-12-19 04:58:00 | 100698.5 | 100727.3 | 100653.0 | 100727.3 | 6707.0 | 100698.4 | 100727.2 | 100652.8 | 100727.2 | 3719.0 | 100698.45 | 100727.25 | 100652.90 | 100727.25 |
| BTCUSD | 2024-12-19 04:59:00 | 100606.1 | 100698.5 | 100606.1 | 100698.5 | 1.0 | 100606.0 | 100698.4 | 100606.0 | 100698.4 | 5076.0 | 100606.05 | 100698.45 | 100606.05 | 100698.45 |
| BTCUSD | 2024-12-19 05:00:00 | 100644.1 | 100655.4 | 100606.1 | 100606.1 | 6005.0 | 100644.0 | 100655.3 | 100606.0 | 100606.0 | 611.0 | 100644.05 | 100655.35 | 100606.05 | 100606.05 |

```python
# Calculate the spread at each minute.
spread = history.askclose - history.bidclose
```

```python
symbol  time
BTCUSD  2024-12-19 04:56:00    0.1
        2024-12-19 04:57:00    0.1
        2024-12-19 04:58:00    0.1
        2024-12-19 04:59:00    0.1
        2024-12-19 05:00:00    0.1
dtype: float64
```

If you intend to use the data in the DataFrame to create `QuoteBar` objects, request that the history request returns the data type you need. Otherwise, LEAN consumes unnecessary computational resources populating the DataFrame. To get a list of `QuoteBar` objects instead of a DataFrame, call the `history[QuoteBar]` method.

```python
# Get the 5 trailing minute QuoteBar objects of the security in QuoteBar format.
history = self.history[QuoteBar](symbol, 5, Resolution.MINUTE)
# Iterate through each QuoteBar and calculate the dollar volume on the bid.
for quote_bar in history:
    t = quote_bar.end_time
    bid_dollar_volume = quote_bar.last_bid_size * quote_bar.bid.close
```

## Ticks

To get historical [tick data](https://www.quantconnect.com/docs/v2/writing-algorithms/securities/asset-classes/crypto-futures/handling-data.md#05-Ticks), call the `history` method with a security's `Symbol` and `Resolution.TICK`. This method returns a DataFrame that contains data on bids, asks, and last trade prices.

```python
class CryptoFutureTickHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        self.set_end_date(2024, 12, 31)
        # Get the Symbol of a security.
        symbol = self.add_crypto_future('BTCUSD').symbol
        # Get the trailing 2 days of ticks for the security in DataFrame format.
        history = self.history(symbol, timedelta(2), Resolution.TICK)
```

| symbol | time | askprice | asksize | bidprice | bidsize | lastprice | quantity |
| --- | --- | --- | --- | --- | --- | --- | --- |
| BTCUSD | 2024-12-17 05:00:00.132868 | 106537.1 | 12342.0 | 106537.0 | 35.0 | 106537.05 | 0.0 |
| BTCUSD | 2024-12-17 05:00:00.145098 | 106537.1 | 12353.0 | 106537.0 | 35.0 | 106537.05 | 0.0 |
| BTCUSD | 2024-12-17 05:00:00.424485 | 106537.1 | 12353.0 | 106537.0 | 355.0 | 106537.05 | 0.0 |
| BTCUSD | 2024-12-17 05:00:00.427716 | 106537.1 | 12353.0 | 106537.0 | 354.0 | 106537.05 | 0.0 |
| BTCUSD | 2024-12-17 05:00:00.431177 | 106537.1 | 12353.0 | 106537.0 | 355.0 | 106537.05 | 0.0 |

```python
# Select the rows in the DataFrame that represent trades. Drop the bid/ask columns since they are NaN.
trade_ticks = history[history.quantity > 0].dropna(axis=1)
```

| symbol | time | lastprice | quantity |
| --- | --- | --- | --- |
| BTCUSD | 2024-12-17 05:00:01.008 | 106537.1 | 2.0 |
| BTCUSD | 2024-12-17 05:00:02.729 | 106537.0 | 2.0 |
| BTCUSD | 2024-12-17 05:00:02.971 | 106537.0 | 5.0 |
| BTCUSD | 2024-12-17 05:00:05.977 | 106537.0 | 4.0 |
| BTCUSD | 2024-12-17 05:00:14.072 | 106537.1 | 56.0 |

If you intend to use the data in the DataFrame to create `Tick` objects, request that the history request returns the data type you need. Otherwise, LEAN consumes unnecessary computational resources populating the DataFrame. To get a list of `Tick` objects instead of a DataFrame, call the `history[Tick]` method.

```python
# Get the trailing 2 days of ticks for the security in Tick format.
history = self.history[Tick](symbol, timedelta(2), Resolution.TICK)
# Iterate through each quote tick and calculate the quote size.
for tick in history:
    if tick.tick_type == TickType.QUOTE:
        t = tick.end_time
        size = max(tick.bid_size, tick.ask_size)
```

Ticks are a sparse dataset, so request ticks over a trailing period of time or between start and end times.

## Slices

To get historical [Slice](https://www.quantconnect.com/docs/v2/writing-algorithms/key-concepts/time-modeling/timeslices.md) data, call the `history` method without passing any `Symbol` objects. This method returns `Slice` objects, which contain data points from all the datasets in your algorithm. If you omit the `resolution` argument, it uses the resolution that you set for each security and dataset when you created the subscriptions.

```python
class SliceHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 1)
        self.set_end_date(2024, 12, 31)
        # Add some securities and datasets.
        self.add_crypto_future('BTCUSD')
        # Get the historical Slice objects over the last 5 days for all the subcriptions in your algorithm.
        history = self.history(5, Resolution.DAILY)
        # Iterate through each historical Slice.
        for slice_ in history:
            # Iterate through each TradeBar in this Slice.
            for symbol, trade_bar in slice_.bars.items():
                close = trade_bar.close
```

## Margin Interest Rates

To get historical [margin interest rate data](https://www.quantconnect.com/docs/v2/writing-algorithms/securities/asset-classes/crypto-futures/handling-data.md#06-Margin-Interest-Rates), call the `history` method with the `MarginInterestRate` type and a security's `Symbol`. This method returns a DataFrame with a single column for the interest rate.

```python
class CryptoFutureMarginInterestRateHistoryAlgorithmHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        self.set_end_date(2024, 12, 31)
        # Get the Symbol of a security.
        symbol = self.add_crypto_future('BTCUSD').symbol
        # Get the MarginInterestRate data of the security over the last 2 trading days in DataFrame format.
        history = self.history(MarginInterestRate, symbol, 2, Resolution.DAILY)
```

| symbol | time | interestrate |
| --- | --- | --- |
| BTCUSD | 2024-12-17 08:00:00 | 0.0001 |
| BTCUSD | 2024-12-17 16:00:00 | 0.0001 |
| BTCUSD | 2024-12-18 00:00:00 | 0.0001 |
| BTCUSD | 2024-12-18 08:00:00 | 0.0001 |
| BTCUSD | 2024-12-18 16:00:00 | 0.0001 |
| BTCUSD | 2024-12-19 00:00:00 | 0.0001 |

```python
# Calculate the change in interest rates.
delta = history.interestrate.diff()[1:]
```

```python
symbol  time
BTCUSD  2024-12-17 16:00:00    0.0
        2024-12-18 00:00:00    0.0
        2024-12-18 08:00:00    0.0
        2024-12-18 16:00:00    0.0
        2024-12-19 00:00:00    0.0
Name: interestrate, dtype: float64
```

If you intend to use the data in the DataFrame to create `MarginInterestRate` objects, request that the history request returns the data type you need. Otherwise, LEAN consumes unnecessary computational resources populating the DataFrame. To get a list of `MarginInterestRate` objects instead of a DataFrame, call the `history[MarginInterestRate]` method.

```python
# Get the MarginInterestRate data of the security over the last 2 trading days in MarginInterestRate format.
history = self.history[MarginInterestRate](symbol, 2, Resolution.DAILY)
# Iterate through the MarginInterestRate objects and access their values
for margin_interest_rate in history:
    t = margin_interest_rate.end_time
    interest_rate = margin_interest_rate.interest_rate
```

## Indicators

To get historical [indicator](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/key-concepts.md) values, call the `indicator_history` method with an indicator and the security's `Symbol`.

```python
class CryptoFutureIndicatorHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        self.set_end_date(2024, 12, 31)
        # Get the Symbol of a security.
        symbol = self.add_crypto_future('BTCUSD').symbol
        # Get the 21-day SMA values of the security for the last 5 trading days.
        history = self.indicator_history(SimpleMovingAverage(21), symbol, 5, Resolution.DAILY)
```

To organize the data into a DataFrame, use the `data_frame` property of the result.

```python
# Organize the historical indicator data into a DataFrame to enable pandas wrangling.
history_df = history.data_frame
```

|  | current | rollingsum |
| --- | --- | --- |
| 2024-12-15 | 97759.090476 | 2052940.9 |
| 2024-12-16 | 98065.342857 | 2059372.2 |
| 2024-12-17 | 98689.038095 | 2072469.8 |
| 2024-12-18 | 99363.738095 | 2086638.5 |
| 2024-12-19 | 99563.576190 | 2090835.1 |

```python
# Get the maximum of the SMA values.
sma_max = history_df.current.max()
```

The `indicator_history` method resets your indicator, makes a history request, and updates the indicator with the historical data. Just like with regular history requests, the `indicator_history` method supports time periods based on a trailing number of bars, a trailing period of time, or a defined period of time. If you don't provide a `resolution` argument, it defaults to match the resolution of the security subscription.

To make the `indicator_history` method update the indicator with an [alternative price field](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/automatic-indicators.md#07-Alternative-Price-Fields) instead of the close (or mid-price) of each bar, pass a `selector` argument.

```python
# Get the historical values of an indicator over the last 30 days, applying the indicator to the security's volume.
history = self.indicator_history(indicator, symbol, timedelta(30), selector=Field.VOLUME)
```

Some indicators require the prices of two securities to compute their value (for example, [Beta](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/beta.md)). In this case, pass a list of the `Symbol` objects to the method.

```python
class CryptoFutureMultiAssetIndicatorHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        self.set_end_date(2024, 12, 31)
        # Add the target and reference securities.
        target_symbol = self.add_crypto_future('DOGEUSD').symbol
        reference_symbol = self.add_crypto_future('BTCUSD').symbol
        # Create a 21-period Beta indicator.
        beta = Beta("", target_symbol, reference_symbol, 21)
        # Get the historical values of the indicator over the last 10 trading days.
        history = self.indicator_history(beta, [target_symbol, reference_symbol], 10, Resolution.DAILY)
        # Get the average Beta value.
        beta_avg = history.data_frame.mean()
```

## Examples

The following examples demonstrate common practices for trading cryptocurrency futures with historical data.

### Example 1: Spot-Future Arbitrage

The example below uses linear regression with historical Spot and Future BTCUSDT data to obtain the cointegrating vector between them. We measure the spread between the two securities and trade mean reversal upon their spread divergence.

```python
from sklearn.linear_model import LinearRegression

class CointegrationVectorRegressionAlgorithm(QCAlgorithm):
    # Threshold of spread divergence to enter position.
    entry_threshold = 2

    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_account_currency("USDT", 100000)
        self.set_cash("BTC", 2)

        # Request BTCUSDT Crypto and Crypto Future data to form the cointegrating vectors and trade.
        self._future = self.add_crypto_future("BTCUSDT", market=Market.BYBIT).symbol
        self._spot = self.add_crypto("BTCUSDT", market=Market.BYBIT).symbol

        # Initialize cointegration vector.
        self.cointegration_vector = np.array([0.0, 0.0])
        self.residual_mean = 0
        self.residual_std = 1

        # Daily recalibration on the cointegrating vectors.
        self.schedule.on(
            self.date_rules.every_day(self._future),
            self.time_rules.at(0, 1),
            self.recalibrate
        )

    def on_data(self, slice: Slice) -> None:
        y = slice.bars.get(self._future)
        x1 = slice.bars.get(self._spot)
        if y and x1:
            # Calculate the spread based on the cointegration vector.
            spread = self.calculate_spread(y.close, x1.close, self.cointegration_vector)
            normalized_spread = (spread - self.residual_mean) / self.residual_std

            # If the spread is too high, short the dependent and long the independent variables based on the weightings of the cointegrating vectors.
            # Else, vice versa.
            total_weights = abs(self.cointegration_vector[1]) + 1
            if normalized_spread > self.entry_threshold and not self.portfolio[self._future].is_short:
                self.set_holdings(self._future, -0.5 / total_weights)
                self.set_holdings(self._spot, 0.5 * self.cointegration_vector[1] / total_weights)
            elif normalized_spread < -self.entry_threshold and not self.portfolio[self._future].is_long:
                self.set_holdings(self._future, 0.5 / total_weights)
                self.set_holdings(self._spot, -0.5 * self.cointegration_vector[1] / total_weights)

            # Exit if spread is converged.
            if (self.portfolio[self._future].is_short and normalized_spread < 0) or (self.portfolio[self._future].is_long and normalized_spread > 0):
                self.liquidate()

    def recalibrate(self) -> None:
        # Calculate the cointegration vector from historical data.
        log_price = np.log(self.history([self._future, self._spot], 252, Resolution.DAILY).unstack(0).close.dropna())
        self.cointegration_vector = self.calculate_cointegrating_vector(log_price)
        residual = log_price.apply(lambda x: self.calculate_spread(x[self._future], x[self._spot], self.cointegration_vector), axis=1)
        self.residual_mean, self.residual_std = np.mean(residual), np.std(residual, ddof=1)

    def calculate_cointegrating_vector(self, log_price: pd.DataFrame) -> np.array:
        try:
            # Perform regression.
            lr = LinearRegression().fit(log_price[[self._spot]], log_price[self._future])

            # The coefficients array will contain the coefficients for each independent variable.
            return np.array([lr.intercept_, lr.coef_[0]])
        except:
            return self.cointegration_vector

    def calculate_spread(self, y: float, x1: float, cointegration_vector: np.array) -> float:
        # Using the cointegration vector to calculate the spread.
        return np.log(y) - (cointegration_vector[0] + np.log(x1) * cointegration_vector[1])
```
