Overall Statistics
Total Orders
113
Average Win
0.46%
Average Loss
-0.22%
Compounding Annual Return
1.143%
Drawdown
3.400%
Expectancy
0.202
Start Equity
1000000
End Equity
1026898.42
Net Profit
2.690%
Sharpe Ratio
-0.353
Sortino Ratio
-0.414
Probabilistic Sharpe Ratio
15.943%
Loss Rate
61%
Win Rate
39%
Profit-Loss Ratio
2.06
Alpha
-0.003
Beta
-0.019
Annual Standard Deviation
0.017
Annual Variance
0
Information Ratio
-0.829
Tracking Error
0.203
Treynor Ratio
0.319
Total Fees
$279.11
Estimated Strategy Capacity
$9400000000.00
Lowest Capacity Asset
CL XON0163K9O75
Portfolio Turnover
0.67%
from AlgorithmImports import *
from QuantConnect.DataSource import *

class USFuturesSecurityMasterDataClassicAlgorithm(QCAlgorithm):
    """
    This algorithm demonstrates the use of the US futures security master to trade a continuous futures contract 
    based on its price relative to a simple moving average (SMA) and considers market volatility using ATR. 
    It goes long when the price is above the SMA by a certain threshold and goes short when it is below by the same threshold. 
    The algorithm also handles contract rollovers and logs when these events occur.
    """
    
    threshold = 0.01  # Define a threshold for price deviation from the SMA (1%)
    
    def Initialize(self) -> None:
        self.SetCash(1000000)  # Set starting cash
        self.SetStartDate(2019, 2, 1)  # Set start date for the backtest
        self.SetEndDate(2021, 6, 1)  # Set end date for the backtest

        # Request continuous future data for Crude Oil WTI with specific data normalization and mapping modes.
        self.continuous_contract = self.AddFuture(Futures.Energies.CrudeOilWTI,
                                                  DataNormalizationMode.BackwardsRatio,
                                                  DataMappingMode.OpenInterest,
                                                  contractDepthOffset=0)
        self.symbol = self.continuous_contract.Symbol  # Get the continuous contract symbol
                      
        # Initialize SMA and ATR indicators
        max_lookback = max(110, 20)  # Determine the max lookback period for indicator warming
        self.sma = self.SMA(self.symbol, 110, Resolution.Daily)
        self.atr = self.ATR(self.symbol, 20, MovingAverageType.Simple, Resolution.Daily)

        # Request historical data to warm up the indicators
        history = self.History(self.symbol, max_lookback, Resolution.Daily)
        if not history.empty:
            for time, row in history.iterrows():
                self.sma.Update(IndicatorDataPoint(time, row.close))
                self.atr.Update(IndicatorDataPoint(time, row.close))
        
    def OnData(self, slice: Slice) -> None:
        mapped_symbol = self.continuous_contract.Mapped  # The currently mapped contract symbol
        
        # Ensure there is data and that both indicators are ready
        if not (slice.Bars.ContainsKey(self.symbol) and self.sma.IsReady and self.atr.IsReady):
            return
        
        # Log indicator values
        self.Log(f"SMA: {self.sma.Current.Value}, ATR: {self.atr.Current.Value}")

        # Trading logic
        current_price = slice.Bars[self.symbol].Price
        if current_price > self.sma.Current.Value * (1 + self.threshold) and not self.Portfolio[mapped_symbol].IsLong:
            self.MarketOrder(mapped_symbol, 1)
        elif current_price < self.sma.Current.Value * (1 - self.threshold) and not self.Portfolio[mapped_symbol].IsShort:
            self.MarketOrder(mapped_symbol, -1)

        # Handle contract rollovers
        for symbol, changed_event in slice.SymbolChangedEvents.items():
            self.HandleRollover(changed_event)

    def HandleRollover(self, changed_event):
        old_symbol = changed_event.OldSymbol
        new_symbol = changed_event.NewSymbol
        tag = f"Rollover - Symbol changed at {self.Time}: {old_symbol} -> {new_symbol}"
        quantity = self.Portfolio[old_symbol].Quantity
        self.Liquidate(old_symbol, tag=tag)
        if quantity != 0:
            self.MarketOrder(new_symbol, quantity, tag=tag)
        self.Log(tag)
from AlgorithmImports import *
from QuantConnect.DataSource import *

class USFuturesSecurityMasterDataClassicAlgorithm (QCAlgorithm):
    """
    This algorithm demonstrates the use of the US futures security master to trade a continuous futures contract 
    based on its price relative to a simple moving average (SMA). It goes long when the price is above the SMA 
    by a certain threshold and goes short when the price is below the SMA by the same threshold. The algorithm 
    also handles contract rollovers and logs when these events occur.
    """
    
    threshold = 0.01  # Define a threshold for price deviation from the SMA (1%)
    
    def Initialize(self) -> None:
        """
        Initialize the algorithm settings, add the future contract, and set up the indicators.
        """
        self.SetCash(1000000)  # Set starting cash
        self.SetStartDate(2019, 2, 1)  # Set start date for the backtest
        self.SetEndDate(2021, 6, 1)  # Set end date for the backtest

        # Request continuous future data for Crude Oil WTI with specific data normalization and mapping modes.
        self.continuous_contract = self.AddFuture(Futures.Energies.CrudeOilWTI,
                                                  resolution= Resolution.DAILY,
                                                  dataNormalizationMode=DataNormalizationMode.BackwardsRatio,
                                                  dataMappingMode=DataMappingMode.OpenInterest,
                                                  contractDepthOffset=0)
        self.symbol = self.continuous_contract.Symbol  # Get the continuous contract symbol
                      
        # Request historical data to warm up the SMA indicator
        history = self.History(self.symbol, 40, Resolution.DAILY)
        self.Debug(f"We got {len(history)} items from our history request")
        
        # Initialize a 10-period SMA indicator with daily resolution
        self.sma = self.SMA(self.symbol, 20, Resolution.Daily)
        # Warm up the SMA with historical data
        if not history.empty:
            for time, row in history.droplevel(0).loc[self.symbol].iterrows():
                self.sma.Update(IndicatorDataPoint(time, row.close))
        
        # Initialize a 20-period ATR Indicator with daily resolution
        self.atr = self.atr(self.symbol, 20, Resolution.DAILY)
        self.dch = self.dch(self.symbol, 20, 10)

        

    def OnData(self, slice: Slice) -> None:
        """
        Event handler for new data. Checks for rollover events and trading signals based on the SMA.
        """
        # Check for contract rollover events and handle them
        for symbol, changed_event in slice.SymbolChangedEvents.items():
            old_symbol = changed_event.OldSymbol  # The symbol of the old contract
            new_symbol = changed_event.NewSymbol  # The symbol of the new contract
            tag = f"Rollover - Symbol changed at {self.Time}: {old_symbol} -> {new_symbol}"
            quantity = self.Portfolio[old_symbol].Quantity  # Quantity held of the old contract

            # Liquidate the old contract position and open a new position in the new contract if necessary
            self.Liquidate(old_symbol, tag=tag)
            if quantity != 0: self.MarketOrder(new_symbol, quantity, tag=tag)
            self.Log(tag)
                
        mapped_symbol = self.continuous_contract.Mapped  # The currently mapped contract symbol

        # Check if there is a price update for the continuous contract and the SMA is ready
        if not (slice.Bars.ContainsKey(self.symbol) and self.sma.IsReady and self.atr.IsReady and self.dch and mapped_symbol):
            return

        # Update ATR with the new data
        # bar = slice.Bars[self.symbol]
        # self.atr.Update(bar)
        # self.dch.Update(bar)

        current_price = slice.Bars[self.symbol].Price  # The current price of the continuous contract
        sma_value = self.sma.Current.Value 
        atr_value = self.atr.Current.Value
        dch_upper_value = self.dch.UpperBand.Current.Value
        dch_lower_value = self.dch.LowerBand.Current.Value
        self.log(f"PRICE: {current_price}")
        self.log(f"SMA VALUE: {sma_value}")
        self.log(f"ATR VALUE: {atr_value}")
        self.log(f"DCH UPPER VALUE: {dch_upper_value}")
        self.log(f"DCH LOWER VALUE: {dch_lower_value}")
        

        
        
        # Trading logic based on the relationship between the current price and the SMA
        current_price = slice.Bars[self.symbol].Price  # The current price of the continuous contract
        sma_value = self.sma.Current.Value  # The current value of the SMA
        # If the current price is significantly above the SMA, and we're not already long, go long
        if current_price > sma_value * (1 + self.threshold) and not self.Portfolio[mapped_symbol].IsLong:
            self.MarketOrder(mapped_symbol, 1)
        # If the current price is significantly below the SMA, and we're not already short, go short
        elif current_price < sma_value * (1 - self.threshold) and not self.Portfolio[mapped_symbol].IsShort:
            self.MarketOrder(mapped_symbol, -1)
from AlgorithmImports import *

class BasicTemplateFutureRolloverAlgorithm(QCAlgorithm):
    """
    An example algorithm demonstrating trading with continuous futures contracts
    and handling contract rollovers.

    This class Serves as the core algorithm class that initializes the trading environment, 
    subscribes to futures data, and handles incoming data points for decision making.
    """
    
    def initialize(self):
        """
        Set up the initial conditions of the algorithm including start/end dates,
        cash balance, and the futures contracts to trade. Map futures symbols to SymbolData objects for managing trading data.
        """
        self.set_start_date(2013, 1, 1)  # Start date of the algorithm
        self.set_end_date(2015, 1, 1)   # End date of the algorithm
        self.set_cash(1000000)            # Starting cash

        self._symbol_data_by_symbol = {}

        # Define futures to be traded
        futures = [Futures.Indices.SP_500_E_MINI
                   ]  

        for future in futures:
            # Add futures contract with specific settings for resolution and market hours
            continuous_contract = self.add_future(
                future,
                resolution=Resolution.DAILY,
                extended_market_hours=False,
                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

    def on_data(self, slice):
        """
        The primary event handler for incoming market data.
        This method that is called with every new data point (slice)
        It processes each symbol's data using SymbolData methods.
        

        In this particular alorithm, it makes trading decisions based on the exponential moving average (EMA) and current price.
        Args:
        slice: A Slice object containing the current market data.
        """
        for symbol, symbol_data in self._symbol_data_by_symbol.items():
            # Update the symbol data with the latest market data slice
            symbol_data.update(slice)
            
            # Skip processing if the symbol data isn't ready or if there's no new data for this symbol
            if not symbol_data.is_ready or not slice.bars.contains_key(symbol):
                continue
            # Get the current value of the exponential moving average (EMA) for this symbol
            ema_current_value = symbol_data.EMA.current.value

            # If the EMA is below the current price and we are not already in a long position,
            # place a buy order to go long on the symbol.
            if ema_current_value < symbol_data.price and not symbol_data.is_long:
                self.market_order(symbol_data.mapped, 1)
            # Conversely, if the EMA is above the current price and we are not already in a short position,
            # place a sell order to go short on the symbol.
            elif ema_current_value > symbol_data.price and not symbol_data.is_short:
                self.market_order(symbol_data.mapped, -1)

class SymbolData:
    """
    Handles and encapsulates all necessary data and operations for each single futures symbol
    within a multi-security algorithm, facilitating the management of state and indicators.
    """
    
    def __init__(self, algorithm, future):
        """
        Initializes a new instance of SymbolData to manage a specific future.
        Instantiates a new SymbolData object with a reference to the algorithm and the associated future.
        
        Args:
        algorithm: The instance of the algorithm using this SymbolData.
        future: The future contract associated with this SymbolData.
        """
        self._algorithm = algorithm
        self._future = future
        self.EMA = algorithm.ema(future.symbol, 100, Resolution.DAILY)
        self.price = 0
        self.is_long = False
        self.is_short = False
        self.reset()
    
    @property
    def symbol(self):
        """Returns the symbol of the future."""
        return self._future.symbol
    
    @property
    def mapped(self):
        """Returns the mapped symbol of the future for current trading."""
        return self._future.mapped
    
    @property
    def is_ready(self):
        """Check if the symbol and EMA are ready for trading."""
        return self.mapped is not None and self.EMA.is_ready
    
    def update(self, slice):
        """
        Updates symbol data with new market data and handles symbol changes due to contract rollovers.
        Adjusts positions based on the new symbol mapping.
        
        Args:
        slice: A slice of new market data.
        """
        if slice.symbol_changed_events.contains_key(self.symbol):
            # Check if there is a rollover event for the current symbol in the data slice
            changed_event = slice.symbol_changed_events[self.symbol]
            old_symbol = changed_event.old_symbol # Extract the old symbol from the rollover event
            new_symbol = changed_event.new_symbol # Extract the new symbol to which we are rolling over

            # Create a tag for logging that includes the time of rollover and the symbols involved
            tag = f"Rollover - Symbol changed at {self._algorithm.time}: {old_symbol} -> {new_symbol}"

            # Get the current position quantity for the old symbol to replicate in the new symbol
            quantity = self._algorithm.portfolio[old_symbol].quantity

            # Liquidate the existing position in the old symbol
            self._algorithm.liquidate(old_symbol, tag=tag)

            # Create a new position in the new symbol with the same quantity as the old symbol
            self._algorithm.market_order(new_symbol, quantity, tag=tag)

            # Reset the symbol data to clear any state specific to the old contract
            self.reset()
        
        # Update the current price with the latest price from the slice, or keep the old if no new data
        self.price = slice.bars[self.symbol].price if slice.bars.contains_key(self.symbol) else self.price

        # Update the long and short position flags based on the current portfolio state
        self.is_long = self._algorithm.portfolio[self.mapped].is_long # Check if the current position is long
        self.is_short = self._algorithm.portfolio[self.mapped].is_short # Check if the current position is short
        
    def reset(self):
        """Resets and warms up indicators for the newly mapped contracts."""
        self.EMA.reset()
        self._algorithm.warm_up_indicator(self.symbol, self.EMA, Resolution.DAILY)
        
    def dispose(self):
        """Frees up resources by resetting the EMA and other indicators."""
        self.EMA.reset()