Overall Statistics |
Total Trades 541 Average Win 2.11% Average Loss -1.06% Compounding Annual Return 3.388% Drawdown 27.000% Expectancy 0.049 Net Profit 22.489% Sharpe Ratio 0.237 Probabilistic Sharpe Ratio 1.179% Loss Rate 65% Win Rate 35% Profit-Loss Ratio 2.00 Alpha -0.027 Beta 0.679 Annual Standard Deviation 0.142 Annual Variance 0.02 Information Ratio -0.44 Tracking Error 0.126 Treynor Ratio 0.05 Total Fees $2037.89 Estimated Strategy Capacity $2400000.00 Lowest Capacity Asset MODN VF1TI9X213Z9 |
#region imports from AlgorithmImports import * #endregion # Your New Python File #you have access to fundamental data in alphamodel #quareterly check added in both files due to separation of concerns methodology class MyAlphaModel(AlphaModel): def __init__(self): self.averages = { } #reshuffle quarterly self.rebalanceTime = datetime.min #every time there is new info for alpha model which creates insights which it passes to portfolio contruction model #you have access to fundamental data in alphamodel def Update(self,algorithm,slice): '''Updates this alpha model with the latest data from the algorithm. This is called each time the algorithm receives data for subscribed securities Args: algorithm: The algorithm instance data: The new data available Returns: New insights''' #Expiry.EndOfQuarter(algorithm.Time) #self.Debug(str(self.Time)+' On data called') #here by Bars, u get only when there is price data for security, # cause OnData is called for dividend etc. types things also insights = [] keys = slice.Bars.Keys for key in keys: if key in self.averages: stock_price = slice[key].Price volume=slice[key].Volume time=slice[key].Time asset = self.averages[key] # warm up stock's indicators if not ready if not asset.areIndsReady(): # for slow ma as we need to check 52 min below 52 low 200ma, we need 200ma avaiable on 52 week before date, so for that we need 253 more data points history = algorithm.History(asset.symbol, 506) for index, row in history.loc[asset.symbol].iterrows(): asset.slow.Update(index, row["close"]) asset.ma200FiftyTwoLow.Update(index, asset.slow.Current.Value) history = algorithm.History(asset.symbol, 253) for index, row in history.loc[asset.symbol].iterrows(): asset.updateInds(index, row["close"]) tradeBar = TradeBar(index, asset.symbol, row.open, row.high, row.low, row.close, row.volume, timedelta(1)) asset.updateBarInds(tradeBar) asset.updateVolInds(index, row.volume) asset.todayVolume=row.volume #update close window asset._closeWindow.Add(row["close"]) # now register indicators for automatic daily updates asset.registerIndsForUpdates(algorithm) vcpHistory = algorithm.History(asset.symbol, 50) for index, row in vcpHistory.loc[asset.symbol].iterrows(): asset.checkVCP(row["close"],index) #manually updating the indicator till found better way asset.ma200FiftyTwoLow.Update(time, asset.slow.Current.Value) asset.updateForSelection(time, slice[key],algorithm) if(slice[key].Symbol.Value=="ACAD" and asset.areIndsReady()): algorithm.Plot("Debug",'Price', stock_price) algorithm.Plot("Debug",'slow', asset.slow.Current.Value) algorithm.Plot("Debug",'slow52Low', asset.ma200FiftyTwoLow.Current.Value) # check if portfolio is full, otherwise invest if algorithm.Time < self.rebalanceTime: # if not invested, then only try to invest, otherwise let portfolio run till monnth change # for x in algorithm.Portfolio: # algorithm.Debug(x) # capacity=10-len([x.Key for x in algorithm.Portfolio if x.Value.Invested]) # if capacity>0: # Filter the values of the dict: we only want up-trending securities from ones which has indicator values values = list(filter(lambda x: x.lowBelow200Low and x.is_uptrend and x.vcpFormed and x.todayVolume>x.avgVolume.Current.Value*2 and not x.isHugeGapUp, self.averages.values())) # Sorts the values of the dict: we want those with greater difference between the moving averages # candidates=sorted(values,key=lambda x: (x.downFromHigh, -x.upFromLow))[:10] candidates=sorted(values,key=lambda x: (x.upFromLow,x.volumeMaDict['yestMa']/x.volumeMaDict['maAtTrend']))[:10] for candidate in candidates: #if candidate not in porfolio already, BUT SHUD THIS BE IN PORTFOLIO CONSTRUCTION? if not algorithm.Portfolio[candidate.symbol].Invested: #algorithm.SetHoldings(candidate.symbol, 0.1) #Insight.Price(candidate.symbol, self.rebalanceTime, InsightDirection.Up, None, None, None, 0.1) insights.append(Insight.Price(candidate.symbol, self.rebalanceTime, InsightDirection.Up, None, None, None, 0.1)) #month changed, liquidate everything and reenter in top 10 else: self.rebalanceTime = Expiry.EndOfQuarter(algorithm.Time) # month changed, liquidate portfolio #algorithm.Liquidate() #dint use it as alpha Insight has expiry added #you can create sort of weighted score of securities by index of it in sorted eps.revenue,margin growth arrays #like 4+2+1=7 return insights def OnSecuritiesChanged(self, algorithm, changes): algorithm.Debug('On security changed called') # liquidate removed securities for security in changes.RemovedSecurities: if security.Symbol in self.averages: self.averages.pop(security.Symbol) # we want 20% allocation in each security in our universe for security in changes.AddedSecurities: self.averages[security.Symbol] = SymbolData(security.Symbol) #self.SetHoldings(security.Symbol, 0.1) class SymbolData(object): def __init__(self, symbol): self.symbol = symbol self.isHugeGapUp=False self._closeWindow = RollingWindow[float](2) self.todayVolume = 0.1 self.tolerance = 1.05 self.fast = ExponentialMovingAverage(50) self.avgVolume = SimpleMovingAverage(20) self.slow = ExponentialMovingAverage(200) self.fiftyTwoHigh = Maximum(253) self.fiftyTwoLow = Minimum(253) self.ma200FiftyTwoLow = Minimum(253) #self.ma200FiftyTwoLowChained = IndicatorExtensions.Of(Minimum(253), self.slow) #self.ma200FiftyTwoLowChained = IndicatorExtensions.MIN(self.slow, 200) self.downFromHigh=1 self.upFromLow=0 self.is_uptrend = False self.lowBelow200Low = False self.scale = 0 self.secondaryRally = 0.0 self.naturalRally = 0.0 self.pivot1=0.0 #high prie pivot self.pivot2=0.0 #low price pivot self.upTrend = 0.0 self.naturalReaction = 0.0 self.secondaryReaction = 0.0 self.trend="up" self.colState="up" self.downTrend=0.0 self.length = 14 self.molt = 1 self.slow = ExponentialMovingAverage(200) self.adrPerc = NormalizedAverageTrueRange(self.length) self.adrPercThreshold = 1.0 self.vcpFormed=False self.volumeMaDict={ 'maAtTrend':0.0, 'yestMa':0.0 } def areIndsReady(self): indsReady=True indsReady=self.fiftyTwoHigh.IsReady and indsReady indsReady=self.fiftyTwoLow.IsReady and indsReady indsReady=self.ma200FiftyTwoLow.IsReady and indsReady indsReady=self.fast.IsReady and indsReady indsReady=self.slow.IsReady and indsReady indsReady=self.avgVolume.IsReady and indsReady indsReady=self._closeWindow.IsReady and indsReady return indsReady def updateForSelection(self, time, currentSlice,context): self.resetVariables() stockPrice=currentSlice.Price #update volume self.todayVolume=currentSlice.Volume #add current Close to rolling window to access it tomorrow self._closeWindow.Add(currentSlice.Close) if self.areIndsReady(): fast = self.fast.Current.Value slow = self.slow.Current.Value self.downFromHigh=1-(stockPrice/self.fiftyTwoHigh.Current.Value) self.upFromLow=(stockPrice/self.fiftyTwoLow.Current.Value)-1 if(self.symbol.Value == 'DDD'): context.Debug('here') #keep updating pivots daily as even when down 200ma, u need the pivots later man self.checkVCP(stockPrice,time) if stockPrice>slow: self.is_uptrend = stockPrice>slow and stockPrice>fast and fast > slow * self.tolerance and self.upFromLow>=0.8 and self.downFromHigh<=0.2 #we want to check today open with yesterday close to see huge gap up opening if((currentSlice.Open-self._closeWindow[1])/self._closeWindow[1])>=0.12: #(self.adrPerc.Current.Value/100): self.isHugeGapUp=True if self.is_uptrend: #self.checkVCP(stockPrice) # if in uptrend and vcp then only enter self.scale = (fast - slow) / ((fast + slow) / 2.0) if self.fiftyTwoLow.Current.Value < self.ma200FiftyTwoLow.Current.Value: self.lowBelow200Low=True def updateInds(self, time, value): self.fiftyTwoHigh.Update(time, value) self.fiftyTwoLow.Update(time, value) self.fast.Update(time, value) #moved slow ind update out as we need it while checking the close below 52 week prior 200ma #self.slow.Update(time, value) #see if you really need it #self.ma200FiftyTwoLow.Update(time, self.slow.Current.Value) if(self.symbol.Value=="ACAD" and self.areIndsReady()): pass def updateBarInds(self,barData): self.adrPerc.Update(barData) def updateVolInds(self,time,volume): #update yesterday volume ma with current volume ma value first self.volumeMaDict['yestMa']=self.avgVolume.Current.Value self.avgVolume.Update(time,volume) def registerIndsForUpdates(self,context): context.RegisterIndicator(self.symbol,self.fiftyTwoHigh,Resolution.Daily) context.RegisterIndicator(self.symbol,self.fiftyTwoLow,Resolution.Daily) context.RegisterIndicator(self.symbol,self.fast,Resolution.Daily) context.RegisterIndicator(self.symbol,self.slow,Resolution.Daily) context.RegisterIndicator(self.symbol,self.adrPerc,Resolution.Daily) context.RegisterIndicator(self.symbol,self.avgVolume,Resolution.Daily,Field.Volume) #context.RegisterIndicator(self.symbol, self.ma200FiftyTwoLowChained, Resolution.Daily) #this method is used to reset all signal variables so that in current iteration we get proper true signal and not prior stale one def resetVariables(self): self.vcpFormed=False self.is_uptrend=False self.scale=0.0 self.isHugeGapUp=False self.lowBelow200Low=False def checkVCP(self,close,time): # self.vcpFormed=True # return True # make default as false self.vcpFormed=False if(self.trend=="down"): # add your logic here for downtrend noting if(self.pivot1==0.0): #no upward pivot set yet self.upTrend= close+close*0.05 #stoploss/up trend marker self.pivot1=self.upTrend self.vcpFormed=False #in pivot setup make vcp false if (close>self.upTrend): #rose above downtrend so downtrend over, mark upTrend instead of natural reaction self.upTrend=close self.trend="up" self.colState="up" #strategy.entry("startBuy",strategy.long) //*** entered long here in scrip self.vcpFormed=True #as we want to go long on stock which broke above downtrend on rising 200 else: if(close>=(self.pivot1+self.pivot1*0.02)): #broke above natural rally pivot, mark uptrend, enter again self.trend="up" # change trend self.colState="up" #change note down column self.upTrend=close #note down price as upTrend #strategy.entry("startBuy",strategy.long, when=strategy.position_size <= 0) //start buying *** entered long here in scrip self.vcpFormed=True #as we want to go long on stock which broke above pivot 1. this u can remove later if u want to enter on 3-C types formation if(close<self.downTrend): #price less than downTrend IS ALWAYS DOWNTREND if(self.trend=="down"): #more downtrend self.downTrend=close self.vcpFormed=False #downtrend continues #note down volume to keep track of volume drying when vcp happens self.volumeMaDict['maAtTrend']=self.avgVolume.Current.Value if(self.colState=="naturalRally"): #direct downtrend from natural rally instead of natural reaction self.downTrend=close self.pivot1=self.naturalRally self.vcpFormed=False #direct downtrend from natural rally, #so vol expanded and dont enter #note down volume to keep track of volume drying when vcp happens self.volumeMaDict['maAtTrend']=self.avgVolume.Current.Value self.pivot2=self.downTrend self.colState="down" else: if(self.colState=="down"): # if noting in downtrend col, price is more than downtrend, check for SIGNIFICANT rally if(((close-self.downTrend)/self.downTrend)>=((self.adrPerc.Current.Value*self.adrPercThreshold)/100)): # first significant rally from downtrend, mark downtrend as lower pivot(pivot2), note colState as naturalRally self.pivot2=self.downTrend self.naturalRally=close self.colState="naturalRally" self.vcpFormed=False #stock going through natural price cycle still if(self.colState=="naturalRally"): if(close>self.naturalRally): self.naturalRally=close #keep updating natural rally with higher values self.vcpFormed=False #stock going through natural price cycle still else: # close less than natural rally if(((self.naturalRally-close)/self.naturalRally)>=((self.adrPerc.Current.Value*self.adrPercThreshold)/100)): # first significant reaction from natural rally, mark natural rally as higher pivot(pivot1), note colState as naturalReaction self.colState="naturalReaction" self.pivot1=self.naturalRally self.vcpFormed=False #stock going through natural price cycle still #self.vcpFormed=True if(self.trend=="up"): if(self.pivot2 == 0.0): #no downward pivot set yet self.downTrend= self.slow.Current.Value-self.slow.Current.Value*0.05 #stoploss self.pivot2=self.downTrend self.vcpFormed=False #in pivot setup make vcp false if(close>self.slow.Current.Value and self.upTrend == 0): #if uptrend is not initialized first time self.trend="up" self.upTrend=close self.pivot1=self.upTrend self.colState="up" self.vcpFormed=False #in pivot setup make vcp false #note down volume to keep track of volume drying when vcp happens self.volumeMaDict['maAtTrend']=self.avgVolume.Current.Value if (close<self.downTrend): #uptrend over, exit strategy, mark downTrend instead of natural reaction self.downTrend=close self.trend="down" self.colState="down" self.vcpFormed=False #broke pivot so vcp false #*****in script closed strategy here**** if(close<=(self.pivot2-self.pivot2*0.02)): # broke natural reaction, mark downtrend self.trend="down" self.downTrend=close self.colState="down" self.vcpFormed=False #broke pivot so vcp false #*****in script closed strategy here**** if(close>self.upTrend): if(self.trend=="up" and self.upTrend != 0):#uptrend is set and prices rising self.upTrend=close self.vcpFormed=False #in constant uptrending and no vcp formed yet #keep latest uptrend as starting point for volume drying up logic self.volumeMaDict['maAtTrend']=self.avgVolume.Current.Value if(self.colState=="naturalReaction"): #direct uptrend from natural reaction instead of natural rally self.upTrend=close self.pivot2=self.naturalReaction self.vcpFormed=True #direct uptrend from natural reaction, #so broke pivot and eligible for entry self.pivot1=self.upTrend #???????READ THIS????? here u assigning pivot when prices rising, shudnt u be assigning it when first reaction from uptrend? if(self.colState=="naturalRally"): #you added it later, broke above natural rally, means from reaction came to natural rally and now uptrend self.vcpFormed=True self.colState="up" if(close<self.upTrend and self.trend=="up"): # only when in uptrend if(self.colState == "up" and ((self.upTrend-close)/(close))>=((self.adrPerc.Current.Value*self.adrPercThreshold)/100)): #first reaction from uptrend self.naturalReaction=close self.colState="naturalReaction" #enter into natural reaction colState self.vcpFormed=False #in normal price behavior, wait for vcp now if(close<self.naturalReaction and self.colState=="naturalReaction"): self.naturalReaction=close #keep updating natural reaction with lower values self.vcpFormed=False #in normal price behavior, wait for vcp now if(self.colState=="naturalReaction" and ((close-self.naturalReaction)/(self.naturalReaction))>=((self.adrPerc.Current.Value*self.adrPercThreshold)/100)): #natural rally happened, mark natural reaction as pivot 2 self.colState="naturalRally" self.naturalRally=close self.pivot2=self.naturalReaction self.vcpFormed=False #natural rally is normal price behavior, wait for vcp now # if(self.colState=="naturalRally" and false): #if were noting in natural rally, # if(((abs(upTrend-high))/(upTrend))<=0.01) // stock high was just short of uptrend, # if(((high-close)/(close))>=((adrPerc-1)/2))//but stock reacts heavily from uptrend point, danger signal, sell # strategy.close("startBuy",when=time_cond) //exit here
#region imports from AlgorithmImports import * #endregion # 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 datetime import datetime from System.Collections.Generic import List #from AlphaModel import * from MyAlpha import * ### <summary> ### In this algorithm we demonstrate how to perform some technical analysis as ### part of your coarse fundamental universe selection ### </summary> ### <meta name="tag" content="using data" /> ### <meta name="tag" content="indicators" /> ### <meta name="tag" content="universes" /> ### <meta name="tag" content="coarse universes" /> class EmaCrossUniverseSelectionAlgorithm(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(2013,12,1) #Set Start Date self.SetEndDate(2020,1,1) #Set End Date #self.SetEndDate(2020,1,1) #Set End Date self.SetCash(100000) #Set Strategy Cash self.UniverseSettings.Resolution = Resolution.Daily self.UniverseSettings.Leverage = 2 self.month = 0 self.coarse_count = 1 #self.SetBenchmark("IJH") self.priorBenchmarkPrice=-1.0 #self.universeSelected=False #reshuffle quarterly self.rebalanceTime = self.Time # this add universe method accepts two parameters: # - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol> self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.AddAlpha(MyAlphaModel()) #use below if you dont want portfolio construction to rebalance portfolio when universe is changed and security is removed self.Settings.RebalancePortfolioOnSecurityChanges = False self.Settings.RebalancePortfolioOnInsightChanges = False self.SetPortfolioConstruction(MyCustomWeightingPortfolioConstructionModel(self.shallRebalance)) #self.SetPortfolioConstruction(NullPortfolioConstructionModel()) #performing middle #self.SetRiskManagement(NullRiskManagementModel()) #performing best self.AddRiskManagement(TrailingStopRiskWithMinProfitManagementModel(0.08,0.2)) #for this its saying algo needs to be manually restarted... #self.AddRiskManagement(MaximumDrawdownPercentPortfolio(0.08)) #performing worst #self.AddRiskManagement(TrailingStopRiskManagementModel(0.1)) self.SetExecution(ImmediateExecutionModel()) #liquidate portfolio on end date self.Schedule.On(self.DateRules.On(self.EndDate.year, self.EndDate.month, self.EndDate.day), self.TimeRules.At(0, 0), self.LiquidateAll) def LiquidateAll(self): self.Liquidate() self.Debug("liquidated on the last day") def OnData(self,slice): #if end date tomorrow, liquidate everything # if(self.Time.date()==((self.EndDate - timedelta(days = 1)).date())): # self.Liquidate() #prior benchmark is not set if self.priorBenchmarkPrice==-1.0: self.priorBenchmarkPrice = self.Benchmark.Evaluate(self.Time) if self.Time < self.rebalanceTime: return else: #till found better place, liquidated from here #self.Liquidate() #try to liquidate laggers, here issue is happening if u liquidate recent entry candidates which havent gotten chance to gain much currentBenchmarkPrice = self.Benchmark.Evaluate(self.Time) percBenchmarkChg = (currentBenchmarkPrice - self.priorBenchmarkPrice)/self.priorBenchmarkPrice for securityMap in [securityMap for securityMap in self.Portfolio if securityMap.Value.Invested]: #bypassing it as of now noOfDaysSinceBought=0.0 filledSecurityOrders = self.Transactions.GetOrders(lambda o: o.Status == OrderStatus.Filled and o.Symbol.Value == securityMap.Key.Value) # [x for x in self.Transactions.GetOrders(None) if (x.Symbol.Value == "securityMap.Key.Value") and (x.Status == OrderStatus.Filled)] latestOrder= next(security for security in filledSecurityOrders) if latestOrder is not None: noOfDaysSinceBought = (self.Time.date() - latestOrder.Time.date()).days if securityMap.Value.UnrealizedProfitPercent<percBenchmarkChg and noOfDaysSinceBought>=30: #returns lagging benchmark self.Liquidate(securityMap.Key) #in this above code self.rebalanceTime = Expiry.EndOfQuarter(self.Time) self.priorBenchmarkPrice = currentBenchmarkPrice #set current benchmark price as prior price for next rebalancing # pass to fine selection function for market cap def CoarseSelectionFunction(self, coarse): #self.Debug('coarse universe selection called') #return portfolio unchanged if not rebalanceTime if self.Time < self.rebalanceTime: return Universe.Unchanged filteredByVolume = list(filter(lambda x: x.Volume >= 500000 and x.HasFundamentalData, coarse)) #and x.Symbol.Value=='WNC' return [ x.Symbol for x in filteredByVolume ] # liquidate and rerank everything to see # if each month actual orders are being placed # self.Liquidate() # Filter the values of the dict: we only want up-trending securities from ones which has indicator values # Filter for market cap less than large caps def FineSelectionFunction(self, fine): self.Debug('fine universe selection called: ') #return portfolio unchanged if not rebalanceTime if self.Time < self.rebalanceTime: return Universe.Unchanged filteredByMarketCap = list(filter(lambda x: 300000000 < x.MarketCap < 10000000000 , fine)) #filteredByMarketCap = list(filter(lambda x: 300000000 < x.MarketCap < 2000000000 , fine)) #take top n stop for analysis sortedByMarketCap = sorted(filteredByMarketCap, key=lambda x: x.MarketCap, reverse=True)[:2000] # will keep the indicators for all mid caps self.Debug(str(self.Time)+' Market cap length'+ str(len(sortedByMarketCap))) #self.universeSelected = True return [ x.Symbol for x in sortedByMarketCap ] # Share the same rebalance function for Universe and PCM for clarity def shallRebalance(self, time): self.Debug(str(time)+'*********Rebalncing called*********') if time.month == self.month or time.month not in [1, 4, 7, 10]: return None self.month = time.month return time class MyCustomWeightingPortfolioConstructionModel(PortfolioConstructionModel): def __init__(self,rebalance=None): #super().__init__(rebalance) #this function is for rebalancing #tried calling but not workinh # if rebalance is not None: # self.SetRebalancingFunc(rebalance) self.month=0 def CreateTargets(self, algorithm, insights): self.percent=0.1 targets=[] capacity=10-len([x.Key for x in algorithm.Portfolio if x.Value.Invested]) #added check here to prevent unnecessary loops if capacity>0: for insight in insights: #added check here to prevent more addition when current target exhaust capacity if capacity>0: #****IMP this does not work with normal cash/leverage=1 account. only works in margin account. dont know why!*** targets.append(PortfolioTarget.Percent(algorithm,insight.Symbol, 0.1)) capacity=capacity-1 return targets # Determines if the portfolio should be rebalanced base on the provided rebalancing func # def IsRebalanceDue(self, insights, algorithmUtc): # if self.rebalance is not None: # return self.rebalance(algorithmUtc) # return True def OnSecuritiesChanged(self, algorithm, changes): pass class TrailingStopRiskWithMinProfitManagementModel(RiskManagementModel): '''Provides an implementation of IRiskManagementModel that limits the maximum possible loss measured from the highest unrealized profit, also it will be triggered once profit level is crossed''' def __init__(self, maximumDrawdownPercent = 0.05, minProfitBeforeTrail=0.1): '''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.minProfitBeforeTrail = abs(minProfitBeforeTrail) self.trailingAbsoluteHoldingsState = 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.trailingAbsoluteHoldingsState.pop(symbol, None) continue if symbol.Value=='CRUS': algorithm.Debug('here') position = PositionSide.Long if security.Holdings.IsLong else PositionSide.Short absoluteHoldingsValue = security.Holdings.AbsoluteHoldingsValue trailingAbsoluteHoldingsState = self.trailingAbsoluteHoldingsState.get(symbol) unrealizedProfitPerc=security.Holdings.UnrealizedProfitPercent # Add newly invested security (if doesn't exist) or reset holdings state (if position changed) if trailingAbsoluteHoldingsState == None or position != trailingAbsoluteHoldingsState.position: self.trailingAbsoluteHoldingsState[symbol] = trailingAbsoluteHoldingsState = self.HoldingsState(position, security.Holdings.AbsoluteHoldingsCost) trailingAbsoluteHoldingsValue = trailingAbsoluteHoldingsState.absoluteHoldingsValue minProfitTrailPointCrossed = trailingAbsoluteHoldingsState.minProfitTrailPointCrossed #dont update trailing point once u noted initial entry point till min profit level is reached #BUT you need to check if it has crossed min profit threshold already or not as after say u in 22% profit ur stoploss should be 14%(8% down), so here if u dont check it, below condition will never set trailing stop when u in profit if(unrealizedProfitPerc> 0.0 and unrealizedProfitPerc<self.minProfitBeforeTrail and minProfitTrailPointCrossed == False): continue # first cross above min profit trailing point observed, now let your algorithm trail from this point if(unrealizedProfitPerc> 0.0 and unrealizedProfitPerc>=self.minProfitBeforeTrail and minProfitTrailPointCrossed == False): self.trailingAbsoluteHoldingsState[symbol].minProfitTrailPointCrossed = True # Check for new max (for long position) or min (for short position) absolute holdings value if ((position == PositionSide.Long and trailingAbsoluteHoldingsValue < absoluteHoldingsValue) or (position == PositionSide.Short and trailingAbsoluteHoldingsValue > absoluteHoldingsValue)): self.trailingAbsoluteHoldingsState[symbol].absoluteHoldingsValue = absoluteHoldingsValue continue drawdown = abs((trailingAbsoluteHoldingsValue - absoluteHoldingsValue) / trailingAbsoluteHoldingsValue) if self.maximumDrawdownPercent < drawdown: # liquidate riskAdjustedTargets.append(PortfolioTarget(symbol, 0)) return riskAdjustedTargets class HoldingsState: def __init__(self, position, absoluteHoldingsValue): self.position = position self.absoluteHoldingsValue = absoluteHoldingsValue self.minProfitTrailPointCrossed = False
# 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 * from System.Collections.Generic import List ### <summary> ### In this algorithm we demonstrate how to perform some technical analysis as ### part of your coarse fundamental universe selection ### </summary> ### <meta name="tag" content="using data" /> ### <meta name="tag" content="indicators" /> ### <meta name="tag" content="universes" /> ### <meta name="tag" content="coarse universes" /> class EmaCrossUniverseSelectionAlgorithm(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(2015,1,1) #Set Start Date self.SetEndDate(2019,1,1) #Set End Date self.SetCash(100000) #Set Strategy Cash self.UniverseSettings.Resolution = Resolution.Daily self.UniverseSettings.Leverage = 2 self.coarse_count = 10 self.averages = { } # this add universe method accepts two parameters: # - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol> self.AddUniverse(self.CoarseSelectionFunction) # sort the data by daily dollar volume and take the top 'NumberOfSymbols' def CoarseSelectionFunction(self, coarse): # We are going to use a dictionary to refer the object that will keep the moving averages for cf in coarse: if cf.Symbol not in self.averages: self.averages[cf.Symbol] = SymbolData(cf.Symbol) # Updates the SymbolData object with current EOD price avg = self.averages[cf.Symbol] avg.update(cf.EndTime, cf.AdjustedPrice) # Filter the values of the dict: we only want up-trending securities values = list(filter(lambda x: x.is_uptrend, self.averages.values())) # Sorts the values of the dict: we want those with greater difference between the moving averages values.sort(key=lambda x: x.scale, reverse=True) for x in values[:self.coarse_count]: self.Log('symbol: ' + str(x.symbol.Value) + ' scale: ' + str(x.scale)) # we need to return only the symbol objects return [ x.symbol for x in values[:self.coarse_count] ] # this event fires whenever we have changes to our universe def OnSecuritiesChanged(self, changes): # liquidate removed securities for security in changes.RemovedSecurities: if security.Invested: self.Liquidate(security.Symbol) # we want 20% allocation in each security in our universe for security in changes.AddedSecurities: self.SetHoldings(security.Symbol, 0.1) class SymbolData(object): def __init__(self, symbol): self.symbol = symbol self.tolerance = 1.01 self.fast = ExponentialMovingAverage(50) self.slow = ExponentialMovingAverage(200) self.is_uptrend = False self.scale = 0 def update(self, time, value): if self.fast.Update(time, value) and self.slow.Update(time, value): fast = self.fast.Current.Value slow = self.slow.Current.Value self.is_uptrend = fast > slow * self.tolerance if self.is_uptrend: self.scale = (fast - slow) / ((fast + slow) / 2.0)