Hello everyone,
I got stuck on my current project trying to use the symbols from my coarse and fine universe for further analysis. Here is what I plan to do:
- Filter for HasFundamentalData and Volume
- Filter for FCFRatio, ROIC, ROE and TotalDebtEquityRatio
- Take the 250 stocks with the highest ROE and calculate SMAs for them. I use a scoring system where whenever one of my criteria is met the score of this particular stock gets increased by one. My criteria is: Price > 20 day SMA; 20 day SMA > 50 day SMA and 50 day SMA > 200 day SMA. The highest sccore a stock can get is 3.
- Then I want to take only stocks with a score of 3 and filter for those which are near a new 52 week high.
- The remaining stocks should be compared to each other on a momentum basis. The best performing stocks over the last year make up the final basket of stocks I want to take a long position in.
How do I go about putting this process into code? I already tried to do it (see below) but I always get errors.
Can someone please help me figure out my mistakes and give me some hints on how to implement that type of workflow in an algorithm?
Best,
JD
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Common")
from QuantConnect.Data.UniverseSelection import *
import numpy as np
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Data.Market import *
from QuantConnect.Data.UniverseSelection import *
class BasicTemplateAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2012, 1, 1)
self.SetEndDate(2012, 1, 31)
self.SetCash(10000)
self.SetWarmUp(250)
self.stock_score = {}
self.returns = {}
self.stock_52w = []
self.fine = []
self.basket = []
self.sma_20 = None
self.sma_50 = None
self.sma_200 = None
self.changes = None
self.AddEquity('SPY', Resolution.Daily)
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
self.Schedule.On(self.DateRules.WeekStart('SPY'), self.TimeRules.AfterMarketOpen('SPY', 1), Action(self.Rebalance))
self.weekly_rebalance = False
def CoarseSelectionFunction(self, coarse):
if self.weekly_rebalance:
CoarseWithFundamental = [x for x in coarse if (x.HasFundamentalData) and (int(x.Volume) > 100000)]
return CoarseWithFundamental
else:
return []
def FineSelectionFunction(self, fine):
if self.weekly_rebalance:
fine = [x for x in fine if (float(x.ValuationRatios.FCFRatio) < 20) and
(float(x.OperationRatios.ROIC.OneYear) > 0) and
(float(x.OperationRatios.ROE.OneYear) > 0) and
(float(x.OperationRatios.TotalDebtEquityRatio.OneYear) < 1)]
sortedByROE = sorted(fine, key = lambda x: x.OperationRatios.ROE.OneYear, reverse = True)[:250]
self.fine = [x.Symbol for x in sortedByROE]
self.weekly_rebalance = False
self.Debug(str(self.Time) + ' Fine Universe: ' + str(self.fine))
self.stock_score = {}
for stock in self.fine:
self.sma_20 = self.SMA(stock, 20, Resolution.Daily)
self.sma_50 = self.SMA(stock, 50, Resolution.Daily)
self.sma_200 = self.SMA(stock, 200, Resolution.Daily)
if not self.sma_200.IsReady:
return
else:
score = 0
score += np.where(self.Securities[stock].Price > self.sma_20.Current.Value, 1, 0)
score += np.where(self.sma_20.Current.Value > self.sma_50.Current.Value, 1, 0)
score += np.where(self.sma_50.Current.Value > self.sma_200.Current.Value, 1, 0)
self.stock_score[stock] = score
self.stocks = [key for key, value in self.stock_score.items() if value == 3]
slices = self.History(self.stocks, 253, Resolution.Daily)
self.stocks_52w = []
if not slices.empty:
for i in self.stocks:
df = slices.loc[i]
if self.Securities[i].Price >= df['high'].nlargest(1) * 0.98:
self.stocks_52w.append(i)
self.returns = {}
if not slices.empty:
for i in self.stocks_52w:
df = slices.loc[i]
prev = df['close'][0]
curr = df['close'][-1]
self.returns[i] = ((curr - prev) / prev) * 100
self.rank_returns = {key: rank for rank, key in enumerate(sorted(self.returns, key = self.returns.get, reverse = True), 1)}
self.basket = [key for key, value in self.rank_returns.items() if value <= 10]
return self.basket
else:
return []
def Rebalance(self):
self.weekly_rebalance = True
def OnData(self, data):
stock_weight = 0.099
if self.changes is None:
return
for security in self.changes.RemovedSecurities:
if security.Invested:
self.Liquidate(security.Symbol)
for security in self.changes.AddedSecurities:
if not security.Invested:
self.SetHoldings(security.Symbol, stock_weight)
self.changes = None
def OnSecuritiesChanged(self, changes):
self.changes = changes
Apollos Hill
you have to attach your backtest so that we can look at what errors you are having.
JDMar
Here is my backtest of the code as it stand right now.
Jing Wu
For universe selection with the indicator, you can refer to this algorithm
The indicator helper method "self.SMA(stock, 20, Resolution.Daily)" doesn't work in FineSlectionFunction as data is not subscribed for those symbols. The engine can not fetch the data for the specified symbol. You need the indicator constructor sma = SimpleMovingAverage(20) and update this constructor method with the daily price manually. You need a class like "SymbolData" in the example to save all the indicators with different periods. The filter condition like the scoring system can also be designed in this class.
For details, please check the attached example.
JDMar
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!