Overall Statistics
Total Trades
31
Average Win
3.67%
Average Loss
-3.46%
Compounding Annual Return
83.236%
Drawdown
25.100%
Expectancy
0.177
Net Profit
11.550%
Sharpe Ratio
1.637
Probabilistic Sharpe Ratio
53.608%
Loss Rate
43%
Win Rate
57%
Profit-Loss Ratio
1.06
Alpha
1.055
Beta
-1.76
Annual Standard Deviation
0.516
Annual Variance
0.266
Information Ratio
1.148
Tracking Error
0.631
Treynor Ratio
-0.48
Total Fees
$57.35
class FuturesMovingAverageCrossOverExample2(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2018, 1, 1)    #Set Start Date
        self.SetEndDate(2018, 3,6)      #Set End Date
        self.SetCash(100000)             #Set Strategy Cash
        self.SetTimeZone('America/Los_Angeles') # Set timezone
        self.reset = True
        self.SymbolData = { }
        self.AddEquity("SPY", Resolution.Minute)
        futureES = self.AddFuture(Futures.Indices.SP500EMini)
        futureES.SetFilter(0, 360)
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Tuesday), self.TimeRules.AfterMarketOpen("SPY", 10), self.ScheduleDemo)
        
    def OnData(self, slice):
        # Reset any open positions based on a contract rollover.
        if self.reset:
            self.reset = False
            self.Log('RESET: closing all positions')
            self.Liquidate()
    
 
    def OnSecuritiesChanged(self, changes):
        for added in changes.AddedSecurities:
            # Only trade Futures
            if added.Type != SecurityType.Future:
                continue
            
            symbol = added.Symbol
            if symbol not in self.SymbolData:
                macd = self.MACD(symbol, 12, 26, 9, MovingAverageType.Exponential, Resolution.Minute)
                self.SymbolData[symbol] = AssetData(macd)

        for removed in changes.RemovedSecurities:
            assetData = self.SymbolData.pop(removed.Symbol, None)
            if assetData:
                self.Liquidate(removed.Symbol)
                assetData.Dispose()


    def ScheduleDemo(self):
        tolerance = 0.003
        for symbol, assetData in self.SymbolData.items():

            security = self.Securities[symbol]
            # Only new positions not invested 
            if security.Invested:
                continue
            
            price = security.Price
            signalDeltaPercent = assetData.macd.Current.Value - assetData.macd.Signal.Current.Value
            if signalDeltaPercent < -tolerance:
                #Go long
                self.MarketOrder(symbol, 1)
                assetData.takeProfit = self.LimitOrder(symbol, -1, price*1.03)
                assetData.stopLoss = self.StopMarketOrder(symbol, -1, price*0.97)
            if signalDeltaPercent > tolerance:
                #Go short
                self.MarketOrder(symbol, -1)
                assetData.takeProfit = self.LimitOrder(symbol, 1, price*0.97)
                assetData.stopLoss = self.StopMarketOrder(symbol, 1, price*1.03)


    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status != OrderStatus.Filled:
            return
        assetData = self.SymbolData.get(orderEvent.Symbol, None)
        if assetData:
            assetData.Cancel(orderEvent.OrderId)


class AssetData:
    def __init__(self, macd):
        self.macd = macd
        self.takeProfit = None
        self.stopLoss = None

    def Cancel(self, id):
        '''cancel one order if the other was filled'''
        if self.takeProfit is not None and id == self.takeProfit.OrderId:
            self.stopLoss.Cancel()
        if self.stopLoss is not None and id == self.stopLoss.OrderId:
            self.takeProfit.Cancel()
            
    def Dispose(self):
        '''cancel submitted orders'''
        if self.takeProfit is not None:
            self.takeProfit.Cancel()
        if self.stopLoss is not None:
            self.stopLoss.Cancel()