How to run pipeline and strategy only on selected stocks

from zipline.pipeline import Pipeline


Zipline

from zipline.api import( symbol,

get_datetime,

order_target_percent,

schedule_function,

date_rules,

time_rules,

attach_pipeline,

pipeline_output,

set_commission,

set_slippage,

get_open_orders,

cancel_order,

order,

record,

order_target,

)

import pandas as pd

import numpy as np

from zipline.pipeline import Pipeline, CustomFactor

from zipline.pipeline.data import EquityPricing

from zipline.pipeline.factors import SimpleMovingAverage

from zipline.errors import HistoryWindowStartsBeforeData

'''

A function to define things to do at the start of the strategy

'''

def initialize(context):

The context variables can be accessed by other methods

context.long_securities = [

symbol('DIVISLAB'),

symbol('SUNPHARMA'),

symbol('MARUTI'),

symbol('AMARAJABAT'),

symbol('BPCL'),

symbol('BAJFINANCE'),

symbol('HDFCBANK'),

symbol('ASIANPAINT'),

symbol('TCS'),

symbol('RELIANCE')

]

schedule_function(strategy,

date_rules.month_end(days_offset=0),

time_rules.market_close(hours=0, minutes=5))

attach_pipeline(make_pipeline(context), name='my_pipeline')



def make_pipeline(context):

Initialize the pipeline

pipe = Pipeline()

Construct Factors

Get the data for past 20 days

currentprice = EquityPricing.close.latest

sma_65 = SimpleMovingAverage(inputs=[EquityPricing.close], window_length=65)

Register outputs

pipe.add(currentprice, 'cmp')

pipe.add(sma_65, 'sma_65')

Momentum = currentprice/sma_65

Pick the top stocks ranked by Momentum

Momentum_rank = Momentum.percentile_between(98, 100)

pipe.set_screen(Momentum_rank)

return pipe

def strategy(context,data):

Access results using the name passed to attach_pipeline.

pipeline_results = pipeline_output('my_pipeline')

mom = pd.DataFrame()

mom['cmp1'] = pipeline_results['cmp']

mom['65m'] = pipeline_results['sma_65']

context.long_securities = mom.loc[mom.loc[:,'cmp1'] > mom.loc[:,'65m']].index

weight = compute_weights(context)

for security in context.long_securities:

order_target_percent(security, weight)

Exit positions which are not part of long_securities

for security in context.portfolio.positions:

if (security not in context.long_securities):

order_target_percent(security, 0)

def compute_weights(context):

Set the allocations to equal weights for each long positions

num_of_security = len(context.long_securities)

return 1.0/num_of_security



I want to run the program only on the 10 stocks which i have initialised. But it runs on entire NSE in Quantra blueshift backtesting.

Running a pipeline with a specified universe, if you think about it, is equivalent to adding another filter to your pipeline - whethere the asset is in your universe or not. This can be achieved as follows:



Step #1: Define a custom filter function to check for your universe. Something like below:

from zipline.pipeline import Pipeline, CustomFilter

def filter_universe(universe):
    class FilteredUniverse(CustomFilter):
        inputs = ()
        window_length = 1
        def compute(self,today,assets,out):
            in_universe = [asset in universe for asset in assets]
            out[:] = in_universe
    return FilteredUniverse()

Step #2: Add this to the screen in your pipeline code. It goes along with your momentum filter, like below (inside your `make_pipeline` function):

universe_filter = filter_universe(context.long_securities)
Momentum_rank = Momentum.percentile_between(75, 100)
pipe.set_screen(universe_filter & Momentum_rank)

Note how this is evaluated. All equities are filtered for the momentum percentile. They are also filtered based on the universe. The `set_screen` will filter the intersection of these two.

However, this is not a very typical case. Pipeline is a good (lazy, and hence efficient) way to apply a computation over a large universe. If your universe has only 10 stocks, it may be faster, and cleaner, to query past data for each and compute this values directly, instead of through pipeline. Only if you have a large universe to filter on, then the above makes sense. That is why, you will notice I changed the `percentile_between` figures to a wider range. Also you may have to add a check that pipeline does not return blank to avoid errors, something like below (in your `strategy` function) or something equivalent:

if len(pipeline_results.index) == 0:
        return


 

Thank You. I got the idea. Actually I want to use NIFTY 200 stocks. I am just trying it out with 10 stocks.



In the schedule function how to schedule it Quarterly?



schedule_function(strategy,

date_rules.month_end(days_offset=0),

time_rules.market_close(hours=0, minutes=5))

attach_pipeline(make_pipeline(context), name='my_pipeline')



 

Lowest supported frequency for <em>schedule_function</em> is monthly (date_rules.month_start or date_rules.month_end). But we can improvise here.  Set the frequency to monthly and in <em>initialize</em> add a tracking variable (counter) and a settings variable (frequency):

context.counter = 0
context.frequency = 3

Then in the callable used in the `schedule_function` call, add at the beginning

context.counter = context.counter + 1
if context.counter % context.frequency != 0:
    return

This will cause the function to run only once in three months, at the quarter months (March, June, Septermber and December) if start at any January.
 

Or you can simply use the month check in the rebalance function (the callable scheduled with a <em>schedule_function</em>), adding the below check in that function:

month = get_datetime().month
if month not in [3,6,9,12]:
    return

Thank you. I got it.