Looking at the dummy code from above, there are a couple of issues. First, the inputs to factors should be slices or factors. So this would be more correct
fred = fred_3mt.usd3mtd156n[symbols('SPY')]
my_slice_factor = Slice_Factor([fred, USEquityPricing.close])
Notice we now have a slice and a factor as inputs.
The second issue is the factor definition.
def make_pipeline():
class Slice_Factor(CustomFactor):
window_length = 4
def compute(self, today, assets, out, fred, close):
out[:] = close[-1] - fred[-1]
The calculations and output only depend upon the latest values of the inputs (ie [-1]) so the window length doesn't matter. Is it perhaps a better example to use the first value of the inputs (ie [0]).
def make_pipeline():
class Slice_Factor(CustomFactor):
window_length = 4
def compute(self, today, assets, out, fred, close):
out[:] = close[0] - fred[symbols('SPY')][0]
That will return the close 4 days ago plus the value of fred 4 days ago. Is that closer to the desired outcome (I know this may not be the exact calculation)? If so, read on...
One needs to add a custom factor as a 'helper' or 'workaround'. When using factors or slices as inputs to other factors, the inputted factors must be window_safe=True
. This is simply a flag that means the values of the factor will be the same when calculated over various 'windows' or timeframes. Really this means its value won't be impacted by stock splits so it's 'safe' to use whether a split is applied or not.
As an example, a '10_day_moving_average_price' factor is not window_safe. If a 2:1 split occurs all the values will be cut in half. However, a '10_day_return' factor is. It's just a ratio which will remain the same even if the prices are halved.
It's up to the author of a factor to determine if a factor is 'window_safe'. The window_safe flag is just used in pipeline to throw an error if it finds it's using a factor in an 'un-safe' way and therefore may be giving incorrect results. Setting this to True simply instructs pipeline to not throw an error. It should be noted that maybe using a factor with unadjusted prices is ok. It just depends upon the situation.
Macro data by definition isn't impacted by stock splits so we are safe to set window_safe=True
. This can be done with a simple custom factor which copies the input to the output but then, importantly, sets window_safe=True
. Like this
class Window_Safe(CustomFactor):
# A factor to make a factor window_safe
window_length = 1
# Tell pipeline that this is window_safe even though it may not really be?
window_safe = True
def compute(self, today, assets, out, value):
out[:] = value
# This can be used like this
my_slice = my_data[symbols('SPY')]
my_window_safe_slice = Window_Safe(inputs=[my_slice])
new_factor = Some_Factor(inputs=[my_window_safe_slice], window_length=4)
That is all a bit general. See the attached notebook for specifically how to use factors and slices as inputs to another factor with window_length greater than 1. Maybe also take a look at this post https://www.quantopian.com/posts/get-lagged-output-of-pipeline-custom-factor.
Hope that helps.