Overall Statistics |
Total Trades 216 Average Win 2.26% Average Loss -1.34% Compounding Annual Return 11.363% Drawdown 47.100% Expectancy 0.492 Net Profit 80.769% Sharpe Ratio 0.411 Probabilistic Sharpe Ratio 3.143% Loss Rate 44% Win Rate 56% Profit-Loss Ratio 1.69 Alpha 0.113 Beta 0.137 Annual Standard Deviation 0.308 Annual Variance 0.095 Information Ratio 0.089 Tracking Error 0.338 Treynor Ratio 0.927 Total Fees $615.09 Estimated Strategy Capacity $2500000.00 Lowest Capacity Asset FLNG X5ESRFXWH9WL |
#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].EndTime asset = self.averages[key] # warm up stock's indicators if not ready if not asset.areIndsReady(): 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 # 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) asset.updateForSelection(time, slice[key],algorithm) # self.Plot("Debug",'P1', asset.pivot1) # self.Plot("Debug",'Price', stock_price) # self.Plot("Debug",'P2', asset.pivot2) # self.Plot("Debug",'ema200', asset.slow.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.is_uptrend and x.vcpFormed and x.todayVolume>x.avgVolume.Current.Value*3, 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.scale, reverse=True)[: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.todayVolume = 0.1 self.tolerance = 1.01 self.fast = ExponentialMovingAverage(50) self.avgVolume = SimpleMovingAverage(20) self.slow = ExponentialMovingAverage(200) self.fiftyTwoHigh = Maximum(253) self.fiftyTwoLow = Minimum(253) self.downFromHigh=1 self.upFromLow=0 self.is_uptrend = 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.vcpFormed=False def areIndsReady(self): indsReady=True indsReady=self.fiftyTwoHigh.IsReady and indsReady indsReady=self.fiftyTwoLow.IsReady and indsReady indsReady=self.fast.IsReady and indsReady indsReady=self.slow.IsReady and indsReady indsReady=self.avgVolume.IsReady and indsReady return indsReady def updateForSelection(self, time, currentSlice,context): stockPrice=currentSlice.Price #update volume self.todayVolume=currentSlice.Volume 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 if self.is_uptrend: #self.checkVCP(stockPrice) # if in uptrend and vcp then only enter self.scale = (fast - slow) / ((fast + slow) / 2.0) def updateInds(self, time, value): self.fiftyTwoHigh.Update(time, value) self.fiftyTwoLow.Update(time, value) self.fast.Update(time, value) self.slow.Update(time, value) def updateBarInds(self,barData): self.adrPerc.Update(barData) def updateVolInds(self,time,volume): 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) 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 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 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/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/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 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 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/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/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(2017,1,1) #Set Start Date self.SetEndDate(2022,7,4) #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.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(MaximumDrawdownPercentPerSecurity(0.08)) #performing worst #self.AddRiskManagement(TrailingStopRiskManagementModel(0.15)) 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() if self.Time < self.rebalanceTime: return else: #till found better place, liquidated from here self.Liquidate() self.rebalanceTime = Expiry.EndOfQuarter(self.Time) # 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 < 2000000000 , fine)) #take top n stop for analysis sortedByMarketCap = sorted(filteredByMarketCap, key=lambda x: x.MarketCap, reverse=True)[:150] # 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
# 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)