Notebook

Last EWMA Crossover Custom Factor

In [109]:
from quantopian.pipeline import Pipeline, CustomFactor
from quantopian.research import run_pipeline

from quantopian.pipeline.data.builtin import USEquityPricing

import quantopian.pipeline.filters as Filters
import quantopian.pipeline.factors as Factors

import numpy as np
import pandas as pd
In [110]:
class Last_Crossover(CustomFactor):  
    # Define inputs
    inputs = [USEquityPricing.close]

    # Set window_length to the lookback days. 252 (a year) may be a good value.
    window_length = 252
    
    def compute(self, today, assets, out, close):
        # First lets turn the numpy array 'close' into a pandas dataframe
        # We can then use the pandas ewm method
        closes_df = pd.DataFrame(
                        data = close,
                        columns = assets)
        
        # Make two new dataframes with short and long ewma
        ma_short = closes_df.ewm(span=20).mean()  
        ma_long = closes_df.ewm(span=50).mean()
        
        # Find where the short ewma is greater than the long ewma
        # All we really care about is the sign (ie is the short ewma above or below the long)
        # Need to go back to numpy for an easy sign method
        delta = np.sign(ma_short - ma_long)
        
        # Almost there.. lets just find the changes or crossovers. This is where the diff is <> 0
        crossovers = delta.diff(axis = 0)
        
        # A switch from below (-1) to above (+1) will have a diff of +2 (+1 - -1)
        # Lets find last index where this occurs (ie find the last +2)
        # Use the fact that +2 is the max value so use the idxmax method
        # However, this finds the first occurance, so first reverse our data using a loc trick
        crossovers_rev = crossovers.loc[::-1].reset_index(drop=True)
        last_crossover_up_index = crossovers_rev.idxmax(axis=0)
        
        # Just for debug - remove when running
        print last_crossover_up_index
        print crossovers
        print crossovers_rev
        
        # Output the indexes. 0 will be yesterday. 1 will be the day before yesterday etc.
        # If you want yesterday to be 1 then simply add one below (out[:] = last_crossover_up_index+1)
        out[:] = last_crossover_up_index
        
In [111]:
def create_pipeline():
    # Create a Pipeline computing the previous open factor.
    my_mask = Filters.StaticAssets(symbols(["IBM", "AAPL"]))
    
    # limit the output for testing
    last_crossover = Last_Crossover(mask = my_mask)

    p = Pipeline()
    p.add(last_crossover, 'last_crossover')
    p.set_screen(my_mask)

    return p
In [112]:
# Run the pipeline with desired date(s). 
# In this case the we run the pipeline as if it were 11-23-2016
# The data is from the prevous day 11-22-2016

results = run_pipeline(create_pipeline(), '11-23-2016', '11-23-2016')
results
24      87
3766     7
dtype: int64
     24    3766
0     NaN   NaN
1    -1.0  -1.0
2     0.0   0.0
3     0.0   2.0
4     0.0   0.0
5     0.0   0.0
6     0.0   0.0
7     0.0   0.0
8     0.0   0.0
9     0.0   0.0
10    0.0  -2.0
11    0.0   0.0
12    0.0   0.0
13    0.0   0.0
14    0.0   0.0
15    0.0   0.0
16    0.0   0.0
17    0.0   0.0
18    0.0   0.0
19    0.0   0.0
20    0.0   0.0
21    0.0   0.0
22    0.0   0.0
23    0.0   0.0
24    0.0   0.0
25    0.0   0.0
26    0.0   0.0
27    0.0   0.0
28    0.0   0.0
29    0.0   0.0
..    ...   ...
222   0.0   0.0
223   0.0   0.0
224   0.0   0.0
225   0.0   0.0
226   0.0   0.0
227   0.0   0.0
228   0.0   0.0
229   0.0   0.0
230   0.0   0.0
231   0.0   0.0
232   0.0   0.0
233   0.0   0.0
234   0.0   0.0
235   0.0   0.0
236   0.0   0.0
237   0.0   0.0
238   0.0   0.0
239   0.0   0.0
240   0.0   0.0
241   0.0   0.0
242   0.0   0.0
243   0.0   0.0
244   0.0   2.0
245   0.0   0.0
246  -2.0   0.0
247   0.0   0.0
248   0.0   0.0
249   0.0   0.0
250   0.0   0.0
251   0.0   0.0

[252 rows x 2 columns]
     24    3766
0     0.0   0.0
1     0.0   0.0
2     0.0   0.0
3     0.0   0.0
4     0.0   0.0
5    -2.0   0.0
6     0.0   0.0
7     0.0   2.0
8     0.0   0.0
9     0.0   0.0
10    0.0   0.0
11    0.0   0.0
12    0.0   0.0
13    0.0   0.0
14    0.0   0.0
15    0.0   0.0
16    0.0   0.0
17    0.0   0.0
18    0.0   0.0
19    0.0   0.0
20    0.0   0.0
21    0.0   0.0
22    0.0   0.0
23    0.0   0.0
24    0.0   0.0
25    0.0   0.0
26    0.0   0.0
27    0.0   0.0
28    0.0   0.0
29    0.0   0.0
..    ...   ...
222   0.0   0.0
223   0.0   0.0
224   0.0   0.0
225   0.0   0.0
226   0.0   0.0
227   0.0   0.0
228   0.0   0.0
229   0.0   0.0
230   0.0   0.0
231   0.0   0.0
232   0.0   0.0
233   0.0   0.0
234   0.0   0.0
235   0.0   0.0
236   0.0   0.0
237   0.0   0.0
238   0.0   0.0
239   0.0   0.0
240   0.0   0.0
241   0.0  -2.0
242   0.0   0.0
243   0.0   0.0
244   0.0   0.0
245   0.0   0.0
246   0.0   0.0
247   0.0   0.0
248   0.0   2.0
249   0.0   0.0
250  -1.0  -1.0
251   NaN   NaN

[252 rows x 2 columns]
Out[112]:
last_crossover
2016-11-23 00:00:00+00:00 Equity(24 [AAPL]) 87.0
Equity(3766 [IBM]) 7.0