So, to make the final example for @Pumplerod, you can create custom factors containing the partial computation you want to share between multiple factors:
class PartialComputationVeryCPUintensive1(CustomFactor):
window_length=30 # some_historical_days_of_data
window_safe = True # see below the explanation
[...]
class PartialComputationVeryCPUintensive2(CustomFactor):
window_length=30 # some_historical_days_of_data
window_safe = True # see below the explanation
[...]
Then, you have the option to create multiple factors that receive in input the same computation:
factor1 = MyFactor1( inputs=[PartialComputationVeryCPUintensive1, PartialComputationVeryCPUintensive2], window_length=1)
factor2 = MyFactor2( inputs=[PartialComputationVeryCPUintensive1, PartialComputationVeryCPUintensive2], window_length=1)
factor3 = MyFactor3( inputs=[PartialComputationVeryCPUintensive1, PartialComputationVeryCPUintensive2], window_length=1)
pipe.add(factor1, 'factor1')
pipe.add(factor2, 'factor2')
pipe.add(factor3, 'factor3')
Or you can create a factor that returns multiple outputs:
factor1, factor2, factor3 = MyFactor123( inputs=[PartialComputationVeryCPUintensive1, PartialComputationVeryCPUintensive2], window_length=1)
pipe.add(factor1, 'factor1')
pipe.add(factor2, 'factor2')
pipe.add(factor3, 'factor3')
Also note that if you can make your cpu expensive computation on a window_length=1, then you can create MyFactor with different window_lengths:
class PartialComputationVeryCPUintensive1(CustomFactor):
window_length=1 # some_historical_days_of_data
window_safe = True
[...]
class PartialComputationVeryCPUintensive2(CustomFactor):
window_length=1 # some_historical_days_of_data
window_safe = True
[...]
factor1 = MyFactor( inputs=[PartialComputationVeryCPUintensive1, PartialComputationVeryCPUintensive2], window_length=10)
factor2 = MyFactor( inputs=[PartialComputationVeryCPUintensive1, PartialComputationVeryCPUintensive2], window_length=4)
factor3 = MyFactor( inputs=[PartialComputationVeryCPUintensive1, PartialComputationVeryCPUintensive2], window_length=7)
pipe.add(factor1, 'factor1')
pipe.add(factor2, 'factor2')
pipe.add(factor3, 'factor3')
EDIT:
added window_safe=True
to PartialComputationVeryCPUintensive
Here is what Quantopian said about using factors as input of other factor:
"[...] is now allowed for a select few factors deemed safe for use as inputs. This includes Returns and any factors created from rank or zscore. The main reason that these factors can be used as inputs is that they are comparable across splits. Returns, rank and zscore produce normalized values, meaning that they can be meaningfully compared in any context."
So, if you are sure your factor is window_safe but you are not going to call zscore or rank on it, then set window_safe=True
in the class