Overall Statistics |
Total Trades 29 Average Win 4.10% Average Loss -8.33% Compounding Annual Return 47.597% Drawdown 9.200% Expectancy 0.279 Net Profit 33.950% Sharpe Ratio 1.866 Probabilistic Sharpe Ratio 71.900% Loss Rate 14% Win Rate 86% Profit-Loss Ratio 0.49 Alpha -0.015 Beta 1.772 Annual Standard Deviation 0.177 Annual Variance 0.031 Information Ratio 0.895 Tracking Error 0.152 Treynor Ratio 0.187 Total Fees $1450.00 Estimated Strategy Capacity $7000.00 Lowest Capacity Asset SPY 30R89NH8XNNTY|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, 5, 1) # Set Start Date self.SetEndDate(2018, 1, 29) # Set Start Date self.SetCash(100000) # Set Strategy Cash self.equity = self.AddEquity("SPY", Resolution.Minute) self.AddOption("SPY", Resolution.Minute) self.equity.SetDataNormalizationMode(DataNormalizationMode.Raw) self.symbol = self.equity.Symbol self.numEntryPosition = 0.5 # Number of short selling call option self.ToleranceOfLoss = 2 # 2 represents for stop loss in 100% loss self.CostOfContract = 0 self.profitPercent=0 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.SetBrokerageModel(MinimumAccountBalanceBrokerageModel(self,500.00)) 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 self.rank > self.IVlvl: self.SellPut(data) #Only do this if profitable or hit stoploss, management code needed if self.contract: self.profitPercent = self.Securities[self.contract].Holdings.UnrealizedProfitPercent if self.CostOfContract!=0 and self.Portfolio[self.contract].Price >= 2 * self.CostOfContract: self.sellAll() self.Log("Closed: Met 100% stop loss") # elif self.CostOfContract!=0 and self.Portfolio[self.contract].Price <= self.CostOfContract/3: elif self.profitPercent >0.50: self.sellAll() self.Log("Closed: took 50% profit") elif self.contract != str() and(self.contract.ID.Date - self.Time) <= timedelta(self.DaysBeforeExp): self.sellAll() self.Log("Closed: too close to expiration") def sellAll(self): totalCost = -(self.CostOfContract*self.Portfolio[self.contract].Quantity)*100 pricePerContract=self.Portfolio[self.contract].Price*100 qty = -self.Portfolio[self.contract].Quantity profit = totalCost-(pricePerContract*qty) self.Log("Closed and took: " + str(profit) + "$ in profit on selling:" + str(self.contract) +" for:" + str(totalCost)) self.Liquidate(self.contract) self.contractsAdded.remove(self.contract) self.contract = str() def SellPut(self, data): if self.contract == str(): self.contract = self.OptionsFilter(data) return if not self.Portfolio[self.contract].Invested and data.ContainsKey(self.contract): bpBefore = self.Portfolio.GetBuyingPower(self.symbol) # self.Log("Starting BP: " + bp) self.CostOfContract = self.MarketOrder(self.contract, -200).AverageFillPrice # self.Log("Cost of contract: " + str(self.CostOfContract)) bpAfter = self.Portfolio.GetBuyingPower(self.symbol) costOfThisTrade = bpBefore-bpAfter self.Log("Cost of Trade " + str(costOfThisTrade)) 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 OnOrderEvent(self, orderEvent): # self.Log(self.profitPercent) 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) class MinimumAccountBalanceBrokerageModel(DefaultBrokerageModel): '''Custom brokerage model that requires clients to maintain a minimum cash balance''' def __init__(self, algorithm, minimumAccountBalance): self.algorithm = algorithm self.minimumAccountBalance = minimumAccountBalance def CanSubmitOrder(self,security, order, message): '''Prevent orders which would bring the account below a minimum cash balance''' message = None # we want to model brokerage requirement of minimumAccountBalance cash value in account orderCost = order.GetValue(security) cash = self.algorithm.Portfolio.Cash cashAfterOrder = cash - orderCost if cashAfterOrder < self.minimumAccountBalance: # return a message describing why we're not allowing this order message = BrokerageMessageEvent(BrokerageMessageType.Warning, "InsufficientRemainingCapital", "Account must maintain a minimum of ${0} USD at all times. Order ID: {1}".format(self.minimumAccountBalance, order.Id)) self.algorithm.Error(str(message)) return False return True