Hi members
I was attempting to create a Long/Short Equity with mean reversion tilt, meaning, go long on equities that were close or hit their 52Lows or go short on equities that just recently hit their 52Highs, but also incorporating certain value investing factors. also, id like to do trades in the account every quarter, not every day or every month. for some reason, when manually checking the data if it does what i want it o do, the data seem to be slightly off, not sure what's going on here. can someone help me fix this?
thank you so much!
#region imports
from AlgorithmImports import *
#endregion
#This is a Template of dynamic stock selection.
#You can try your own fundamental factor and ranking method by editing the CoarseSelectionFunction and FineSelectionFunction
import statistics
from QuantConnect.Data.UniverseSelection import *
class BasicTemplateAlgorithm(QCAlgorithm):
def __init__(self):
# set the flag for rebalance
self.reb = 1
# Number of stocks to pass CoarseSelection process
self.num_coarse = 250
# Number of stocks to long/short
self.num_fine = 20
self.symbols = None
self.first_month = 0
self.topFine = None
def Initialize(self):
self.SetCash(100000)
self.SetStartDate(2015,2,1)
# if not specified, the Backtesting EndDate would be today
self.SetEndDate(2023,4,25)
self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.CoarseSelectionFunction,self.FineSelectionFunction)
# Schedule the rebalance function to execute at the begining of each month
self.Schedule.On
self.Schedule.On(self.DateRules.MonthStart(self.spy),
self.TimeRules.AfterMarketOpen(self.spy,5), Action(self.rebalance))
def CoarseSelectionFunction(self, coarse):
# if the rebalance flag is not 1, return null list to save time.
if self.reb != 1:
return self.topFine if self.topFine is not None else []
# make universe selection once a month
# drop stocks which have no fundamental data or have too low prices
selected = [x for x in coarse if (x.HasFundamentalData)
and (float(x.Price) > 5)]
sortedByDollarVolume = sorted(selected, key=lambda x: x.DollarVolume, reverse=True)
top = sortedByDollarVolume[:self.num_coarse]
return [i.Symbol for i in top]
def FineSelectionFunction(self, fine):
# return null list if it's not time to rebalance
if self.reb != 1:
return self.topFine if self.topFine is not None else []
self.reb = 0
# Calculate 52-week high and low prices
high_low_prices = {}
for x in fine:
symbol = x.Symbol
trade_bars_df = self.History(TradeBar, [symbol], 252, Resolution.Daily)
try:
high = max(trade_bars_df.high)
low = min(trade_bars_df.low)
high_low_prices[symbol] = {'high': high, 'low': low}
# self.Debug(f"high_low_prices: {high_low_prices[symbol]}")
except:
high_low_prices[symbol] = {'high': None, 'low': None}
pass
# Filter long and short candidates
long_candidates = [x for x in fine if high_low_prices[x.Symbol]['low'] is not None and float(x.Price) <= 1.25 * high_low_prices[x.Symbol]['low']]
short_candidates = [x for x in fine if high_low_prices[x.Symbol]['high'] is not None and float(x.Price) >= 0.97 * high_low_prices[x.Symbol]['high']]
# Rank candidates using the existing factors
long_ranked = self.rank_stocks(long_candidates)
short_ranked = self.rank_stocks(short_candidates)
self.long = [x[0] for x in long_ranked[:self.num_fine]]
self.short = [x[0] for x in short_ranked[-self.num_fine:]]
self.topFine = [i.Symbol for i in self.long + self.short]
return self.topFine
def rank_stocks(self, stock_list):
sortedByfactor1 = sorted(stock_list, key=lambda x: x.ValuationRatios.CFOPerShare, reverse=True)
sortedByfactor2 = sorted(stock_list, key=lambda x: x.ValuationRatios.PCFRatio, reverse=True)
sortedByfactor3 = sorted(stock_list, key=lambda x: x.ValuationRatios.EVToEBITDA, reverse=False)
stock_dict = {}
for i, ele in enumerate(sortedByfactor1):
rank1 = i
rank2 = sortedByfactor2.index(ele)
rank3 = sortedByfactor3.index(ele)
score = sum([rank1 * 0.333333, rank2 * 0.333333, rank3 * 0.333333])
stock_dict[ele] = score
return sorted(stock_dict.items(), key=lambda d: d[1], reverse=True)
def OnData(self, data):
pass
def rebalance(self):
if self.first_month == 0:
self.first_month += 1
return
current_month = self.Time.month
if current_month not in [12]:
return
# if this month the stock are not going to be long/short, liquidate it.
long_short_list = self.topFine
for i in self.Portfolio.Values:
if (i.Invested) and (i.Symbol not in long_short_list):
self.Liquidate(i.Symbol)
# Alternatively, you can liquidate all the stocks at the end of each month.
# Which method to choose depends on your investment philosiphy
# if you prefer to realized the gain/loss each month, you can choose this method.
#self.Liquidate()
# Assign each stock equally. Alternatively you can design your own portfolio construction method
for i in self.long:
self.SetHoldings(i.Symbol, 1.2/self.num_fine)
for i in self.short:
self.SetHoldings(i.Symbol, -0.2/self.num_fine)
self.reb = 1
Michael0000
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!