Skip to content
All library documents

Managing Continuous Futures Rolls with an EMA Direction Rule

Article Strategy library · Author: QuantConnect

Summary

This example outlines a continuous-futures algorithm using a daily S&P 500 E-mini series. It selects backward-ratio price normalization and open-interest contract mapping, then uses a 20-period EMA comparison with price to choose a long or short position in the currently mapped contract. Per-symbol state is held in a helper object, which updates the price and portfolio direction as new data arrives.

When the mapped contract changes, the example liquidates the old contract and opens the replacement with the prior quantity. It then resets and warms the EMA for the continuous symbol. This illustrates how signal state and positions can be handled across a futures roll, rather than presenting a tested trading edge. The sample covers a short historical date window and provides no performance analysis. Its rollover sequence and indicator handling are implementation examples; users should verify platform behavior and order quantities for their own algorithm and data setup.

Key ideas

  • The example maps a continuous E-mini future by open interest and uses backward-ratio normalization.
  • A daily EMA and price comparison determines whether the algorithm seeks long or short exposure.
  • A symbol-change event triggers liquidation of the old contract and transfer of its quantity to the new one.
  • The EMA is reset and warmed after the mapped contract changes.
  • The document demonstrates algorithm structure but does not provide evidence of trading performance.

Tags

Full text
# BasicTemplateFutureRolloverAlgorithm


# BasicTemplateFutureRolloverAlgorithm









Example algorithm for trading continuous future Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here. Slice object keyed by symbol containing the stock data Abstracted class object to hold information (state, indicators, methods, etc.) from a Symbol/Security in a multi-security algorithm Constructor to instantiate the information needed to be hold Handler of new slice of data received reset RollingWindow/indicator to adapt to newly mapped contract, then warm up the RollingWindow/indicator disposal method to remove consolidator/update method handler, and reset RollingWindow/indicator to free up memory and speed

## Source (Apache-2.0)

```python
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from AlgorithmImports import *

### <summary>
### Example algorithm for trading continuous future
### </summary>
class BasicTemplateFutureRolloverAlgorithm(QCAlgorithm):

    ### <summary>
    ### Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
    ### </summary>
    def initialize(self):
        self.set_start_date(2013, 10, 8)
        self.set_end_date(2013, 12, 10)
        self.set_cash(1000000)

        self._symbol_data_by_symbol = {}
        
        futures = [
            Futures.Indices.SP_500_E_MINI
        ]

        for future in futures:
            # Requesting data
            continuous_contract = self.add_future(future,
                resolution = Resolution.DAILY,
                extended_market_hours = True,
                data_normalization_mode = DataNormalizationMode.BACKWARDS_RATIO,
                data_mapping_mode = DataMappingMode.OPEN_INTEREST,
                contract_depth_offset = 0
            )
            
            symbol_data = SymbolData(self, continuous_contract)
            self._symbol_data_by_symbol[continuous_contract.symbol] = symbol_data

    ### <summary>
    ### on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here.
    ### </summary>
    ### <param name="slice">Slice object keyed by symbol containing the stock data</param>
    def on_data(self, slice):
        for symbol, symbol_data in self._symbol_data_by_symbol.items():
            # Call SymbolData.update() method to handle new data slice received
            symbol_data.update(slice)
            
            # Check if information in SymbolData class and new slice data are ready for trading
            if not symbol_data.is_ready or not slice.bars.contains_key(symbol):
                return
            
            ema_current_value = symbol_data.EMA.current.value
            if ema_current_value < symbol_data.price and not symbol_data.is_long:
                self.market_order(symbol_data.mapped, 1)
            elif ema_current_value > symbol_data.price and not symbol_data.is_short:
                self.market_order(symbol_data.mapped, -1)
    
### <summary>
### Abstracted class object to hold information (state, indicators, methods, etc.) from a Symbol/Security in a multi-security algorithm
### </summary>
class SymbolData:
    
    ### <summary>
    ### Constructor to instantiate the information needed to be hold
    ### </summary>
    def __init__(self, algorithm, future):
        self._algorithm = algorithm
        self._future = future
        self.EMA = algorithm.ema(future.symbol, 20, Resolution.DAILY)
        self.price = 0
        self.is_long = False
        self.is_short = False

        self.reset()
    
    @property
    def symbol(self):
        return self._future.symbol
    
    @property
    def mapped(self):
        return self._future.mapped
    
    @property
    def is_ready(self):
        return self.mapped is not None and self.EMA.is_ready
    
    ### <summary>
    ### Handler of new slice of data received
    ### </summary>
    def update(self, slice):
        if slice.symbol_changed_events.contains_key(self.symbol):
            changed_event = slice.symbol_changed_events[self.symbol]
            old_symbol = changed_event.old_symbol
            new_symbol = changed_event.new_symbol
            tag = f"Rollover - Symbol changed at {self._algorithm.time}: {old_symbol} -> {new_symbol}"
            quantity = self._algorithm.portfolio[old_symbol].quantity

            # Rolling over: to liquidate any position of the old mapped contract and switch to the newly mapped contract
            self._algorithm.liquidate(old_symbol, tag = tag)
            self._algorithm.market_order(new_symbol, quantity, tag = tag)

            self.reset()
        
        self.price = slice.bars[self.symbol].price if slice.bars.contains_key(self.symbol) else self.price
        self.is_long = self._algorithm.portfolio[self.mapped].is_long
        self.is_short = self._algorithm.portfolio[self.mapped].is_short
        
    ### <summary>
    ### reset RollingWindow/indicator to adapt to newly mapped contract, then warm up the RollingWindow/indicator
    ### </summary>
    def reset(self):
        self.EMA.reset()
        self._algorithm.warm_up_indicator(self.symbol, self.EMA, Resolution.DAILY)
            
    ### <summary>
    ### disposal method to remove consolidator/update method handler, and reset RollingWindow/indicator to free up memory and speed
    ### </summary>
    def dispose(self):
        self.EMA.reset()

```

Shown in full with attribution under the source's licence. Licence: Apache-2.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.