Overall Statistics
Total Orders
239
Average Win
0.32%
Average Loss
-0.13%
Compounding Annual Return
1.022%
Drawdown
2.700%
Expectancy
0.141
Start Equity
1000000
End Equity
1024007.2
Net Profit
2.401%
Sharpe Ratio
-0.404
Sortino Ratio
-0.548
Probabilistic Sharpe Ratio
12.947%
Loss Rate
67%
Win Rate
33%
Profit-Loss Ratio
2.48
Alpha
-0.004
Beta
-0.021
Annual Standard Deviation
0.017
Annual Variance
0
Information Ratio
-0.833
Tracking Error
0.203
Treynor Ratio
0.338
Total Fees
$590.33
Estimated Strategy Capacity
$2800000000.00
Lowest Capacity Asset
CL XON0163K9O75
Portfolio Turnover
1.41%
from AlgorithmImports import *
from QuantConnect.DataSource import *

class USFuturesSecurityMasterDataClassicAlgorithm (QCAlgorithm):
    
    threshold = 0.01 # 1%
    
    def initialize(self) -> None:
        self.set_cash(1000000)
        self.set_start_date(2019, 2, 1)
        self.set_end_date(2021, 6, 1)

        # Requesting data
        self.continuous_contract = self.add_future(Futures.Energies.CRUDE_OIL_WTI,
                                                  data_normalization_mode = DataNormalizationMode.BACKWARDS_RATIO,
                                                  data_mapping_mode = DataMappingMode.OPEN_INTEREST,
                                                  contract_depth_offset = 0)
        self.continuous_contract_symbol = self.continuous_contract.symbol
                      
        # Historical data
        history = self.history(self.continuous_contract_symbol, 500, Resolution.MINUTE)
        self.debug(f"We got {len(history)} items from our history request")
        
        self.sma = self.SMA(self.continuous_contract_symbol, 10, Resolution.DAILY)
        if not history.empty:
            for time, row in history.droplevel(0).loc[self.continuous_contract_symbol].iterrows():
                self.sma.update(IndicatorDataPoint(time, row.close))
        

    def on_data(self, slice: Slice) -> None:
        # Accessing data
        for symbol, changed_event in slice.symbol_changed_events.items():
            old_symbol = changed_event.old_symbol
            new_symbol = changed_event.new_symbol
            tag = f"Rollover - Symbol changed at {self.time}: {old_symbol} -> {new_symbol}"
            quantity = self.portfolio[old_symbol].quantity

            # Rolling over: to liquidate any position of the old mapped contract and switch to the newly mapped contract
            self.liquidate(old_symbol, tag = tag)
            if quantity != 0: self.market_order(new_symbol, quantity, tag = tag)
            self.log(tag)
                
        mapped_symbol = self.continuous_contract.mapped

        if not (slice.bars.contains_key(self.continuous_contract_symbol) and self.sma.is_ready and mapped_symbol):
            return
        
        if slice.bars[self.continuous_contract_symbol].price > self.sma.current.value * (1+self.threshold) and not self.portfolio[mapped_symbol].is_long:
            self.market_order(mapped_symbol, 1)
        elif slice.bars[self.continuous_contract_symbol].price < self.sma.current.value * (1-self.threshold) and not self.portfolio[mapped_symbol].is_short:
            self.market_order(mapped_symbol, -1)