Overall Statistics
Total Trades
48
Average Win
3.82%
Average Loss
-2.30%
Compounding Annual Return
85.107%
Drawdown
17.200%
Expectancy
0.663
Net Profit
85.016%
Sharpe Ratio
2.083
Probabilistic Sharpe Ratio
77.214%
Loss Rate
38%
Win Rate
62%
Profit-Loss Ratio
1.66
Alpha
0.193
Beta
2.028
Annual Standard Deviation
0.286
Annual Variance
0.082
Information Ratio
1.856
Tracking Error
0.214
Treynor Ratio
0.293
Total Fees
$292.40
Estimated Strategy Capacity
$200000000.00
Lowest Capacity Asset
ES XZDYPWUWC7I9
Portfolio Turnover
54.05%
# region imports
from AlgorithmImports import *
from scipy.stats import entropy
import numpy as np
# endregion

class SquareMagentaHamster(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2021, 1, 1)  # Set Start Date
        self.SetEndDate(2022, 1, 1)
        self.SetCash(100000)  # Set Strategy Cash

        self.ShortcontractSymbol = None
        self.LongcontractSymbol = None

        self.future = self.AddFuture(Futures.Indices.SP500EMini, Resolution.Hour, extendedMarketHours= True)
        self.future.SetFilter(timedelta(0), timedelta(182))
        self.symbol = self.future.Symbol

        self.lookback = 30
        self.priceWindow = RollingWindow[QuoteBar](self.lookback)

    def OnData(self, data: Slice):

        if not(data.ContainsKey(self.symbol) and data[self.symbol] is not None):
            return

        self.priceWindow.Add(data[self.symbol])

        if not self.priceWindow.IsReady:
            return

        self.closingPrices = [bar.Close for bar in self.priceWindow]


        if self.HurstExponent() > 0.5 and self.Entropy() > 2.6:
            if self.Portfolio.Invested:
                if self.ShortcontractSymbol != None:
                    self.Log("covered short for long EMA")
                    self.BuyFuture(data)

                elif self.LongcontractSymbol != None:
                    return
            else:
                self.BuyFuture(data)
                self.Log("Bought future")
 
        elif self.HurstExponent() < 0.5 and self.Entropy() < 2.6:
            if self.Portfolio.Invested:
                if self.LongcontractSymbol != None:
                    self.Log("sold long for short EMA")
                    self.ShortFuture(data)

                elif self.ShortcontractSymbol != None:
                    return
            else:
                self.ShortFuture(data)
                self.Log("Short future")

        self.Plot("Hurst", "Value", self.HurstExponent())
        self.Plot("Entropy", "value", self.Entropy())
        #self.Log(self.Entropy())

    def HurstExponent(self):
        
        #Hurst Exponent = (log(Rn/S) / log(n)) - 0.5
        #where Rn is the range of the cumulative sum of n data points and S is the standard deviation of the n data points

        prices = np.array([self.closingPrices])

        Rn = np.cumsum(prices)
        S = np.std(prices)

        hursts = ((np.log(Rn/S)) / np.log(prices)) - 0.5

        return hursts[-1][-1]

    def Entropy(self):

        base = 2
        value,counts = np.unique(self.closingPrices, return_counts=True)
        return entropy(counts, base=base)

    def ShortFuture(self, data: Slice):
 
        for chain in data.FutureChains:
            # Get contracts expiring no earlier than in 90 days
            contracts = list(filter(lambda x: x.Expiry > self.Time + timedelta(90), chain.Value))
 
            # if there is any contract, trade the front contract
            if len(contracts) == 0: continue
            self.ShortcontractSymbol = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[0]
 
            #self.ShortcontractSymbol = front
            self.SetHoldings(self.ShortcontractSymbol.Symbol, -0.20, True)
 
        return
 
    def BuyFuture(self, data: Slice):
 
        for chain in data.FutureChains:
            # Get contracts expiring no earlier than in 90 days
            contracts = list(filter(lambda x: x.Expiry > self.Time + timedelta(90), chain.Value))
 
            # if there is any contract, trade the front contract
            if len(contracts) == 0: continue
            self.LongcontractSymbol = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[0]
 
            #self.LongcontractSymbol = front
            self.SetHoldings(self.LongcontractSymbol.Symbol, 0.20, True)
 
        return