Overall Statistics
Total Orders
16
Average Win
8.47%
Average Loss
-3.03%
Compounding Annual Return
-15.415%
Drawdown
16.100%
Expectancy
-0.052
Start Equity
100000
End Equity
97285.6
Net Profit
-2.714%
Sharpe Ratio
-0.071
Sortino Ratio
-0.073
Probabilistic Sharpe Ratio
30.474%
Loss Rate
75%
Win Rate
25%
Profit-Loss Ratio
2.79
Alpha
0.459
Beta
1.44
Annual Standard Deviation
0.428
Annual Variance
0.183
Information Ratio
0.858
Tracking Error
0.36
Treynor Ratio
-0.021
Total Fees
$34.40
Estimated Strategy Capacity
$580000000.00
Lowest Capacity Asset
NQ XWWD1EKJDJWH
Portfolio Turnover
81.56%
Drawdown Recovery
20
from AlgorithmImports import *

class NqHourlySanityCheck(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2022, 1, 1)
        self.SetEndDate(2022, 3, 1)
        self.SetCash(100000)

        # Add the canonical/continuous future (chain)
        future = self.AddFuture(
            Futures.Indices.NASDAQ_100_E_MINI,
            Resolution.Hour,
            extendedMarketHours=True
        )

        # Filter to a small set of near-dated contracts
        future.SetFilter(0, 90)

        self.canonical = future.Symbol
        self.contract = None

        self.ema_fast = None
        self.ema_slow = None

    def OnData(self, slice: Slice):
        # Get the chain for the canonical symbol
        chain = slice.FutureChains.get(self.canonical)
        if chain is None:
            return

        # Choose the nearest expiry contract (front month)
        contracts = sorted(chain, key=lambda x: x.Expiry)
        if len(contracts) == 0:
            return

        front = contracts[0].Symbol

        # If contract changed (roll), update indicators
        if self.contract != front:
            self.contract = front
            self.Log(f"Front contract: {self.contract}")

            self.ema_fast = self.EMA(self.contract, 12, Resolution.Hour)
            self.ema_slow = self.EMA(self.contract, 48, Resolution.Hour)

        if self.ema_fast is None or self.ema_slow is None:
            return
        if not (self.ema_fast.IsReady and self.ema_slow.IsReady):
            return

        # Simple crossover just to validate trading
        invested = self.Portfolio.Invested

        if self.ema_fast.Current.Value > self.ema_slow.Current.Value:
            if not invested:
                self.MarketOrder(self.contract, 1)   # long 1 contract
        else:
            if invested:
                self.Liquidate()