book
Checkout our new book! Hands on AI Trading with Python, QuantConnect, and AWS Learn More arrow

Asset Classes

Futures

Introduction

This page explains how to get historical data for Futures. Some of the data you can get include prices, open interest, indicators, and contract chain data.

Trades

To get historical trade data, 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.

Select Language:
class FutureTradeBarHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        # Get the Symbol of a security.
        future = self.add_future(Futures.Indices.SP_500_E_MINI)
        symbol = list(self.futures_chain(future.symbol))[0].symbol
        # Get the 5 trailing daily TradeBar objects of the security in DataFrame format. 
        history = self.history(TradeBar, symbol, 5, Resolution.DAILY)
closehighlowopenvolume
expirysymboltime
2024-12-20 13:30:00ES YOGVNNAOI1OH2024-12-12 17:00:006058.756087.756055.506082.251152801.0
2024-12-13 17:00:006051.256084.756041.256077.251027682.0
2024-12-16 17:00:006076.506090.506064.756072.50378263.0
2024-12-17 17:00:006052.006063.506040.756052.75278531.0
2024-12-18 17:00:005872.256074.505840.006050.25335048.0
# Calculate the daily returns.
daily_returns = history.close.pct_change().iloc[1:]
expiry               symbol           time               
2024-12-20 13:30:00  ES YOGVNNAOI1OH  2024-12-13 17:00:00   -0.001238
                                      2024-12-16 17:00:00    0.004173
                                      2024-12-17 17:00:00   -0.004032
                                      2024-12-18 17:00:00   -0.029701
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.

# 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, 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.

Select Language:
class FutureQuoteBarHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        # Get the Symbol of a security.
        future = self.add_future(Futures.Indices.SP_500_E_MINI)
        symbol = list(self.futures_chain(future.symbol))[0].symbol
        # Get the 5 trailing minute QuoteBar objects of the security in DataFrame format. 
        history = self.history(QuoteBar, symbol, 5, Resolution.MINUTE)
askcloseaskhighasklowaskopenasksizebidclosebidhighbidlowbidopenbidsizeclosehighlowopen
expirysymboltime
2024-12-20 13:30:00ES YOGVNNAOI1OH2024-12-12 17:00:006058.756087.756055.756082.5019.06058.506087.506055.506082.2552.06058.6256087.6256055.6256082.375
2024-12-13 17:00:006051.506085.006041.506077.5059.06051.256084.756041.256077.251.06051.3756084.8756041.3756077.375
2024-12-16 17:00:006076.756090.756064.756072.5010.06076.506090.506064.506072.257.06076.6256090.6256064.6256072.375
2024-12-17 17:00:006052.256063.506041.006053.0012.06052.006063.256040.506052.751.06052.1256063.3756040.7506052.875
2024-12-18 17:00:005872.756074.755840.256050.252.05872.256074.505840.006050.001.05872.5006074.6255840.1256050.125
# Calculate the spread at each minute.
spread = history.askclose - history.bidclose
expiry               symbol           time               
2024-12-20 13:30:00  ES YOGVNNAOI1OH  2024-12-12 17:00:00    0.25
                                      2024-12-13 17:00:00    0.25
                                      2024-12-16 17:00:00    0.25
                                      2024-12-17 17:00:00    0.25
                                      2024-12-18 17:00:00    0.50
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.

# 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, 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.

Select Language:
class FutureTickHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        # Get the Symbol of a security.
        future = self.add_future(Futures.Indices.SP_500_E_MINI)
        symbol = list(self.futures_chain(future.symbol))[0].symbol
        # Get the trailing 2 days of ticks for the security in DataFrame format.
        history = self.history(symbol, timedelta(2), Resolution.TICK)
askpriceasksizebidpricebidsizelastpriceopeninterestquantity
expirysymboltime
2024-12-20 13:30:00ES YOGVNNAOI1OH2024-12-17 09:30:00.001NaNNaNNaNNaN6052.75NaN1.0
2024-12-17 09:30:00.0010.00.06052.7533.06052.75NaN0.0
2024-12-17 09:30:00.0016053.011.00.000.06053.00NaN0.0
2024-12-17 09:30:00.0016053.010.00.000.06053.00NaN0.0
2024-12-17 09:30:00.0016053.09.00.000.06053.00NaN0.0
# 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)
lastpricequantity
expirysymboltime
2024-12-20 13:30:00ES YOGVNNAOI1OH2024-12-17 09:30:00.0016052.751.0
2024-12-17 09:30:00.0026053.001.0
2024-12-17 09:30:00.0026053.002.0
2024-12-17 09:30:00.0036052.751.0
2024-12-17 09:30:00.0036052.753.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.

# 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 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.

Select Language:
class SliceHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 1)
        # Add some securities and datasets.
        future = self.add_future(Futures.Indices.SP_500_E_MINI)
        symbol = list(self.futures_chain(future.symbol))[0].symbol
        # 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

Continuous Contract Prices

To get historical trade, quote, or tick data for the continuous contract, use the symbol property of the Future object when you make the history request.

Select Language:
class FutureContinuousContractHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        # Add the Future and specify the contract rollover settings.
        future = self.add_future(
            Futures.Indices.SP_500_E_MINI,
            data_mapping_mode=DataMappingMode.OPEN_INTEREST,
            data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
            contract_depth_offset=0
        )
        # Get the 5 trailing daily TradeBar objects of the continuous contract. 
        history = self.history(TradeBar, future.symbol, 5, Resolution.DAILY)
closehighlowopenvolume
expirysymboltime
1899-12-30/ES2024-12-12 17:00:006132.5822566161.9356516129.2926516156.3686281152801.0
2024-12-13 17:00:006124.9908606158.8990936114.8690006151.3076981027682.0
2024-12-16 17:00:006150.5485586164.7191636138.6553726146.499814378263.0
2024-12-17 17:00:006125.7500006137.3901406114.3629076126.509140278531.0
2024-12-18 17:00:005939.5000006148.0000005906.5000006124.0000002009987.0
# Calculate the daily returns.
daily_returns = history.close.pct_change().iloc[1:]
expiry      symbol  time               
1899-12-30  /ES     2024-12-13 17:00:00   -0.001238
                    2024-12-16 17:00:00    0.004173
                    2024-12-17 17:00:00   -0.004032
                    2024-12-18 17:00:00   -0.030404
Name: close, dtype: float64

Open Interest

To get historical open interest data, call the history method with the OpenInterest type and a security's Symbol. This method returns a DataFrame with a single column.

Select Language:
class FutureOpenInterestHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        # Get the Symbol of a security.
        future = self.add_future(Futures.Indices.SP_500_E_MINI)
        symbol = list(self.futures_chain(future.symbol))[0].symbol
        # Get the 5 trailing daily OpenInterest objects of the security in DataFrame format. 
        history = self.history(OpenInterest, symbol, 5, Resolution.DAILY)
openinterest
expirysymboltime
2024-12-20 13:30:00ES YOGVNNAOI1OH2024-12-14 19:00:001631020.0
2024-12-15 19:00:001625312.0
2024-12-16 19:00:00965789.0
2024-12-17 19:00:00687472.0
2024-12-18 19:00:00556365.0
# Calculate the daily change in open interest.
oi_delta = history.openinterest.diff().iloc[1:]
expiry               symbol           time               
2024-12-20 13:30:00  ES YOGVNNAOI1OH  2024-12-15 19:00:00     -5708.0
                                      2024-12-16 19:00:00   -659523.0
                                      2024-12-17 19:00:00   -278317.0
                                      2024-12-18 19:00:00   -131107.0
Name: openinterest, dtype: float64

If you intend to use the data in the DataFrame to create OpenInterest 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 OpenInterest objects instead of a DataFrame, call the history[OpenInterest] method.

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

Contracts

To get historical Futures contracts, call the history method with the FutureUniverse type and the continuous contract Symbol. The data this method returns contains information on all the currently tradable contracts, not just the contracts that pass your filter. If you pass flatten=True, this method returns a DataFrame with columns for the data point attributes.

Select Language:
class FutureContractsHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        # Add a Future.
        future = self.add_future(Futures.Indices.SP_500_E_MINI)
        # Get the daily contract data over the last two trading days.
        history = self.history(FutureUniverse, future.symbol, 2, flatten=True)
closehighlowopenopeninterestvaluevolume
timesymbol
2024-12-18ES YOGVNNAOI1OH6052.0006063.5006040.7506052.750687472.06052.000278531.0
ES YQYHC5L1GPA96125.7506138.2506114.2506127.5001902280.06125.750997849.0
ES YTG30NVEFCW16185.2506198.7506173.5006184.2505044.06185.250197.0
ES YVXOP65RE0HT6238.2506258.0006229.3756238.25020.06238.2500.0
ES YYFADOG4CO3L6299.1256313.0006057.0006071.5001106.06299.1250.0
2024-12-19ES YOGVNNAOI1OH5872.2506074.5005840.0006050.250556365.05872.250335048.0
ES YQYHC5L1GPA95939.5006148.0005906.5006124.0002064361.05939.5002009987.0
ES YTG30NVEFCW16001.2506205.0005966.0006181.2505096.06001.250536.0
ES YVXOP65RE0HT6054.2506450.7505934.3756243.37520.06054.2500.0
ES YYFADOG4CO3L6315.0006315.0006315.0006315.0001116.06315.00010.0
ES Z0WW26QHBBPD6171.3756379.6256142.0006368.3750.06171.3750.0
# Select the contract with the largest open interest each day.
most_oi = history.groupby('time').apply(lambda x: x.nlargest(1, 'openinterest')).reset_index(level=1, drop=True).openinterest
time        symbol         
2024-12-18  ES YQYHC5L1GPA9    1902280.0
2024-12-19  ES YQYHC5L1GPA9    2064361.0
Name: openinterest, dtype: float64

If you intend to use the data in the DataFrame to create FutureUniverse 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 FutureUniverse objects instead of a DataFrame, call the history[FutureUniverse] method.

# Get the 5 trailing daily FutureUniverse objects in FutureUniverse format. 
history = self.history[FutureUniverse](future.symbol, 5, Resolution.DAILY)
# Iterate through the FutureUniverse objects and access their volumes.
for future_universe in history:
    t = future_universe.end_time
    most_oi = sorted(future_universe, key=lambda contract: contract.open_interest)[-1]

Contango

To get historical contango data for VX Futures, call the history method with the dataset Symbol. This method returns a DataFrame that contains the data point attributes.

Select Language:
class VIXContangoHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        # Add the dataset and save a reference to its Symbol.
        symbol = self.add_data(VIXCentralContango, "VIX", Resolution.DAILY).symbol
        # Get the trailing 7 days of contango data in DataFrame format.
        history = self.history(dataset_symbol, 7, Resolution.DAILY)
contango_f2_minus_f1contango_f7_minus_f4contango_f7_minus_f4_div_3f1f2f3f4f5f6f7f8frontmonthperiod
symboltime
VIX.VIXCentralContango2024-12-130.13140.04980.016614.406216.299117.155317.900518.246018.558318.792318.9500121 days
2024-12-140.12120.04970.016614.519016.278217.125017.947418.299318.575018.839019.0500121 days
2024-12-170.12990.04570.015214.699716.609117.459218.196318.496518.741119.028219.1884121 days
2024-12-180.07650.03890.013015.748316.953117.666618.299818.499618.718519.010819.1500121 days
# Calculate the changes in contango.
contango_diff = history.f1.diff().iloc[1:]
symbol                  time      
VIX.VIXCentralContango  2024-12-14    0.1128
                        2024-12-17    0.1807
                        2024-12-18    1.0486
Name: f1, dtype: float64

If you intend to use the data in the DataFrame to create VIXCentralContango 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 dataset objects instead of a DataFrame, call the history[VIXCentralContango] method.

# Get the trailing 7 days of contango data in VIXCentralContango format. 
history = self.history[VIXCentralContango](symbol, 7, Resolution.DAILY)
# Iterate through each VIXCentralContango object.
for data_point in history:
    t = data_point.end_time
    value = data_point.contango_f_2_minus_f_1

Indicators

To get historical indicator values, call the indicator_history method with an indicator and the security's Symbol.

Select Language:
class FuturesIndicatorHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        # Get the Symbol of a security.
        symbol = self.add_future(Futures.Indices.SP_500_E_MINI).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.

# Organize the historical indicator data into a DataFrame to enable pandas wrangling.
history_df = history.data_frame
currentrollingsum
2024-12-12 17:00:006150.577121129162.119546
2024-12-13 17:00:006154.322490129240.772299
2024-12-16 17:00:006162.919815129421.316116
2024-12-17 17:00:006169.413410129557.681602
2024-12-18 17:00:006166.050570129487.061970
# 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 instead of the close (or mid-price) of each bar, pass a selector argument.

Select Language:
# 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). In this case, pass a list of the Symbol objects to the method.

Select Language:
class FuturesMultiAssetIndicatorHistoryAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 12, 19)
        # Add the target and reference securities.
        target_symbol = self.add_future(Futures.Metals.GOLD).symbol
        reference_symbol = self.add_future(Futures.Indices.SP_500_E_MINI).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 some common practices for trading Futures with historical data.

Example 1: Stock-Gold All-Weather Portfolio

The following algorithm constructs a monthly rebalance risk-parity portfolio using S&P 500 EMini and Micro Gold Future contracts. A risk Parity portfolio can dissipate the risk of each crypto pair to reduce overall drawdown. The position sizing can be optimized by 1-year historical daily return of the universe members.

Select Language:
from Portfolio.RiskParityPortfolioOptimizer import RiskParityPortfolioOptimizer

class CryptoHistoricalDataAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        self.set_end_date(2025, 1, 1)
        self.set_cash(100000000)
        
        # Select the future contracts representing various asset classes to diversify the portfolio.
        self._futures = [self.add_future(
                x, 
                fill_forward=True, 
                extended_market_hours=True, 
                data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, 
                data_mapping_mode=DataMappingMode.OPEN_INTEREST, 
                contract_depth_offset=0
            ) for x in [Futures.Indices.SP_500_E_MINI, Futures.Metals.MICRO_GOLD]]

        # Instantiate the optimizer to perform risk-parity optimization.
        # Risk Parity portfolio can dissipate the risk of each crypto pair to reduce overall drawdown.
        self._optimizer = RiskParityPortfolioOptimizer(0.0, 1.0)

        # Set a scheduled event to rebalance the portfolio at the start of every month.
        self.schedule.on(
            self.date_rules.month_start(), 
            self.time_rules.at(9, 31),
            self.rebalance
        )

    def rebalance(self) -> None:
        # Historical data request to get 1-year data for optimization.
        history = self.history([x.symbol for x in self._futures], 253, Resolution.DAILY, extended_market_hours=True, fill_forward=True)\
            .droplevel([0]).unstack(0).close.dropna()
        # Daily return on the universe members to calculate the optimized weights.
        returns = history.pct_change().dropna()

        # Calculate the optimized weights.
        weights = self._optimizer.optimize(returns)

        # Rebalance the portfolio according to the optimized weights.
        targets = [PortfolioTarget(future.mapped, size) for future, size in zip(self._futures, weights)]
        self.set_holdings(targets, liquidate_existing_holdings=True)

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: