Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Getting Benchmark Pricing Within a Custom Factor

Hi,

I'm really having a hard time mastering this whole Custom Factor business. I seem to have all the basics down pretty solidly, but there are certain things where I just seem to go in circles. So I'm hoping one of you all has found some exercises to help with mastering the data structures for more complex calculations.

As for my current task, I'm simply trying to call the pipeline one single time, and in that pipeline I would like to call a Custom Factor that will reference a dictionary based on the sector code, and then calculate the ratio spread with the current (assets close)/(sector etf close).

Is there an easier way to do this?

Here is what I was trying but getting nowhere with.

Thanks in advance!



class Spread_Ratio(CustomFactor):  
    """  
    Ratio Spread Based on Input Stock Symbol  
    """

    inputs=[USEquityPricing.close, ac.morningstar_sector_code]  
    window_length = 1

    def compute(self, today, assets, out, close, sectors):

        prices = close  
        #etf = []  
        #for x in sectors[-1]:  
        #    etf.append(sector_ETFs[x].sid)

        # get index of benchmark  
        benchmark_index = np.where((assets == sector_ETFs[sectors[-1]].sid) == True)[0][0]

        # get returns of benchmark  
        benchmark_prices = prices[-1, benchmark_index]  
        out[:] = close[-1, :]/benchmark_prices

sector_ETFs = {  
        101: symbols('XLB'),  
        102: symbols('XLY'),  
        103: symbols('XLF'),  
        104: symbols('IYR'),  
        205: symbols('XLP'),  
        206: symbols('XLV'),  
        207: symbols('XLU'),  
        308: symbols('IYZ'),  
        309: symbols('XLE'),  
        310: symbols('XLI'),  
        311: symbols('XLK')  
}
1 response

Working with the inputs within a custom factor can be challenging at times. The inputs are numpy ndarrays with rows representing trading days within the defined window_length. There is one row for each day. The most recent row is [-1]. Each column represents the associated data for an asset. The order of the columns (ie the mapping of column number to asset) can be determined from the 'asset' parameter. Numpy methods are powerful but pandas often offers more intuitive options.

It's sometimes easier to turn the various inputs into pandas Series or Dataframe objects. Since the inputs share common indexes (the assets) these pandas structures make lookup type calculations easy. This isn't always the most computationally efficient approach but often it speeds up the algo design effort. If one is just exploring ideas this approach can get those ideas up and running much faster.

So, here is one way to implement a 'asset price to associated ETF ratio' custom factor.

class Spread_Ratio(CustomFactor):  
    """  
    Factor to calculate the ratio of asset close price to the ETF price in  
    the associated sector.  
    """  
    inputs = [USEquityPricing.close, Sector()]  
    window_length = 1  
    # Dict of ETF SIDs and their associated sector  
    sector_to_etf_map = {  
        101: 19654, # [XLB]  
        102: 19662, # [XLY]  
        103: 19656, # [XLF]  
        104: 21652, # [IYR]  
        205: 19659, # [XLP]  
        206: 19661, # [XLV]  
        207: 19660, # [XLU]  
        308: 21525, # [IYZ]  
        309: 19655, # [XLE]  
        310: 19657, # [XLI]  
        311: 19658, # [XLK]  
    }

    def compute(self, today, assets, out, close_price, sector):  
        # Create series out of our data.  
        sectors = pd.Series(data=sector[-1], index=assets)  
        close_prices = pd.Series(data=close_price[-1], index=assets)

        # Use the 'map' method. First to map sector to ETF SID then to map ETF SID to ETF close_price  
        associated_etf = sectors.map(self.sector_to_etf_map)  
        associated_etf_price = associated_etf.map(close_prices)

       # Finally, divide asset close price by associated ETF close price  
        ratio = (close_prices / associated_etf_price) - 1.0  
        out[:] =  ratio  

This custom factor can be implemented in a few statements by using the pandas map method (twice). The map method is a great tool whenever one wants to do a lookup (see https://pandas.pydata.org/pandas-docs/version/0.18/generated/pandas.Series.map.html). In this case we first want to lookup the ETF SID when we know the associated sector. What we really want however, is the ETF close price. So, do a second lookup. Now that we have the ETF SID we can easily lookup the ETF close price from the close_prices series by using map a second time. This can be done with numpy methods but it's a little less intuitive and flexible.

As with any factor, one can over-ride the default inputs. By over-riding the default 'USEquityPricing.close' input with 'returns' one can get the ratio of asset returns to ETF returns using the exact same custom factor. This ratio could be helpful in finding assets which outperform their associated sector.

Attached is a notebook showing this custom factor in action along with a bit more explanatory comments.

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.