Overall Statistics
Total Trades
912
Average Win
0.04%
Average Loss
-0.05%
Compounding Annual Return
-0.948%
Drawdown
2.200%
Expectancy
-0.093
Net Profit
-1.973%
Sharpe Ratio
-1.476
Probabilistic Sharpe Ratio
0.016%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
0.82
Alpha
-0.01
Beta
0
Annual Standard Deviation
0.006
Annual Variance
0
Information Ratio
-0.891
Tracking Error
0.148
Treynor Ratio
-19.968
Total Fees
$1839.42
class KeltnerMeanReversionAlgorithm(QCAlgorithm):

    def Initialize(self):
        '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''

        self.SetStartDate(2018, 1, 1)    #Set Start Date
        #self.SetEndDate(2015, 1, 1)      #Set End Date
        self.SetCash(250000)             #Set Strategy Cash
        # Find more symbols here: http://quantconnect.com/data
        self.AddEquity("SPY")

        # create the 20 EMA
        self.mean = self.EMA("SPY", 20, Resolution.Daily)

        # create the ATR
        self.atr = self.ATR("SPY", 20, Resolution.Daily)

        self.previous = None

        self.marketTicket = None
        self.limitTicket = None
        self.stopTicket = None
    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.'''
        # a couple things to notice in this method:
        #  1. We never need to 'update' our indicators with the data, the engine takes care of this for us
        #  2. We can use indicators directly in math expressions
        #  3. We can easily plot many indicators at the same time

        # wait for our ema to fully initialize
        if not self.mean.IsReady:
            return

        # only once per day
        if self.previous is not None and self.previous.date() == self.Time.date():
            return
        
        risk_per_trade = 100
        holdings = self.Portfolio["SPY"].Quantity

        # If there is no trades
        if holdings == 0:
            quantity = round(risk_per_trade / self.atr.Current.Value)
            price = self.Securities["SPY"].Price
            
        # Mean reversion when price is too low
            if self.Securities["SPY"].Price < self.mean.Current.Value - 2 * self.atr.Current.Value and self.marketTicket is None:
                self.marketTicket = self.MarketOrder("SPY", quantity)
                self.limitTicket = self.LimitOrder("SPY", -quantity, price + self.atr.Current.Value)
                self.stopTicket = self.StopMarketOrder("SPY", -quantity, price - self.atr.Current.Value)

        # Mean reversion when price is too high
            if self.Securities["SPY"].Price > self.mean.Current.Value + 2 * self.atr.Current.Value and self.marketTicket is None:
                self.marketTicket = self.MarketOrder("SPY", -quantity)
                self.limitTicket = self.LimitOrder("SPY", quantity, price - self.atr.Current.Value)
                self.stopTicket = self.StopMarketOrder("SPY", quantity, price + self.atr.Current.Value)
            
        self.previous = self.Time
        
        
    def OnOrderEvent(self, orderevent):
        
        
        if orderevent.Status == OrderStatus.Filled:
            
            orderId = orderevent.OrderId
            # If limit order has been filled, we cancel our stop loss and reset all order tickets
            if self.limitTicket is not None and orderId == self.limitTicket.OrderId:
                self.marketTicket = None
                self.limitTicket = None
                self.stopTicket.Cancel()
                self.stopTicket = None
            # If stop order has been filled, we cancel our limit order and reset all order tickets
            elif self.stopTicket is not None and orderId == self.stopTicket.OrderId:
                self.marketTicket = None
                self.stopTicket = None
                self.limitTicket.Cancel()
                self.limitTicket = None