Hi,
I have this code where I invest in the top 3 companies of NASDAQ by Market Cap. I want to try different portfolio distributions, so I import a csv with three columns, each has the corresponding portfolio percentage for each stock i.e 0.9, 0.1, 0.1 or 0.5, 0.2, 0.3.
The problem is that when I compared the same algo with 1 stock (investing in the stock with greatest market cap in nasdaq) and the portfolio distribution (1, 0, 0) I don't get the same results…
I'm using SetHoldings to set the percentages. Here are both codes with results. Any help is appreciated.
First code with parameters symbols = 1 and count = 0
from AlgorithmImports import *
class MorningStarDataAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2009, 1, 1) # Cambiar fechas en las que quieres que corra
self.SetEndDate(2023, 11, 1)
self.SetCash(100000)
self.UniverseSettings.Resolution = Resolution.Hour
self.SetBenchmark("QQQ")
self.num_symbols = int(self.GetParameter('symbols')) # Número de acciones en el portafolio
self.AddUniverse(self.FundamentalSelection)
self.Settings.FreePortfolioValuePercentage = 0.05
self.Schedule.On(self.DateRules.On(self.EndDate),
self.TimeRules.At(15, 55),
self.Liquidate)
self.changes = None
self.i = int(self.GetParameter('count'))
def FundamentalSelection(self, fundamental):
selected = [c for c in fundamental if c.HasFundamentalData and c.SecurityReference.ExchangeId == "NAS"]
sorted_by_mc = sorted(selected, key=lambda f: f.MarketCap, reverse=True)
sorted_by_mc = sorted_by_mc[self.i:self.num_symbols+self.i]
return [ f.Symbol for f in sorted_by_mc ]
def OnData(self, data):
# if we have no changes, do nothing
if self.changes is None: return
# liquidate removed securities
for security in self.changes.RemovedSecurities:
if security.Invested:
self.Liquidate(security.Symbol)
#self.Debug("Liquidated Stock: " + str(security.Symbol.Value))
# we want 50% allocation in each security in our universe
for security in self.changes.AddedSecurities:
self.SetHoldings(security.Symbol, 1/self.num_symbols)
#self.Debug("Bought Stock: " + str(security.Symbol.Value))
self.changes = None
def OnSecuritiesChanged(self, changes):
self.changes = changes
for security in self.changes.RemovedSecurities:
self.Debug('Removed '+ str(security.Symbol))
x = 1
for security in self.changes.AddedSecurities:
self.Debug('Added ' +str(x)+' '+ str(security.Symbol))
x +=1
#def Liquidate(self):
# self.Liquidate()
And the results
Second code i = 65
from AlgorithmImports import *
import pandas as pd
from io import StringIO
class MorningStarDataAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2009, 1, 1) # Cambiar fechas en las que quieres que corra
self.SetEndDate(2023, 11, 1)
self.SetCash(100000)
self.UniverseSettings.Resolution = Resolution.Hour
self.SetBenchmark("QQQ")
self.num_symbols = 3 # Número de acciones en el portafolio
self.AddUniverse(self.FundamentalSelection)
self.Settings.FreePortfolioValuePercentage = 0.05
self.Settings.MinimumOrderMarginPortfolioPercentage = 0
self.Schedule.On(self.DateRules.On(self.EndDate),
self.TimeRules.At(15, 55),
self.Liquidate)
self.changes = None
i = int(self.GetParameter('i'))
self.percentages = self.get_text()
self.x1 = self.percentages.loc[i,'Number1']
self.x2 = self.percentages.loc[i,'Number2']
self.x3 = self.percentages.loc[i,'Number3']
self.Debug('1st stock percentage '+str(self.x1))
self.Debug('2nd stock percentage '+str(self.x2))
self.Debug('3rd stock percentage '+str(self.x3))
self.e1 = None
self.e2 = None
self.e3 = None
self.sorted_added_securities = []
self.active_securities = []
def FundamentalSelection(self, fundamental):
selected = [c for c in fundamental if c.HasFundamentalData and c.SecurityReference.ExchangeId == "NAS"]
sorted_by_mc = sorted(selected, key=lambda f: f.MarketCap, reverse=True)
sorted_by_mc = sorted_by_mc[:self.num_symbols]
return [ f.Symbol for f in sorted_by_mc ]
def OnData(self, data):
# if we have no changes, do nothing
if self.changes is None: return
# liquidate removed securities
for security in self.changes.RemovedSecurities:
self.Liquidate(security.Symbol)
if security.Symbol.Value == self.e1:
self.Debug("Liquidated Stock 1: " + str(security.Symbol.Value))
self.e1 = None
if security.Symbol.Value == self.e2:
self.Debug("Liquidated Stock 2: " + str(security.Symbol.Value))
self.e2 = None
if security.Symbol.Value == self.e3:
self.Debug("Liquidated Stock 3: " + str(security.Symbol.Value))
self.e3 = None
if len(self.active_securities)==3:
self.Rebalance_weights()
self.changes = None
sorted_added_securities = []
def OnSecuritiesChanged(self, changes):
self.changes = changes
for security in self.changes.RemovedSecurities:
self.Debug('Removed '+ str(security.Symbol))
if security in self.active_securities:
self.active_securities.remove(security)
for security in self.changes.AddedSecurities:
self.active_securities.append(security)
self.Debug('Added stock '+str(security.Symbol))
def Rebalance_weights(self):
# Sort active securities by Market Cap
self.active_securities = sorted(self.active_securities, key=lambda x: x.Fundamentals.MarketCap, reverse=True)
# Set portfolio weights accordingly
# Check if the security is already invested with that percentage
if self.active_securities[0].Invested:
self.Debug('is invested')
percentage_invested_1 = self.Portfolio[self.active_securities[0].Symbol].Quantity * self.Securities[self.active_securities[0].Symbol].Price / self.Portfolio.TotalPortfolioValue
if not percentage_invested_1 >= (self.x1-0.09):
self.Debug('Port per 1 '+str(percentage_invested_1))
self.SetHoldings(self.active_securities[0].Symbol, self.x1)
self.Debug("Bought Stock 1: " + str(self.active_securities[0].Symbol))
self.e1 = self.active_securities[0].Symbol.Value
elif not self.active_securities[0].Invested:
self.Debug('not invested')
self.SetHoldings(self.active_securities[0].Symbol, self.x1)
self.Debug("Bought Stock 1: " + str(self.active_securities[0].Symbol))
self.e1 = self.active_securities[0].Symbol.Value
if self.active_securities[1].Invested:
percentage_invested_2 = self.Portfolio[self.active_securities[1].Symbol].Quantity * self.Securities[self.active_securities[1].Symbol].Price / self.Portfolio.TotalPortfolioValue
if percentage_invested_2 >= (self.x2-0.09):
self.Debug('Port per 2 '+str(percentage_invested_2))
self.SetHoldings(self.active_securities[1].Symbol, self.x2)
self.Debug("Bought Stock 2: " + str(self.active_securities[1].Symbol))
self.e2 = self.active_securities[1].Symbol.Value
elif not self.active_securities[1].Invested:
self.SetHoldings(self.active_securities[1].Symbol, self.x2)
self.Debug("Bought Stock 2: " + str(self.active_securities[1].Symbol))
self.e2 = self.active_securities[1].Symbol.Value
if self.active_securities[2].Invested:
percentage_invested_3 = self.Portfolio[self.active_securities[2].Symbol].Quantity * self.Securities[self.active_securities[2].Symbol].Price / self.Portfolio.TotalPortfolioValue
if percentage_invested_3 >= (self.x3-0.09):
self.Debug('Port per 3 '+str(percentage_invested_3))
self.SetHoldings(self.active_securities[2].Symbol, self.x3)
self.Debug("Bought Stock 3: " + str(self.active_securities[2].Symbol))
self.e3 = self.active_securities[2].Symbol.Value
elif not self.active_securities[2].Invested:
self.SetHoldings(self.active_securities[2].Symbol, self.x3)
self.Debug("Bought Stock 3: " + str(self.active_securities[2].Symbol))
self.e3 = self.active_securities[2].Symbol.Value
#def Liquidate(self):
# self.Liquidate()
def get_text(self):
url = 'https://www.dropbox.com/scl/fi/cgcdnn328vi7mqwjsgoi6/portfolio_weights_1.csv?rlkey=1dam9mp6430gj4xt32qlpwjx6&dl=1'
csv_data = self.Download(url)
# Use pd.read_csv to directly read the CSV data into a DataFrame
df = pd.read_csv(StringIO(csv_data), index_col=0)
return df
And results
It doesnt even change at the same dates, I find it wierd.
Thaaanks
Ashutosh
Hi Renata
Great coding and I truly appreciate the efforts you've put into your work.
I was going through both of your codes and ran the backtest and could see that both the backtest gave me the same results.
I found a small logical issue with the code:
Here when the rankings for the tickers change “OnSecuritiesChanged” does not get triggered and you observe a mismatch in orders.
So in the initial code when MSFT, CSCO, and INTC change positions there is no event triggered which leads to no orders until a ticker is removed or added.
This was making orders different in both codes. You can check if these rankings change and place orders when this happens to get the correct output.
I have attached a sample code that triggers the same orders and will give you the correct direction on the code ahead.
Best,
Ashutosh
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.
Renata
Thank you so much Ashutosh! I found the same issue but was not sure how to solve it. Thanks :)
Renata
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!