Overall Statistics
Total Trades
536
Average Win
1.05%
Average Loss
-1.25%
Compounding Annual Return
3.424%
Drawdown
20.800%
Expectancy
0.062
Net Profit
19.584%
Sharpe Ratio
0.343
Probabilistic Sharpe Ratio
5.011%
Loss Rate
42%
Win Rate
58%
Profit-Loss Ratio
0.84
Alpha
0.034
Beta
-0.012
Annual Standard Deviation
0.096
Annual Variance
0.009
Information Ratio
-0.247
Tracking Error
0.195
Treynor Ratio
-2.802
Total Fees
$536.36
import numpy as np
import pandas as pd
from datetime import datetime, timedelta

class ZIVTradingStrategy(QCAlgorithm):

    ## order ticket for stop order
    stopMarketTicket = None
    stopMarketFillTime = datetime.min
    highestPrice = 0
    
    def Initialize(self):
       self.SetStartDate(2015, 1, 2)  # Set Start Date
       self.SetCash(10000)  # Set Strategy Cash
               
       self.ziv = self.AddEquity("ZIV", Resolution.Daily)
       self.ziv.SetDataNormalizationMode(DataNormalizationMode.Raw)
       
       # Creates an indicator and adds to a rolling window when it is updated
       self.rocfiveday = self.ROC("ZIV", 5, Resolution.Daily)
       self.rocfiveday.Updated += self.ROCFiveDayUpdated
       self.rocfiveDayWindow = RollingWindow[IndicatorDataPoint](5)
       
       self.roctenday = self.ROC("ZIV", 10, Resolution.Daily)
       self.roctenday.Updated += self.ROCTenDayUpdated
       self.roctenDayWindow = RollingWindow[IndicatorDataPoint](5)
       
       self.momfiveday = self.MOM("ZIV", 5, Resolution.Daily)
       self.momfiveday.Updated += self.MOMFiveDayUpdated
       self.momfiveDayWindow = RollingWindow[IndicatorDataPoint](5)
       
       self.momtenday = self.MOM("ZIV", 10, Resolution.Daily)
       self.momtenday.Updated += self.MOMTenDayUpdated
       self.momtenDayWindow = RollingWindow[IndicatorDataPoint](5)
       
       # get ATR to use for stop loss.
       self.atr = self.ATR("ZIV", 20, Resolution.Daily)
       # get volatility lookback
       self.lookback = 30
       self.SetWarmUp(self.lookback)
       # set up volatility calculation. this is more efficient instead of calling historical data in onData
       self.roc1 = self.ROC("ZIV", 1, Resolution.Daily)
       self.std = IndicatorExtensions.Of(StandardDeviation(self.lookback), self.roc1)
       # define portfolio volatility
       self.target_portfolio_sigma = 0.15
       
       # this is for listening to order status
       self.lastOrderEvent = None 
       self.ticket = None # Flag for position status

    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
                    Arguments:
                        data: Slice object keyed by symbol containing the stock data
                '''
        if self.IsWarmingUp:
            return
        
        # check that 1 day have passed since we hit the stop loss.
        if(self.Time - self.stopMarketFillTime).days < 2:
            return ## this means wait
        
        long_signal_1 = self.momfiveDayWindow[0].Value > float(0)
    
        if not self.Portfolio.Invested:
            if long_signal_1 == True:
                volatility = volatility = self.std.Current.Value * np.sqrt(252) # get vol.
                weight = self.target_portfolio_sigma / volatility # get weight.
                quantity = (self.Portfolio.TotalPortfolioValue * weight)/self.Securities["ZIV"].Close # get quantity.
                self.ticket = self.MarketOrder("ZIV", quantity)
                self.stopMarketTicket = self.StopMarketOrder("ZIV", -quantity, self.Securities["ZIV"].Close - 2 * self.atr.Current.Value)
                
        elif self.ticket is not None and (self.UtcTime - self.ticket.Time).days < 3: ## to update stop loss.
            if self.Securities["ZIV"].Close > self.highestPrice:
                self.highestPrice = self.Securities["ZIV"].Close # save the new highestPrice
                updateFields = UpdateOrderFields()
                updateFields.StopPrice = self.highestPrice - 2 * self.atr.Current.Value ## updating stop price
                self.stopMarketTicket.Update(updateFields)
                self.Debug("ZIV: " + str(self.highestPrice) + " Stop: " + str(updateFields.StopPrice))
        
        elif self.ticket is not None and self.UtcTime >= self.ticket.Time + timedelta(days = 3):
            self.Liquidate("ZIV")
            self.highestPrice = 0
            self.ticket = None
                
                
    # define methods to add indicator values to a rolling window.
    def MOMFiveDayUpdated(self, sender, updated):
        self.momfiveDayWindow.Add(updated)
        
    def MOMTenDayUpdated(self, sender, updated):
        self.momtenDayWindow.Add(updated)
        
    # Adds updated values to rolling window
    def ROCFiveDayUpdated(self, sender, updated):
        self.rocfiveDayWindow.Add(updated)
         
    def ROCTenDayUpdated(self, sender, updated):
        self.roctenDayWindow.Add(updated)
         
    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status != OrderStatus.Filled:
            return ## if no orders were filled then wait and dont worry.
        
        # this checks that the stop order ticket was submitted
        if self.stopMarketTicket is not None and self.stopMarketTicket.OrderId == orderEvent.OrderId:
            self.stopMarketFillTime = self.Time
            self.Debug(self.stopMarketFillTime)