Overall Statistics |
Total Trades 1212 Average Win 0.15% Average Loss -0.10% Compounding Annual Return 3.175% Drawdown 4.200% Expectancy 0.237 Net Profit 13.327% Sharpe Ratio 0.686 Loss Rate 51% Win Rate 49% Profit-Loss Ratio 1.53 Alpha 0.015 Beta 0.727 Annual Standard Deviation 0.039 Annual Variance 0.001 Information Ratio 0.259 Tracking Error 0.039 Treynor Ratio 0.036 Total Fees $1691.24 |
from Selection.QC500UniverseSelectionModel import QC500UniverseSelectionModel from Alphas.HistoricalReturnsAlphaModel import HistoricalReturnsAlphaModel from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel from Execution.ImmediateExecutionModel import ImmediateExecutionModel from LevermannFactorsAlphaModel import LevermannFactorsAlphaModel class BasicTemplateFrameworkAlgorithm(QCAlgorithmFramework): def Initialize(self): # Set requested data resolution self.UniverseSettings.Resolution = Resolution.Minute self.SetStartDate(2015, 1, 1) #Set Start Date self.SetEndDate(2019, 1, 1) #Set End Date self.SetCash(100000) #Set Strategy Cash self.SetUniverseSelection(QC500UniverseSelectionModel()) self.SetAlpha(LevermannFactorsAlphaModel(self.Time)) self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel()) self.SetExecution(ImmediateExecutionModel()) self.SetRiskManagement(NullRiskManagementModel()) def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Filled: # self.Debug("Purchased Stock: {0}".format(orderEvent.Symbol)) pass
# Your New Python File# 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 collections import deque from dateutil.relativedelta import relativedelta class LevermannFactorsAlphaModel(AlphaModel): '''Uses Historical returns to create insights.''' def __init__(self, time): '''Initializes a new default instance of the HistoricalReturnsAlphaModel class. Args: lookback(int): Historical return lookback period resolution: The resolution of historical data''' self.symbolDataBySymbol = {} self.lastRebalancing = time - relativedelta(month=1) self.portfolioSize = 10 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''' insights = [] symbolsWithMinimalScore = [] symbolsWithHighestScore = [] if self.lastRebalancing.month != algorithm.Time.month: for symbolData in self.symbolDataBySymbol.values(): symbolData.Update(algorithm.Securities[symbolData.symbol.Symbol].Close) if symbolData.IsReady: if symbolData.Score()>4: symbolsWithMinimalScore.append(symbolData) symbolsWithHighestScore = sorted(symbolsWithMinimalScore, key = lambda x: x.Score(), reverse=True)[:self.portfolioSize] for symbolData in symbolsWithHighestScore: insights.append(Insight.Price(symbolData.symbol.Symbol, timedelta(days = 1), InsightDirection.Up)) self.lastRebalancing = algorithm.Time 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''' # clean up data for removed securities #algorithm.Log(changes.AddedSecurities[0].Fundamentals.ValuationRatios.TotalYield) for removed in changes.RemovedSecurities: symbolData = self.symbolDataBySymbol.pop(removed.Symbol, None) # initialize data for added securities symbols = [ x.Symbol for x in changes.AddedSecurities ] for added in changes.AddedSecurities: if added.Symbol not in self.symbolDataBySymbol: self.symbolDataBySymbol[added.Symbol]= SymbolData(added, algorithm) class SymbolData: '''Contains data specific to a symbol required by this model''' def __init__(self, symbol, qcContext): self.symbol = symbol self.sixMonthsInDays = 100 self.twelveMonthsInDays = 200 self.queue = deque(maxlen=self.twelveMonthsInDays) self.qcContext = qcContext self.WarmUp() self.score=0 def Return(self, period): return 2#self.queue[0]/self.queue[period-1]-1 def Update(self, value): self.queue.appendleft(value) count = len(self.queue) self.IsReady = count == self.queue.maxlen def WarmUp(self): history = self.qcContext.History(self.symbol.Symbol, self.twelveMonthsInDays, Resolution.Daily) if str(self.symbol) in history.index: for tuple in history.itertuples(): self.Update(tuple.close) #Scoring according to https://letyourmoneygrow.com/2018/03/09/susan-levermann-approach-an-investment-strategy-that-works/ def Score(self): score = 0 #1 One year RoE >20%: +1 ; <10%: -1 if self.symbol.Fundamentals.OperationRatios.ROE.OneYear > 0.2: score = score + 1 elif self.symbol.Fundamentals.OperationRatios.ROE.OneYear < 0.1: score = score - 1 #2 EBIT One Year >12%: +1 ; <6%: -1 if self.symbol.Fundamentals.OperationRatios.EBITMargin.OneYear > 0.12: score = score + 1 elif self.symbol.Fundamentals.OperationRatios.EBITMargin.OneYear < 0.06: score = score - 1 #3 Equity Ratio one year >25%: +1 ; <15%: -1 leverage = self.symbol.Fundamentals.OperationRatios.FinancialLeverage.OneYear if leverage != 0: if 1/leverage>0.25: score = score + 1 elif 1/leverage<0.15: score = score - 1 #4 P/E one Year <12: +1 ; >16: -1 if self.symbol.Fundamentals.ValuationRatios.PERatio <12: score = score +1 elif self.symbol.Fundamentals.ValuationRatios.PERatio>16: score = score - 1 #5 P/E five years <13: +1 ; >17: -1 #if self.symbol.Fundamentals.ValuationRatios.PERatio.FiveYear<13: # score = score +1 #elif self.symbol.Fundamentals.ValuationRatios.PERatio.FiveYear>17: # score = score - 1 #6 Analyst Opinions #7 Real price reaction in % on quarterly EPS report >1%: +1 ; <-1%: -1 #8 Current FQ Est EPS% change >5%: +1 ; <-5%: -1 if self.symbol.Fundamentals.ValuationRatios.ForwardEarningYield > 0.05: score = score+1 if self.symbol.Fundamentals.ValuationRatios.ForwardEarningYield < -0.05: score = score-1 #9 6 months price change >5%: +1 ; <-5%: -1 if self.Return(self.sixMonthsInDays) > 0.05: score = score+1 elif self.Return(self.sixMonthsInDays)<-0.05: score = score-1 #10 12 months price change >5%: +1 ; <-5%: -1 if self.Return(self.twelveMonthsInDays) > 0.05: score = score+1 elif self.Return(self.twelveMonthsInDays)<-0.05: score = score-1 #11 EPS growth: Change of current to next FY Est EPS >5%: +1; <-5%: -1 if self.symbol.Fundamentals.ValuationRatios.SecondYearEstimatedEPSGrowth > 0.05: score = score+1 elif self.symbol.Fundamentals.ValuationRatios.SecondYearEstimatedEPSGrowth < -0.05: score = score-1 #12 Momentum: if 6 months price change > 5% and 12 month price change < -5%: 1 # if 6 months price change < -5% and 12 month price change > 5%: -1 if self.Return(self.sixMonthsInDays) > 0.05 and self.Return(self.twelveMonthsInDays)<-0.05: score = score+1 elif self.Return(self.sixMonthsInDays) <0.05 and self.Return(self.twelveMonthsInDays)>0.05: score = score+1 #13 Reversal: if better than benachmark: 1 ; if worse than benchmark -1 return score def __str__(self, **kwargs): return 'Symbol'