Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Help with Custom Factor

Hi all,
I know there are quite a bit of these threads but after looking over several, I became no wiser.

I want to write a custom factor that fetches returns over the last year, resamples them to monthly and ultimately returns the cumulative product of these monthly returns (for the purpose of making a filter). How can I go about this?

Thank you all in advance.

11 responses

You may not need a custom factor for this. However, not completely sure what you intend by 'cumulative product of monthly returns'. Are you wanting to get the total return from the monthly returns? Something like this?

month_1_return : 1%  
month_2_return : 2%  
month_3_return : -1%  
total_returns : ?

In the above case the total return would be

1.01 x 1.02 x .99 = 1.019898  

this equals 1.9898%

Is that the goal?

Hi Dan, thanks for answering!
No, the goal would be

0.01 x 0.02 x -0.01  

I don't care about the actual product in the end, only the sign!

Gotcha. Another question then... Do the monthly returns need to be 'true' 1st of the month - to 1st of the month returns, or, could an average previous 21 trading days returns be good enough (there are about 21 trading days per month on the average).

I know this isn't an exact solution to what you described above, but hopefully it will help guide in the right direction.

When you initialize your algo, you can add the below Custom Factor:

Monthly Return Factor = Price of current day / Price of 21 days ago

class MonthlyReturns(CustomFactor):  

# predeclare your inputs and window length  
inputs = [USEquityPricing.close]  
window_length=21

# return out the montly return value  
def compute(self, today, assets, out, close):  
    out[:] = close[-1]/close[0]

You could set your pipeline to update once per month, that way once per month you are adding 21 day returns to your pipeline (i.e. one month of trading days).

Once you have added this factor to the pipeline with something like:

monthly_returns = MonthlyReturns()
pipe.add(monthly_returns, 'monthly_returns')

In your order function, you can maybe implement an if statement on the return column for your algo to go long on stocks that are > 0 in this regard and short stock that are < 0 monthly returns. In the attached, I just went long on the top 100 returning stocks each month.

Dan: I think median or mean could be a suitable substitute yes!
Brooks: Thank you for taking the time to write that out, but it is unfortunately not what I need. I am essentially trying to do a double-sort as a part of a momentum strategy. I essentially want to classify stocks to either have positive or negative time-series momentum (the sign of the cumulative product of monthly returns) and then calculate cross-sectional momentum (like your factor does) for both the positive and negative time-series partitions of the data.

Ultimately I want to long stocks with high cross-sectional momentum and positive time-series momentum and short the losers in the negative time-series partition.

Nikolas,

I made a few adjustments after having a better idea of what you were looking for. I simplified the factors using the built-in Returns class. I also added a column to the pipeline that calculates the running product of the monthly returns. If the cumulative product is positive it will display 'True' in this column. Cumulative returns that are <= 0 will show as 'False' in this column. To accomplish the double-sort, these are used as filters for the long and short lists before they are then ranked for cross-sectional momentum. We go long the top 50 monthly returning stocks that have a positive cumulative return product. We go short the bottom 50 monthly returning stocks that have a negative cumulative return product. I have not implemented the Optimize API or any other constraints other than leverage. Hopefully this is a little more helpful than before --I try to make the comments very descriptive.

Thank you so much Brooks! I will try to re-create this logic in the Research Environment as well, just so I understand fully what is happening and I'll work on implementing the Optimize API.

One question regarding line 46 though: Doesn't the Returns factor return a single value as opposed to a return series? Would it not be more appropriate to use the DailyReturns factor?

For anyone interested, I ended up doing this to achieve my goal:

class TSMomentum(CustomFactor):  
    inputs = [USEquityPricing.close]  
    window_length=252  
    def compute(self, today, assets, out, close):  
        monthly_price = close[::21,  :]  
        monthly_returns = np.diff(monthly_price, axis=0) / monthly_price[:-1,:]  
        out[:] = np.prod(monthly_returns, axis=0)

does that factor have alpha?

I plan to use it in conjunction with other factors, this one is really just for filtering purposes, but probably not, no. That doesn't really matter too much to me because I am doing this as part of a research paper. If it sucks, that is a completely valid result in the end.