Overall Statistics
Total Trades
19
Average Win
1.08%
Average Loss
-3.73%
Compounding Annual Return
2.297%
Drawdown
11.100%
Expectancy
0.146
Net Profit
4.652%
Sharpe Ratio
0.269
Probabilistic Sharpe Ratio
10.154%
Loss Rate
11%
Win Rate
89%
Profit-Loss Ratio
0.29
Alpha
-0.005
Beta
0.305
Annual Standard Deviation
0.067
Annual Variance
0.005
Information Ratio
-0.576
Tracking Error
0.102
Treynor Ratio
0.059
Total Fees
$47.50
Estimated Strategy Capacity
$54000.00
Lowest Capacity Asset
SPY 3174CDSVN3D5Y|SPY R735QTJ8XC9X
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from AlgorithmImports import *

class TrailingStopRiskManagementModel(RiskManagementModel):
    '''Provides an implementation of IRiskManagementModel that limits the maximum possible loss
    measured from the highest unrealized profit'''
    def __init__(self, maximumDrawdownPercent = 0.05):
        '''Initializes a new instance of the TrailingStopRiskManagementModel class
        Args:
            maximumDrawdownPercent: The maximum percentage drawdown allowed for algorithm portfolio compared with the highest unrealized profit, defaults to 5% drawdown'''
        self.maximumDrawdownPercent = abs(maximumDrawdownPercent)
        self.trailing = dict()

    def ManageRisk(self, algorithm, targets):
        '''Manages the algorithm's risk at each time step
        Args:
            algorithm: The algorithm instance
            targets: The current portfolio targets to be assessed for risk'''
        riskAdjustedTargets = list()

        for kvp in algorithm.Securities:
            symbol = kvp.Key
            security = kvp.Value

            # Remove if not invested
            if not security.Invested:
                self.trailing.pop(symbol, None)
                continue

            profitPercent = security.Holdings.UnrealizedProfitPercent

            # Add newly invested securities
            value = self.trailing.get(symbol)
            if value == None:
                newValue = profitPercent if profitPercent > 0 else 0
                self.trailing[symbol] = newValue
                continue

            # Check for new high and update
            if value < profitPercent:
                self.trailing[symbol] = profitPercent
                continue

            # If unrealized profit percent deviates from local max for more than affordable percentage
            if profitPercent < value - self.maximumDrawdownPercent:
                # liquidate
                riskAdjustedTargets.append(PortfolioTarget(symbol, 0))

        return riskAdjustedTargets
import TrailingStopRiskManagementModel as mr

class UpgradedRedOrangeParrot(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2017, 10, 1)  # Set Start Date
        self.SetEndDate(2019, 10, 1)  # Set Start Date
        self.SetCash(100000)  # Set Strategy Cash
        self.equity = self.AddEquity("SPY", Resolution.Minute)
        self.equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.symbol = self.equity.Symbol
        
        self.vix = self.AddData(CBOE, "VIX").Symbol
        self.rank = 0
        self.contract = str()
        self.contractsAdded = set()
        self.LatestAskPrice=1
        self.DaysBeforeExp = 2
        self.DTE = 45
        self.OTM = 0.10
        self.lookbackIV = 252
        self.IVlvl = 0.25
        self.percentage=0.5
        self.security = self.Securities["SPY"]
        # self.SetRiskManagement(TrailingStopRiskManagementModel())

        self.Schedule.On(self.DateRules.EveryDay(self.symbol), \
                        self.TimeRules.AfterMarketOpen(self.symbol, 30), \
                        self.Plotting)
                        
        self.Schedule.On(self.DateRules.EveryDay(self.symbol),\
                        self.TimeRules.AfterMarketOpen(self.symbol, 30), \
                        self.VIXRank)       
    def VIXRank(self):
        history = self.History(CBOE, self.vix, self.lookbackIV, Resolution.Daily)
        self.rank = ((self.Securities[self.vix].Price - min(history[:-1]["low"])) / (max(history[:-1]["high"]) - min(history[:-1]["low"])) )
    

    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
        # if not self.Portfolio[self.symbol].invested:
        #     self.SetHoldings(self.symbol, self.percentage)
        profitPercent = self.security.Holdings.UnrealizedProfitPercent
        if self.rank > self.IVlvl:
            self.SellPut(data)

        
        #Only do this if profitable, management code needed
        if self.contract:
            if profitPercent >= 0.25 or profitPercent <= -2:
                self.contract.Liquidate
            if (self.contract.ID.Date - self.Time) <= timedelta(self.DaysBeforeExp):
                self.Liquidate(self.contract)
                self.Log("Closed: too close to expiration")
                self.contract = str()
        
    def SellPut(self, data):
        if self.contract == str():
            self.contract = self.OptionsFilter(data)
            return
        elif not self.Portfolio[self.contract].Invested and data.ContainsKey(self.contract):
            # qty = self.Portfolio.TotalPortfolioValue / self.LatestAskPRice
            # qty = int(self.percentage * quantity / 100)
            bp = str(self.Portfolio.GetBuyingPower(self.symbol))
            # self.Log("Starting BP: " + bp)
            # self.Debug("Selling puts: " + str(10) + "@" + str(self.contract.ID.StrikePrice))
                
            self.Sell(self.contract, 10)
            bp = str(self.Portfolio.GetBuyingPower(self.symbol))
            # self.Debug("Ending BP: " + bp)
            
    def OptionsFilter(self, data):
        contracts = self.OptionChainProvider.GetOptionContractList(self.symbol, data.Time)
        self.underlyingPrice = self.Securities[self.symbol].Price
        otm_puts = [i for i in contracts if i.ID.OptionRight== OptionRight.Put and
                    self.underlyingPrice - i.ID.StrikePrice > self.OTM * self.underlyingPrice and 
                    self.DTE - 8 < (i.ID.Date - data.Time).days < self.DTE + 8]
                    

        if len(otm_puts) > 0:
            
            contract = sorted(sorted(otm_puts, key = lambda x: abs((x.ID.Date - self.Time).days - self.DTE)),
                                                key = lambda x: self.underlyingPrice - x.ID.StrikePrice)[0]
            if contract not in self.contractsAdded:
                self.contractsAdded.add(contract)
                self.AddOptionContract(contract, Resolution.Minute)
            return contract 
        else:
            return str()
    
    def Plotting(self):
            self.Plot("Vol Chart", "Rank", self.rank)
            self.Plot("Vol Chart", "lvl", self.IVlvl)
            self.Plot("Data Chart", self.symbol, self.Securities[self.symbol].Close)
            
            option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type == SecurityType.Option]
            if option_invested:
                self.Plot("Data Chart", "Strike", option_invested[0].ID.StrikePrice)