Overall Statistics
Total Trades
5
Average Win
30.29%
Average Loss
0%
Compounding Annual Return
92.786%
Drawdown
15.800%
Expectancy
0
Net Profit
92.685%
Sharpe Ratio
2.441
Probabilistic Sharpe Ratio
85.622%
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
0.191
Beta
2.206
Annual Standard Deviation
0.257
Annual Variance
0.066
Information Ratio
2.659
Tracking Error
0.161
Treynor Ratio
0.284
Total Fees
$10.75
Estimated Strategy Capacity
$580000.00
Lowest Capacity Asset
ES XUERCWA6EWAP
Portfolio Turnover
3.33%
# 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]


        hurst_exponent = self.HurstExponent()
        entropy = self.Entropy()

        self.Log(str(hurst_exponent[-1]))
        self.Log(str(hurst_exponent))

        if hurst_exponent[-1] > 0.5 and 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 hurst_exponent[-1] < 0.5 and 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", hurst_exponent[0])
        #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)
        n = len(prices)

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

        return hursts


    def Entropy(self):

        base = 2
        value,counts = np.unique(self.closingPrices, return_counts=True)
        probs = counts / np.sum(counts)

        return entropy(probs, 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