#Custom factor to calculate number of days where close price < 50 day SMA
class LTUptrend(CustomFactor):
inputs = [USEquityPricing.close]
window_length = 50
def compute(self, today, assets, out, last_close_price):
#*inputs are M x N numpy arrays, where M (axis=0) is the window_length and N (axis=1) is the number of securities. find mean of all the rows for each security
#50 day SMA
sma50cf = np.nanmean(last_close_price[0:-1], axis=0)
# 'True' (=1) to detect bad days whenever last_close_price < sma50cf, otherwise it will be 0 (good day)
# we need to find the last date for this event for each day
#Since it is date descending, we want to find the last '1' in the series. or we can flip it upside down, and find the first 1 in series
bad_day = last_close_price < sma50cf
# Flip this array to now have the most recent date as row 0 (it's normally -1)
bad_day_flipped = np.flipud(bad_day)
# Now find the first occurrence of a bad day for each stock (find first '1' so use argmax)
days_since_last_bad_day = np.argmax(bad_day_flipped, axis=0)
# There is a special case where all days in the window are good days (all 0's)
# The argmax method will return 0 for such day (so will make it look like it was a bad day when it was in fact all good days)
# We want it to return the max days or the window_length for such cases where it is all 0's
#use np.any to deal with such cases to just reutrn the max window length where its all good days
all_good_days = np.any(bad_day_flipped, axis=0)
days_since_last_bad_day = np.where(all_good_days==False, self.window_length, days_since_last_bad_day)
out[:] = days_since_last_bad_day
#Just a custom filter so we can run it for AAPL only later on
class SidInList(CustomFilter):
inputs = []
window_length = 1
params = ('sid_list',)
def compute(self, today, assets, out, sid_list):
out[:] = np.in1d(assets, sid_list)
#Make pipeline
def make_pipeline():
sma50 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=50)
last_close_price = USEquityPricing.close.latest
#run through customfactor
bad_day = last_close_price < sma50
days_since_last_bad_day = LTUptrend()
#filter for AAPL only
include_filter = SidInList(sid_list = (24)) # SID for APPL
return Pipeline(columns={'bad_day':bad_day,'bad_days_count': days_since_last_bad_day,'last_close_price':last_close_price,'sma50':sma50},screen=include_filter)
# Specify a time range to evaluate
period_start = '2020-01-12'
period_end = '2020-03-08'
# Execute pipeline over evaluation period
pipeline_output = run_pipeline(
make_pipeline(),
start_date=period_start,
end_date=period_end
)
pipeline_output