First off welcome to Quantopian.
When writing a custom factor it's important to understand the inputs are numpy arrays. There is a column for each asset which passes the 'mask' filter (if any). There is a row for each trading day of data. As an example, consider the following custom factor
class Sample(CustomFactor):
inputs=[Returns(window_length=2)]
window_length = 30
def compute(self, today, asset_ids, out, returns):
out[:] = returns
my_sample = Sample(mask=Q500US())
The input parameter 'returns' will be a numpy array with ~500 columns (one for each asset in the Q500US()) and 30 rows (one row for each of the previous 30 trading days - since the window_length
is 30). The most recent trading day is the last row (ie index -1) and the data for 30 days ago would be the first row (ie index 0). There's more information in the docs https://www.quantopian.com/docs/api-reference/pipeline-api-reference#quantopian.pipeline.CustomFactor.
So, the problem is with the statement
rolling_mean_returns = SimpleMovingAverage(
inputs=[returns],
window_length=20
)
The SimpleMovingAverage
factor expects a BoundColumn or factor as an input and NOT a numpy array. The input parameter 'returns' is a numpy array. Generally, one doesn't use built in factors, such as SimpleMovingAverage
, inside a compute function simply because factors don't accept numpy arrays as parameters. Use numpy, pandas, or other methods instead.
However, in this case, one can use SimpleMovingAverage
as an input like this.
class Average_Returns_Signal(CustomFactor):
inputs=[SimpleMovingAverage(inputs=[Returns(window_length=2).log1p()], window_length=20)]
window_length = 1
def compute(self, today, asset_ids, out, returns):
out[:] = np.sign(returns - 0.001)
The input parameter 'returns' will be a numpy array with values which are the 20 day average of 2 day returns. One doesn't really care about previous average values, so a window_length of 1 will return just the latest 20 day averages.
Using the np.sign
method is a clever approach to outputting a -1 or 1. I believe you need to subtract, rather than add, .001 to accomplish what you want. Additionally, one cannot really average arithmetic returns (eg 10% and -10% do not average to 0%). Use log returns instead. There is a built in method to convert to log returns log1p()
(see https://www.quantopian.com/docs/api-reference/pipeline-api-reference#zipline.pipeline.Factor.log1p).
Attached is a notebook which shows this custom factor.
Good luck.