Overall Statistics |
Total Trades 1072 Average Win 0.28% Average Loss -0.18% Compounding Annual Return 25.523% Drawdown 35.000% Expectancy 1.039 Net Profit 212.783% Sharpe Ratio 0.985 Probabilistic Sharpe Ratio 38.575% Loss Rate 20% Win Rate 80% Profit-Loss Ratio 1.55 Alpha 0.058 Beta 1.342 Annual Standard Deviation 0.246 Annual Variance 0.061 Information Ratio 0.985 Tracking Error 0.107 Treynor Ratio 0.181 Total Fees $1221.82 Estimated Strategy Capacity $17000000.00 |
from datetime import timedelta from QuantConnect.Data.UniverseSelection import * from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel #from QuantConnect.Algorithm.Framework.Portfolio import EqualWeightingPortfolioConstructionModel class SectorBalancedPortfolioConstruction(QCAlgorithm): def Initialize(self): self.SetStartDate(2015, 12, 28) self.SetEndDate(2020, 12, 31) self.SetCash(100000) self.UniverseSettings.Resolution = Resolution.Hour self.SetUniverseSelection(MyUniverseSelectionModel()) self.SetAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(1), 0.025, None)) self.SetPortfolioConstruction(MySectorWeightingPortfolioConstructionModel(timedelta(weeks=1))) self.SetExecution(ImmediateExecutionModel()) self.Settings.RebalancePortfolioOnInsightChanges = False self.Settings.RebalancePortfolioOnSecurityChanges = False self.Settings.FreePortfolioValuePercentage = 0.2 def OnEndOfDay(self): self.Plot(f"Margin", "Used", self.Portfolio.TotalMarginUsed) self.Plot(f"Margin", "Remaining", self.Portfolio.MarginRemaining) self.Plot(f"Cash", "Remaining", self.Portfolio.Cash) class MyUniverseSelectionModel(FundamentalUniverseSelectionModel): def __init__(self): super().__init__(True, None, None) def SelectCoarse(self, algorithm, coarse): filtered = [x for x in coarse if x.HasFundamentalData and x.Price > 0] sortedByDollarVolume = sorted(filtered, key=lambda x: x.DollarVolume, reverse=True) return [x.Symbol for x in sortedByDollarVolume][:100] def SelectFine(self, algorithm, fine): filtered = [f for f in fine if f.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Technology] self.technology = sorted(filtered, key=lambda f: f.MarketCap, reverse=True)[:3] filtered = [f for f in fine if f.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.FinancialServices] self.financialServices = sorted(filtered, key=lambda f: f.MarketCap, reverse=True)[:2] filtered = [f for f in fine if f.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.ConsumerDefensive] self.consumerDefensive = sorted(filtered, key=lambda f: f.MarketCap, reverse=True)[:1] return [x.Symbol for x in self.technology + self.financialServices + self.consumerDefensive] class MySectorWeightingPortfolioConstructionModel(EqualWeightingPortfolioConstructionModel): def __init__(self, rebalance = Resolution.Daily): super().__init__() self.symbolBySectorCode = dict() self.result = dict() def DetermineTargetPercent(self, activeInsights): #1. Set the self.sectorBuyingPower before by dividing one by the length of self.symbolBySectorCode self.sectorBuyingPower = 1/len(self.symbolBySectorCode) if len(self.symbolBySectorCode) > 0 else 0 for sector, symbols in self.symbolBySectorCode.items(): #2. Search for the active insights in this sector. Save the variable self.insightsInSector self.insightsInSector = [insight for insight in activeInsights if insight.Symbol in symbols] #3. Divide the self.sectorBuyingPower by the length of self.insightsInSector to calculate the variable percent # The percent is the weight we'll assign the direction of the insight self.percent = self.sectorBuyingPower / len(self.insightsInSector) #4. For each insight in self.insightsInSector, assign each insight an allocation. # The allocation is calculated by multiplying the insight direction by the self.percent for insight in self.insightsInSector: self.result[insight] = insight.Direction * self.percent return self.result def OnSecuritiesChanged(self, algorithm, changes): for security in changes.AddedSecurities: #algorithm.Log(f"Remove {security.Symbol}") if security.Fundamentals is None: raise AttributeError(f"{security.Symbol} has fundamentals") sectorCode = security.Fundamentals.AssetClassification.MorningstarSectorCode if sectorCode not in self.symbolBySectorCode: self.symbolBySectorCode[sectorCode] = list() self.symbolBySectorCode[sectorCode].append(security.Symbol) sectorsToRemove = [] for security in changes.RemovedSecurities: #algorithm.Log(f"Remove {security.Symbol}") #if security.Fundamentals: # algorithm.Log(f"{security.Symbol} has fundamentals") for sectorCode, symbols in self.symbolBySectorCode.items(): if security.Symbol in symbols: symbols.remove(security.Symbol) if not symbols: sectorsToRemove.append(sectorCode) for sectorCode in set(sectorsToRemove): if sectorCode in self.symbolBySectorCode: self.symbolBySectorCode.pop(sectorCode, None) super().OnSecuritiesChanged(algorithm, changes)