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

Hello Quantopians!!

I am creating my first algorithm. It is very simple and revolves around the median. I end up with a run-time error because there are no inputs. There error says: "TermInputsNotSpecified: Median requires inputs, but no inputs list was passed." This occurs in line 43 which is part 2 of what is included below. Any suggestions??

I have attached my algorithm and backtest for further help!

Here is my code (part 1):

create a custom factor for finding the median for the past 15 days

class Median(CustomFactor):
def compute(self, today, assets, out, median):
def get_history(context, data):
prices = data.history(self, fields="price", bar_count=15, frequency="1d")
prices_10_30 = prices.tz_convert('US/Eastern').between_time('10:30', '10:30')
median = prices_10_30.median
out[:] = median

And later on in the code (part 2), I have:

Add the median factor defined to the pipeline

median = Median()  
pipe.add(median, 'median')

Thanks for the help!

4 responses

Pipelines only work on daily data. Also one can't fetch any data inside of a custom factor 'compute' method. However, minute data can be fetched in the algo. So, simply use the pipeline to narrow down the stocks to an appropriate universe (in this case something approximating the Russel2000) then fetch minute prices for all those stocks. I like to keep all the stock data in one place (ie one dataframe) so the median price can then be added as a new column to 'context.output'. Something like this.


def before_trading_start(context, data):  
    # Call pipeline_output to get the output  
    context.output = pipeline_output('ranked_2000')  
    # Make a list of all the potential stocks (approx Russel2000)  
    russel2000 = context.output.index.tolist()  


    # Get the median of the prices at 10:30 each day  
    prices = data.history(russel2000, fields="price", bar_count=15*390, frequency="1m")  
    prices_10_30_median = prices.tz_convert('US/Eastern').between_time('10:30', '10:30').median()


    # Add this column of data to the pipeline dataframe using the 'assign' method  
    context.output = context.output.assign(median_price = prices_10_30_median)  


    # Narrow down the securities to only the top 5 on each side & update my universe  
    # Use the nlargest and nsmallest methods  
    context.long_list = context.output.nlargest(5, 'median_price').index.tolist()  
    context.short_list = context.output.nsmallest(5, 'median_price').index.tolist()  

A few other things with the algo.

Use Fundamentals instead of Morningtar (see https://www.quantopian.com/posts/faster-fundamental-data)

Probably want to start with a universe like QTradableStocksUS then filter that to top 2000 by market cap. Otherwise the algo will potentially pick up ETFs and other things that maybe wouldn’t be expected. Or easier, use the Q1500US built in filter. To get more specific one can use the ‘make_us_equity_universe’ method (see https://www.quantopian.com/help#built-in-filters )

As noted, the 'nlargest' and 'nsmallest' methods are perhaps a better choice than sorting and slicing.

Finally, this is undoubtedly a work in progress but not sure why one would want the top and bottom stocks by price.

Attached is an algo with some changes. Good luck.

Thanks Dan!! I have one more question, and I appreciate your help!

I know asking asking for the median prices was weird. After some thinking, this is more of what I was trying to do. I wanted to get the price at 10am and the price at 3:30pm for the past 15 days. Then I wanted take the percentage change, (3:30pm price - 10am price)/10am price for the past 15 days. Then I wanted to get the median of that percentage change. I attempted to do that in the algo you provided me, I think I did it correctly, however, the logs do not show any equities. Could you take a quick glance for me??

Lastly, what is the reason for bar_count = 15*390... why do you multiply by 390?

Thanks for all the help!

First, the easy question. "what is the reason for bar_count = 15*390... why do you multiply by 390"
There are 6.5 hours in a typical trading day (9:30am - 4:00pm). 6.5 hours x 60 min/hr = 390 minutes in a typical trading day. So, 15 days worth of minutes would be 15 x 390 = 5850. I just put the formula there so it would be easier to vary the days (eg from 15 to 5).

Now, trying to do what you want would probably be easier by starting with getting ALL the percent differences. Then select the 10:00am differences. Then find the mean of of those. Something like this.

    # Get the prices  
    prices = data.history(russel2000, fields="price", bar_count=15*390, frequency="1m")  


    # Get percentage changes from 3:30 - 10:00.  
    # This is 18.5 hours or 18.5 x 60 = 1110 minutes  
    # T is the symbol for minutes  
    price_changes = prices.pct_change(1110, freq = 'T')  
    price_changes_10am = price_changes.tz_convert('US/Eastern').between_time('10:00', '10:00')  
    median_price_changes_at_10am = price_changes_10am.median()

This starts by getting ALL the changes. For example from 3:31-10:01, 3:32-10:02, etc. Then just the 10:00am differences are averaged. There's (at least) one issue with this though it may not be that bad. Mondays will return NaN for the difference because it will be getting the difference from Sunday at 3:30 (which doesn't exist). Likewise for any market half days. Also, the correct way to average percent changes is use log returns and not the basic arithmetic returns.

One can do the calculation similar to what was started in the algo above. However, there are some issues which make it not work 'out of the box'. The biggest is that pandas is too smart and won't let you subtract the 10:00 prices from the 3:30 prices as you have it. Pandas will try to align the the times and subtract 10:00 from 10:00. One needs to reset the index and then shift the values before subtracting.

Attached is a start. Good luck.

Great! Thank you!!