Dear Zipline Maintainers,
Before I tell you about my issue, let me describe my environment:
Now that you know a little about me, let me tell you about the issue I am
having:
def before_trading_start(context, data):
context.pipeline_data = pipeline_output('my_pipeline')
Here is how you can reproduce this issue on your machine:
I searched this site, examined the help docs, and stackoverflow, but couldn't figure out the issue
I'm not the best coder, so I apologize if this is a simple issue and a waste of your time.
Sincerely,
Robert
Hey, it looks like the issue is that you are mixing up the pipeline expression
objects and the data they represent. In the Pipeline API, computation happens in
two steps:
These two steps are completely isolated and act on different data types.
The first step, defining the computation, acts on Pipeline objects like
Filter, Factor, and Classifier. These objects represent a step in the
computation you want to perform. The general name for objects in this space is a
Term, which encompasses all of the different types of computations which can
be run. There are two high level types of terms:
A loadable term is a term which represents loading data into memory. For
example, USEquityPricing.close represents the work needed to fetch the close
prices into memory. These terms can only serve as inputs to computable terms.
A computable term represents some computation on the in-memory data which are
either loaded from loadable terms, or are the results of executing other
computable terms. For example, USequityPricing.close.latest represents the
computation needed to slice off just the most recent day of close prices.
Pipeline defines a rich set of operations which act on these symbolic
objects. These objects may be used in arithmetic expressions to define new
computable terms. They also have a lot of methods to express common
computations, for example: .abs().
The second step, execute the computation, acts on 2d numpy arrays of shape
(dates, assets). This is where we translate the symbolic computation defined in
the first step into actual data and perform the given operations.
Because we have defined the computation up front, we can perform optimizations
like common subexpression elimination and temporary elimination. Overall, this
makes the computation faster than a naive numpy solution. By controlling the
execution environment, we can also ensure that adjustments and amendments are
being applied correctly in a point-in-time way.
CustomFactorCustomFactor is a little weird because it straddles the two steps. A
CustomFactor is a symbolic representation of a computation _paired_ with the
concrete implementation of the computation. The instance itself is a Term, and
the compute() method is what gets called in the execute step, meaning it acts
on numpy arrays.
CustomFactor exists for the cases where you cannot express a computation
naturally with the built-in methods or as an arithmetic expression. In general,
using Pipeline terms in expressions is both more readable and runs faster, but
CustomFactor is like the escape hatch to add new novel operations.
The issue you are having is that you are trying to use the symbolic pipeline
objects in the compute() method of your custom factor.
def compute(self, today, assets, out, values):
slow_sma = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=kwargs['slow_window_length'])
fast_sma = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=kwargs['fast_window_length'])
out[:] = (fast_sma - slow_sma) / USEquityPricing.close
Because we are in compute(), we have already moved on to step two, execute;
however, you are trying to define new symbolic terms: slow_sma and
fast_sma. Your pipeline can actually be defined without a CustomFactor at
all:
def make_pipeline():
slow_sma = SimpleMovingAverage(
inputs=[USEquityPricing.close],
window_length=200,
)
fast_sma = SimpleMovingAverage(
inputs=[USEquityPricing.close],
window_length=30,
)
difference = (fast_sma - slow_sma) / USEquityPricing.close.latest
universe = difference.notnull()
return Pipeline(
{
'testing_factor': difference,
'longs': difference.top(10),
'shorts': difference.bottom(10)
},
screen=universe,
)
This defines slow_sma and fast_sma as symbolic objects which represent the
work needed to compute their values. We can then use them in the same arithmetic
expression that you were attempting in the compute(); however, this time it is
still in step one, define the computation, so there are no issues. Some small
notes: you cannot divide through by USEquityPricing.close, you need to add the
.latest because that is what represents the most recent day's close. Also,
for float data type terms like difference, I believe .notnull() and
.notnan() are the same so you only need one of them.
Using this definition, I was able to run your algorithm for the given time
window without any other errors.
One last thing, which you may already know, Zipline has a CLI which can be used
to run algorithms. Instead of putting the call to run_algorithm at the bottom
of your algorithm, you can invoke Zipline on your file like:
$ zipline run -f algo.py -s 2015 -e 2017 --capital-base 10000
I hope this helps explain your issue, and helps you understand a little more
about pipeline in general!
You're awesome! Thank you sooo much for taking the time to explain this to me. i_owe_ya +=1
Most helpful comment
Hey, it looks like the issue is that you are mixing up the pipeline expression
objects and the data they represent. In the Pipeline API, computation happens in
two steps:
These two steps are completely isolated and act on different data types.
Defining the computation
The first step, defining the computation, acts on Pipeline objects like
Filter,Factor, andClassifier. These objects represent a step in thecomputation you want to perform. The general name for objects in this space is a
Term, which encompasses all of the different types of computations which canbe run. There are two high level types of terms:
A loadable term is a term which represents loading data into memory. For
example,
USEquityPricing.closerepresents the work needed to fetch the closeprices into memory. These terms can only serve as inputs to computable terms.
A computable term represents some computation on the in-memory data which are
either loaded from loadable terms, or are the results of executing other
computable terms. For example,
USequityPricing.close.latestrepresents thecomputation needed to slice off just the most recent day of close prices.
Pipeline defines a rich set of operations which act on these symbolic
objects. These objects may be used in arithmetic expressions to define new
computable terms. They also have a lot of methods to express common
computations, for example:
.abs().Execute the computation
The second step, execute the computation, acts on 2d numpy arrays of shape
(dates, assets). This is where we translate the symbolic computation defined in
the first step into actual data and perform the given operations.
Because we have defined the computation up front, we can perform optimizations
like common subexpression elimination and temporary elimination. Overall, this
makes the computation faster than a naive numpy solution. By controlling the
execution environment, we can also ensure that adjustments and amendments are
being applied correctly in a point-in-time way.
CustomFactorCustomFactoris a little weird because it straddles the two steps. ACustomFactoris a symbolic representation of a computation _paired_ with theconcrete implementation of the computation. The instance itself is a
Term, andthe
compute()method is what gets called in the execute step, meaning it actson numpy arrays.
CustomFactorexists for the cases where you cannot express a computationnaturally with the built-in methods or as an arithmetic expression. In general,
using Pipeline terms in expressions is both more readable and runs faster, but
CustomFactoris like the escape hatch to add new novel operations.The issue you are having is that you are trying to use the symbolic pipeline
objects in the
compute()method of your custom factor.Because we are in
compute(), we have already moved on to step two, execute;however, you are trying to define new symbolic terms:
slow_smaandfast_sma. Your pipeline can actually be defined without aCustomFactoratall:
This defines
slow_smaandfast_smaas symbolic objects which represent thework needed to compute their values. We can then use them in the same arithmetic
expression that you were attempting in the
compute(); however, this time it isstill in step one, define the computation, so there are no issues. Some small
notes: you cannot divide through by
USEquityPricing.close, you need to add the.latestbecause that is what represents the most recent day's close. Also,for
floatdata type terms likedifference, I believe.notnull()and.notnan()are the same so you only need one of them.Using this definition, I was able to run your algorithm for the given time
window without any other errors.
Zipline CLI
One last thing, which you may already know, Zipline has a CLI which can be used
to run algorithms. Instead of putting the call to
run_algorithmat the bottomof your algorithm, you can invoke Zipline on your file like:
I hope this helps explain your issue, and helps you understand a little more
about pipeline in general!