You can't use builtin (or custom) factors directly in compute because they don't contain actual values yet. They only get filled when you call them later.
THEORETICALLY you could use it like this:
class GoldenCross(CustomFilter):
inputs = [SimpleMovingAverage(inputs = [USEquityPricing.close], window_length = 50, mask = base_universe)]
def compute(self, today, asset_ids, out, sma50):
"""Do some calculations with sma50"""
But SimpleMovingAverage happens to be a factor that isn't window safe and you will get an error. There is however a quick and dirty hack to get around it, simply define a class that inherits from SimpleMovingAverage and add window_safe = True to it:
class SimpleMovingAverage(SimpleMovingAverage):
window_safe = True
Now you can use it as input for your GoldenCross:
class GoldenCross(CustomFilter):
data_inputs = [USEquityPricing.close]
sma50 = SimpleMovingAverage(inputs=data_inputs, window_length=50, mask=base_universe)
sma200 = SimpleMovingAverage(inputs=data_inputs, window_length=200, mask=base_universe)
inputs = [sma50, sma200]
window_length = 3
mask = base_universe
def compute(self, today, asset_ids, out, sma50, sma200):
screen = (sma50[-2] < sma200[-2]) & (sma50[-1] > sma200[-1])
out[:] = screen
An approach without using SimpleMovingAverage at all which does the same:
class GoldenCross(CustomFilter):
inputs = [USEquityPricing.close]
window_length = 202
mask = base_universe
def compute(self, today, asset_ids, out, c):
# Calculate the mean for the last 50 and 200 days up until yesterday
ma50_yesterday = np.nanmean(c[-51:-1], axis=0)
ma200_yesterday = np.nanmean(c[-201:-1], axis=0)
# and the mean for the most recent 50 and 200 days
ma50_today = np.nanmean(c[-50:], axis=0)
ma200_today = np.nanmean(c[-200:], axis=0)
# This will result in a boolean array where the value is True for all assets with a golden cross:
cross = (ma50_yesterday < ma200_yesterday) & (ma50_today > ma200_today)
out[:] = cross
A more detailed explanation about what's going on in custom filters and factors:
In the compute function you're dealing with numpy arrays which have the shape (window_length, number_of_assets). When you slice them like some_input[-1] you get an array with shape (,number_of_assets). Let's say you have a window_length of 5 and the base_universe would contain 3 assets, some_input would look like this (just some random integers):
[[ 4 10 8]
[ 6 3 2]
[10 3 3]
[ 6 1 5]
[ 4 2 3]]
some_input[-1] would be just the last row:
[4 2 3]
and some_input[-2] the second last:
[6 1 5]
Now using an if-statement on these 2 arrays like
if some_input[-1] > some_input[-2]:
pass
would result in a
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
But you're not interested if any or all of the elements satisfy the condition. You want to know WHERE the condition is true. This can simply be accomplished by stating the condition:
boolean_array = some_input[-1] > some_input[-2]
The boolean_array would look like this:
[False True False]
which is excactly the kind of output you want for a custom filter. If you want to combine several conditions you need to use the bitwise and '&' instead of 'and'. If you don't assign them as a variable first you need to put parentheses around them like so:
combined_boolean = (a > b) & (c < d)
For your output you want the whole boolean array as output. But if you set
out[-1] = True
You will get an array where only the value for the last asset is True. To get all truth values use
out[:] = boolean_array