Overall Statistics |
Total Trades 114 Average Win 0% Average Loss 0% Compounding Annual Return 341.578% Drawdown 1.200% Expectancy 0 Net Profit 2.055% Sharpe Ratio 7.965 Probabilistic Sharpe Ratio 69.419% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha -8.567 Beta 4.548 Annual Standard Deviation 0.161 Annual Variance 0.026 Information Ratio -5.797 Tracking Error 0.152 Treynor Ratio 0.282 Total Fees $118.89 Estimated Strategy Capacity $600000.00 Lowest Capacity Asset OFLX TASN5M2GPOIT |
# 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 clr import AddReference AddReference("QuantConnect.Algorithm.Framework") AddReference("QuantConnect.Indicators") AddReference("QuantConnect.Common") from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Algorithm.Framework.Alphas import * from datetime import timedelta from enum import Enum class PPOAlphaModel(AlphaModel): '''Uses PPO to create insights. Using default settings, slow/fast is 12/26. Above/below -.5 will trigger a new insight.''' rebalance_date = None rebalance_complete = False def __init__(self, fastPeriod = 12*5, slowPeriod = 26*5, trigger = 0.0, consolidationPeriod = 7, resolution = Resolution.Daily): self.lookback = (slowPeriod * 3) self.fastPeriod = fastPeriod self.slowPeriod = slowPeriod self.resolution = resolution self.consolidationPeriod = consolidationPeriod self.trigger = trigger self.predictionInterval = Extensions.ToTimeSpan(self.resolution) self.symbolDataBySymbol = {} resolutionString = Extensions.GetEnumString(resolution, Resolution) self.Name = '{}({},{},{})'.format(self.__class__.__name__, fastPeriod, slowPeriod, resolutionString) def Update(self, algorithm, data): '''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: The new insights generated''' #algorithm.Debug("Update Called on PPOAlphaModel") # generate insights when scheduled insights = [] #and algorithm.Time.hour == 8 and algorithm.Time.minute == 59 if self.rebalance_complete or not (algorithm.Time.date() == self.rebalance_date): return insights # only get here to build new insights once on the scheduled trading date self.rebalance_complete = True #algorithm.Debug("Now Generating Insights in PPOAlpha") for symbol, symbolData in self.symbolDataBySymbol.items(): if symbolData.CanEmit: direction = InsightDirection.Flat magnitude = symbolData.Return #algorithm.Debug("got a positive CanEmit with symbol and magnitude: " + str(symbol) + str(magnitude)) if magnitude > self.trigger: direction = InsightDirection.Up if magnitude < self.trigger: direction = InsightDirection.Flat insights.append(Insight.Price(symbol, self.predictionInterval, direction)) algorithm.Log(str(symbol) + ": " + str(magnitude) + ": " + str(direction)) return insights def OnSecuritiesChanged(self, algorithm, changes): '''Event fired each time the we add/remove securities from the data feed Args: algorithm: The algorithm instance that experienced the change in securities changes: The security additions and removals from the algorithm''' #algorithm.Debug("starting OnSecuritiesChanged") # clean up data for removed securities for removed in changes.RemovedSecurities: symbolData = self.symbolDataBySymbol.pop(removed.Symbol, None) if symbolData is not None: symbolData.RemoveConsolidators(algorithm) #algorithm.Debug("in OnSecuritiesChanged past removed list") # initialize data for added securities symbols = [ x.Symbol for x in changes.AddedSecurities ] #algorithm.Debug("before history retrieval") history = algorithm.History(symbols, self.lookback, self.resolution) #algorithm.Debug("after history retrieval") if history.empty: return tickers = history.index.levels[0] for ticker in tickers: symbol = SymbolCache.GetSymbol(ticker) if symbol not in self.symbolDataBySymbol: symbolData = SymbolData(symbol, self.fastPeriod, self.slowPeriod, self.consolidationPeriod) self.symbolDataBySymbol[symbol] = symbolData symbolData.RegisterIndicators(algorithm, self.resolution) symbolData.WarmUpIndicators(history.loc[ticker]) class SymbolData: '''Contains data specific to a symbol required by this model''' def __init__(self, symbol, fastPeriod, slowPeriod, consolidationPeriod): self.Symbol = symbol self.Fast = fastPeriod self.Slow = slowPeriod self.PPO = PercentagePriceOscillator(fastPeriod, slowPeriod, MovingAverageType.Exponential) #self.Consolidator = TradeBarConsolidator(TimeSpan.FromDays(consolidationPeriod)) self.Consolidator = None self.Previous = None # True if the fast is above the slow, otherwise false. # This is used to prevent emitting the same signal repeatedly #self.FastIsOverSlow = False #self.abovePPOTrigger = False #def setPPOTrigger (self, value): #self.abovePPOTrigger = value @property def belowPPOTrigger(self): return (not self.abovePPOTrigger) @property def CanEmit(self): if self.Previous == self.PPO.Samples: return False self.Previous = self.PPO.Samples return self.PPO.IsReady @property def Return(self): return float(self.PPO.Current.Value) #def OnWeeklyData(self, sender, bar): #self.PPO.Update(bar.EndTime, bar.Close) def RegisterIndicators(self, algorithm, resolution): #self.Consolidator = algorithm.ResolveConsolidator(self.Symbol, resolution) algorithm.RegisterIndicator(self.Symbol, self.PPO, self.Consolidator) def RemoveConsolidators(self, algorithm): if self.Consolidator is not None: algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.Consolidator) def WarmUpIndicators(self, history): for tuple in history.itertuples(): self.PPO.Update(tuple.Index, tuple.close) #self.Consolidator.Update(tuple)
class StockSelectionStrategyBasedOnFundamentalFactorsAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2021, 11, 1) # Set Start Date #self.SetEndDate(2017, 5, 2) # Set End Date self.SetCash(100000) # Set Strategy Cash self.current_month = -1 self.coarse_count = 300 self.fine_count = 10 self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.SetAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(30))) self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(lambda time:None)) #self.AddEquity("SPY", Resolution.Daily) self._list = [ ] def CoarseSelectionFunction(self, coarse): if self.current_month == self.Time.month: return Universe.Unchanged self.current_month = self.Time.month selected = [x for x in coarse if (x.HasFundamentalData) and x.DollarVolume > 1000000] return [x.Symbol for x in selected] def FineSelectionFunction(self, fine): fine = [x for x in fine if x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > 0 #and ((x.OperationRatios.RevenueGrowth.OneYear) - (x.FinancialStatements.IncomeStatement.TotalRevenue.OneMonth)) / (x.FinancialStatements.IncomeStatement.TotalRevenue.OneMonth) > 0.25 and x.OperationRatios.NormalizedNetProfitMargin.OneYear > 0.07 and x.FinancialStatements.IncomeStatement.TotalRevenue.TwelveMonths < 900000000 and x.FinancialStatements.CashFlowStatement.OperatingCashFlow.TwelveMonths > 0 and x.OperationRatios.ROE.OneYear > 0.15 and x.SecurityReference.IsPrimaryShare and x.SecurityReference.SecurityType == "ST00000001" and x.SecurityReference.IsDepositaryReceipt == 0 and x.CompanyReference.IsLimitedPartnership == 0] #and x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Technology] return [x.Symbol for x in fine]