Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Risk-Adjusted ROIC (CustomFactor)

Hi All,

I've been trying to create a 'Risk Adjusted ROIC' CustomFactor (based on Max's TEM CapexVol factor). Basically I'd like it to take the average of a company's past X quarters' (say 12 quarters, or 3 years) ROIC, and divide it by the standard deviation of those ROIC values (sort of like a sharpe ratio, but using ROIC instead of Returns).

This is what I have. Could someone please help me identify any mistakes I've made, or point out any issues with doing it this way? Would I need to adjust for 'seasonality' and if so, how would one do this? Thank you!

import numpy as np  
from quantopian.pipeline.data import factset as fsf  
from quantopian.pipeline.factors import CustomFactor  
from quantopian.pipeline.filters import QTradableStocksUS

class RiskAdjValue(CustomFactor):  
    """  
    RiskAdjValue = Average value of past 12 quarters' / standard deviation of those values  
    """  
    window_length = 65 * 12  
    def compute(self, today, assets, out, asof_date, value):  
        values = value  
        for column_ix in range(asof_date.shape[1]):  
            _, unique_indices = np.unique(asof_date[:, column_ix], return_index=True)  
            quarterly_values = values[unique_indices, column_ix]  
            if len(quarterly_values) < 12:  
                quarterly_values = np.hstack([  
                    np.repeat([np.nan], 12 - len(quarterly_values)),  
                    quarterly_values,  
                ])  
            out[column_ix] = np.mean(quarterly_values[-12:]) / np.std(quarterly_values[-12:])

ra_roic = RiskAdjValue(  
        inputs=[fsf.Fundamentals.cf_roic_qf_asof_date,  
                fsf.Fundamentals.cf_roic_qf,  
               ], mask=QTradableStocksUS() )
4 responses

Hey Joakim,

I don't understand why you replace missing values by np.nan, computing mean and std with np.mean and np.std will result give nan outputs. You should either use np.nanmean and np.nanstd, or use the available data (less than 12 obs).

Regarding the seasonality, you can use the difference method or model the seasonal component, but 12 obs isn't sufficient in my opinion

Hey Mathieu!

Thank you, much appreciated! If I don't want any NaN to have an impact on the average or the std, how should I do this? Replace any NaN with the average? Replacing the missing values with np.nan I think was already in Max's template, which I just slightly modified. Maybe it made sense in his CustomFactor, but not in this one?

Also, I don't understand what np.nanmean and np.nanstd do. How can one get a mean or std from NaN values? (I'm quite a numpy rookie, sorry).

Thanks again!

Np.nanmean and np.nanstd simply ignore (drop) the NaN values when computing mean and std.

If you don't want NaNs to affect the values, then use them as Mathieu suggested. If you think there may be some relationship between them, you can try to model it and fill in the NaNs. For example the array [3, 5, NaN, 9, 11] might justify a linear interpolation (i.e. filling it with 7), but it is up to your best judgment - perhaps a non-linear or more complex model to capture the relationship. Since there are only 12 values, each NaN dropped would be a whole quarter's worth of information dropped so I would be somewhat cautious of this.

Brilliant, thanks Adam!!