I've been getting a really good grasp on the LEAN framework and have been testing out utilization of it. For this algo I'm practicing on, I'd like to rebalance my portfolio every 6 months. This being said, I simply do that work in the uinverse selection, and my alpha model only sends insights once for every 6 month period to signal purchasing (or if the stocks in the universe aren't invested because of risk management).
I'm using the ImmediateExecutionModel and although I only send alpha insights once every 6 months, the execution model seems to be placing trades for each stocks in the universe every single day.
Of course, I can go and create my own execution model, but I figured the ImmediateExecutionModel should be what I need in this scenario since I just want the orders to be placed as soon as the insights are sent. This also brings me to question if my design of the alpha model is correct. Any help or suggestions would be apreciated!
public class ActiveUniverseTest : QCAlgorithm
{
private bool _rebalance = true;
private DateTime lastRebalance;
public override void Initialize()
{
SetStartDate(2016, 1, 1);
SetEndDate(2017, 1, 1);
SetCash(100000);
UniverseSettings.Resolution = Resolution.Daily;
AddUniverse(SelectCoarse, SelectFine);
AddAlpha(new PriceIndexAlphaModel());
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
SetExecution(new ImmediateExecutionModel());
}
public IEnumerable<Symbol> SelectCoarse(IEnumerable<CoarseFundamental> coarse)
{
if (!_rebalance)
return Universe.Unchanged;
var stocks = (from c in coarse
where c.DollarVolume > 300000 &&
c.DollarVolume < 2000000 &&
c.Price > 5
where c.HasFundamentalData
orderby c.DollarVolume descending
select c.Symbol).Take(500);
return stocks;
}
public IEnumerable<Symbol> SelectFine(IEnumerable<FineFundamental> fine)
{
if (!_rebalance)
return Universe.Unchanged;
var sortedByPriceIndex = from stock in fine
where stock.MarketCap < 1000000000
let history = History<TradeBar>(stock.Symbol, new TimeSpan(180, 0, 0), Resolution.Daily)
where history.Count() > 0
let priceIndex = history.Last().Close - history.First().Close
orderby priceIndex
descending
select stock;
int numberOfStocksToPick = (int)Math.Round(sortedByPriceIndex.Count() * 0.2);
var highestPriceIndexStocks = sortedByPriceIndex.Take(numberOfStocksToPick);
_rebalance = false;
lastRebalance = Time;
var symbols = (from x in highestPriceIndexStocks
orderby x.ValuationRatios.PBRatio
select x.Symbol).Take(30).ToList();
Debug(Time + ": Succesfully chose " + ActiveSecurities.Count + " new stocks!");
return symbols;
}
public override void OnSecuritiesChanged(SecurityChanges changes)
{
foreach (var removed in changes.RemovedSecurities)
{
if (removed.Invested)
{
Liquidate(removed.Symbol);
}
}
}
public override void OnData(Slice slice)
{
if (Time > lastRebalance.AddMonths(6))
{
_rebalance = true;
}
}
}
}
public class PriceIndexAlphaModel : IAlphaModel
{
List<Symbol> symbols = new List<Symbol>();
public void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
foreach (var added in changes.AddedSecurities)
{
symbols.Add(added.Symbol);
}
foreach (var removed in changes.RemovedSecurities)
{
symbols.Remove(removed.Symbol);
}
}
public IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
{
List<Insight> insights = new List<Insight>();
foreach (var symbol in symbols)
{
if (!algorithm.Portfolio[symbol].Invested)
{
insights.Add(new Insight(symbol, new TimeSpan(180, 0, 0, 0, 0), InsightType.Price, InsightDirection.Up));
}
}
return insights;
}
}
Stephen Hyer
It could be the EqualWeightingPortfolioConstructionModel rebalancing. I'm not sure which version of the code is imported but I believe the default rebalance parameter is daily.
Derek Melchin
Hi Cody,
To restrict the PCM to rebalance only when new insights are emitted, we need to override its rebalancing function. We can do so by changing
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
to
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel(time => null));
See the attached backtest for reference.
Best,
Derek Melchin
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.
Cody Gordon
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!