Hi there,
i m developing some code that relies on Fundamental Universe selection on a monthly basis (Algo framework).
- My first issue is that I m getting different backtesting results with the same algorithm (sometimes up 40%, sometimes up 10%, sometimes 2% etc etc)… running the test between Jan 2021 and Jan 2022; the first 6 months look pretty similar between the results, but then the algorithm goes crazy. Not sure if it's something deterministic going around there.
- My second issue is that my algorithm should fire trades once a month, but it's firing way more than that (every trading day). My vector of insights have about 10 stocks (5 longs, 5 shorts).. in a year's time should fire, in worst case scenario 10 trades a month x 12 months = 120 or so, currently firing 1000 ish trades.
#from datetime import timedelta
from QuantConnect.Data.UniverseSelection import *
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
class FirstAttempt(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021, 1, 1) # Set Start Date
self.SetEndDate(2022,1,1) # Setting end date
self.SetCash(100000) # Set Strategy Cash
#self.SetBenchmark('SPY')
self.lastMonth = -1
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverseSelection(HighRoICHighPEUniverse())
#self.AddUniverseSelection(HighRoICHighPEUniverse())
self.AddAlpha(BuyValue())
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.SetExecution(ImmediateExecutionModel())
class HighRoICHighPEUniverse(FundamentalUniverseSelectionModel):
def __init__(self):
super().__init__(True, None) #took it from the bootcamp
self.lastMonth = -1
def SelectCoarse(self, algorithm, coarse):
if algorithm.Time.month == self.lastMonth:
return Universe.Unchanged
self.lastMonth = algorithm.Time.month
filtered = [ x for x in coarse if x.HasFundamentalData ]
return [x.Symbol for x in filtered]
def SelectFine(self,algorithm, fine):
sorted_high = sorted([x for x in fine if x.MarketCap < 2e9
and x.OperationRatios.ROIC.OneYear > 0.2
and x.ValuationRatios.PERatio > 20
and x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.FinancialServices
and x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.Healthcare],
key=lambda x: x.ValuationRatios.PERatio, reverse=True)
algorithm.Log(f"High Universe consists of {len(sorted_high)} securities")
algorithm.Log(f"High Universe #1 {sorted_high[0]} securities")
algorithm.Log(f"High Universe #2 {sorted_high[1]} securities")
algorithm.Log(f"High Universe #3 {sorted_high[2]} securities")
algorithm.Log(f"High Universe #4 {sorted_high[3]} securities")
algorithm.Log(f"High Universe #5 {sorted_high[4]} securities")
sorted_low = sorted([x for x in fine if x.MarketCap < 2e9
and x.OperationRatios.ROIC.OneYear < 0.05
and x.ValuationRatios.PERatio < 10
and x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.FinancialServices
and x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.Healthcare],
key=lambda x: x.ValuationRatios.PERatio, reverse=True)
#and x.ValuationRatios.PERatio < 10
#key=lambda x: x.OperationRatios.OperationMargin.OneYear, reverse=True
algorithm.Log(f"Low Universe consists of {len(sorted_low)} securities")
algorithm.Log(f"Low Universe #1 {sorted_low[0]} securities")
algorithm.Log(f"Low Universe #2 {sorted_low[1]} securities")
algorithm.Log(f"Low Universe #3 {sorted_low[2]} securities")
algorithm.Log(f"Low Universe #4 {sorted_low[3]} securities")
algorithm.Log(f"Low Universe #5 {sorted_low[4]} securities")
Universe = sorted_high[:5] + sorted_low[-5:]
return [z.Symbol for z in Universe]
class BuyValue (AlphaModel):
def __init__(self):
self.lastMonth = -1
self.longs = []
self.shorts = []
def Update (self, algorithm, data):
if algorithm.Time.month == self.lastMonth:
return []
self.lastMonth = algorithm.Time.month
insights = []
#Check if existing algorithm portfolio contains any securities in long or short directories
#if we are invested on securities that are not in long or shorts, then we go 'flat'
for x in algorithm.Portfolio:
holding = x.Value
symbol = holding.Symbol
if holding.Invested and symbol not in self.longs and symbol not in self.shorts:
insights.append(Insight(symbol, timedelta(30), InsightType.Price, InsightDirection.Flat))
#generate insight-Up for long positions
for symbol in self.longs:
insights.append(Insight(symbol, timedelta(30), InsightType.Price, InsightDirection.Up))
#generate insights for short positions
for symbol in self.shorts:
insights.append(Insight(symbol, timedelta(30), InsightType.Price, InsightDirection.Down))
return insights
def OnSecuritiesChanged(self, algorithm, changes):
added = [x for x in changes.AddedSecurities]
sortedByPE = sorted(added, key=lambda x: x.Fundamentals.ValuationRatios.PERatio, reverse=True)
self.longs = [x.Symbol for x in sortedByPE[:5]]
algorithm.Log('LONGS: ' + ', '.join(sorted([x.Value for x in self.longs])))
self.shorts = [x.Symbol for x in sortedByPE[-5:]]
algorithm.Log('SHORTS: ' + ', '.join(sorted([x.Value for x in self.shorts])))
Jose David Delgado Mosquera
hi here, have not received any response or help about this
Varad Kabade
Hi Jose David Delgado Mosquera,
We could not reproduce the issue of different results for the same backtest; this may have happened due to a previous data issue which is now fixed
The extra orders result from the portfolio rebalancing to maintain leverage of 1. To stop this from occurring, we need to redefine the rebalancing function of the PCM:
Also, orders would be greater than 10 for each time the algorithm scans for insights(due to the presence of flat insights). Refer to the attached backtest
Best,
Varad Kabade
Jose David Delgado Mosquera
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!