Overall Statistics
Total Trades
11
Average Win
13.89%
Average Loss
-0.73%
Compounding Annual Return
23.912%
Drawdown
15.000%
Expectancy
11.067
Net Profit
23.964%
Sharpe Ratio
1.009
Probabilistic Sharpe Ratio
46.702%
Loss Rate
40%
Win Rate
60%
Profit-Loss Ratio
19.11
Alpha
0.082
Beta
0.604
Annual Standard Deviation
0.184
Annual Variance
0.034
Information Ratio
0.08
Tracking Error
0.178
Treynor Ratio
0.307
Total Fees
$353.21
Estimated Strategy Capacity
$1200000.00
Lowest Capacity Asset
CL XX0ATS17OJB5
Portfolio Turnover
2.13%
#region imports
from AlgorithmImports import *
#endregion
# region imports

# Import the libraries
from QuantConnect.Data.UniverseSelection import *
from datetime import timedelta
from QuantConnect.Algorithm.Framework.Risk import *

# Define the class
class MomentumFuturesAlgorithm(QCAlgorithm):

    def Initialize(self):
        # Set the start and end dates
        self.SetStartDate(2020,12, 1)
        self.SetEndDate(2021, 12, 1)
        
        # Set the cash amount
        self.SetCash(1000000)
        # self.contractSymbol=None
        # Add a futures contract
        self.future = self.AddFuture(Futures.Energies.CrudeOilWTI, Resolution.Hour)
        # self.sym = future1.Symbol
        # Set the filter for the futures contract
        self.future.SetFilter(timedelta(0), timedelta(182))
        # benchmark = self.AddEquity("SPY")
        # self.SetBenchmark(benchmark.Symbol)
       
        # Define the trade size
        self.tradeSize = 0.062
        
        # Define the entry and exit thresholds
        self.entryThreshold = 40
        self.exitThreshold = 70
        
        # Define the stop loss percentage
        self.stopLossPct = 0.005
        
        # Define the roll forward date as 2 days before expiration
        self.rollForwardDate = timedelta(2)
        
        # Initialize the variables for holding the contract and the order ticket
        self.contract = None

        self.isLong = 0


        
        # Add a maximum drawdown risk management model with 15% threshold
        # self.SetRiskManagement(MaximumDrawdownPercentPortfolio(0.01))
        # self.SetExecution(ImmediateExecutionModel())
        
        # Add a historical value at risk risk management model with 5% threshold and 99% confidence level
        # self.SetRiskManagement(HistoricalVaR(0.05, 0.99))

    def OnData(self,slice):

        # hist = self.History([Futures.Energies.CrudeOilWTI],self.Time-timedelta(241),self.Time,Resolution.Hour)
        # hist = hist['close'].unstack(level =0).pct_change().dropna()
        # var = np.quantile(hist,0.01)
        # self.Log(var)
        
        # Check if we have any open positions
        if not self.Portfolio.Invested:
            # self.contract = [contract for chain in slice.FutureChains.Values for contract in chain.Contracts.Values]
            # self.contract = sorted(self.contract, key = lambda x: x.Expiry, reverse=False)
            for chain in slice.FutureChains:
                contracts = list(filter(lambda x: x.Expiry > self.Time, chain.Value))
                if len(contracts) == 0:
                    continue
                front = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[0]
                self.contract = front

                price = slice[front.Symbol].Close
                if self.RSI(front.Symbol, 20, Resolution.Hour).Current.Value < self.entryThreshold:
                    # Enter a long position and place a limit order to exit at a higher price
                    self.SetHoldings(front.Symbol, self.tradeSize)
                    self.isLong = 1
                    
                elif self.RSI(front.Symbol, 20, Resolution.Hour).Current.Value > self.exitThreshold:
                    # Enter a short position and place a limit order to exit at a lower price
                    self.SetHoldings(front.Symbol, -self.tradeSize)
                    self.isLong = -1
        
        else:
            # Get the current price of the contract
            for chain in slice.FutureChains:
                contracts = list(filter(lambda x: x.Expiry > self.Time, chain.Value))
                if len(contracts) == 0:
                    continue
                front = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[-1]
                # self.contractSymbol = front.Symbol
                price = slice[front.Symbol].Close

                
            
            # Check if we need to exit the position based on the momentum indicator or the stop loss
                if (self.Portfolio[front.Symbol].IsLong and self.RSI(front.Symbol, 20, Resolution.Hour).Current.Value > self.exitThreshold) or \
                    (self.Portfolio[front.Symbol].IsShort and self.RSI(front.Symbol, 20, Resolution.Hour).Current.Value < self.entryThreshold) or \
                    (self.Portfolio[front.Symbol].UnrealizedProfitPercent < -self.stopLossPct):
                
                # Liquidate the position and cancel any open orders
                    self.Liquidate(front.Symbol)
                    self.isLong = 0
                
                # Log the exit signal and the price
                    #self.Log("Exit signal: {0}, Price: {1}".format(self.mom.Current.Value, price))
                
                # Reset the contract and the order ticket variables
                    self.contract = None
                elif self.Time + self.rollForwardDate > self.contract.Expiry:
                    self.Liquidate(self.contract.Symbol)
                    self.contract = front
                    self.SetHoldings(front.Symbol, self.isLong * self.tradeSize)