A few weeks ago, we introduced the Quantopian Risk Model to make it easier to build strategies that are suitable for capital allocations. At the time of launch, the Quantopian risk model output was available in research for performance attribution. Performance attribution is a great tool for understanding your risk exposure after an algorithm has completed, but because it's done after the fact, it doesn't assist an algorithm with dynamically managing its risk exposure as it runs. Today, we're introducing new tools to help manage your risk in algorithms.
How Do I Access Risk Model Data?
The easiest way to access risk model data in an algorithm is to use risk_loading_pipeline, a new function we've added to the quantopian.pipeline.experimental module. risk_loading_pipeline constructs a pre-configured Pipeline that produces a column of output for each factor in the Quantopian Risk Model.
The pipeline created by risk_loading_pipeline works just like any other pipeline. Here's a simple example of how you can use risk_loading_pipeline in an algorithm:
from quantopian.pipeline.experimental import risk_loading_pipeline
def initialize(context):
attach_pipeline(risk_loading_pipeline(), 'risk_loading_pipeline')
def before_trading_start(context, data):
context.risk_loading_pipeline = pipeline_output('risk_loading_pipeline')
# Multiple pipelines are supported in an algorithm.
context.my_other_pipeline = pipeline_output('my_other_pipeline')
def my_method(context, data):
# Access context.risk_loading_pipeline in your methods.
The value of context.risk_loading_pipeline in the example is a DataFrame containing a row for every asset and a column for every output of the pipeline. Since risk_loading_pipeline has an output for each column of the Quantopian Risk Model, its result will look like this:
The risk_loading_pipeline is a convenient way to access all the common risk factors in one go. You can also access individual risk factors, like in this example which accesses only the Size factor:
from quantopian.pipeline import Pipeline
from quantopian.pipeline.experimental import Size
def initialize(context):
pipe = Pipeline()
pipe.add(Size(), 'size')
attach_pipeline(pipe, 'size_factor_pipeline')
def before_trading_start(context, data):
context.size_factor_pipeline = pipeline_output('size_factor_pipeline')
def my_method(context, data):
# Access context.size_factor_pipeline in your methods.
Since you can access multiple pipelines in an algorithm, you can use the risk pipelines side-by-side with any pipelines that your algorithm has already defined. The risk model pipeline is also accessible in the research environment via run_pipeline:
from quantopian.pipeline.experimental import risk_loading_pipeline
from quantopian.research import run_pipeline
run_pipeline(risk_loading_pipeline(), '2014-01-01', '2014-01-02')
How Can I Use This Data?
One of the main use cases is to keep your common risk exposure within certain bounds, perhaps in order to meet the contest entry criteria. You can do this by passing a constraint to order_optimal_portfolio, to help place orders such that your common risk exposure thresholds are not exceeded. The snippet shows an example of how you'd constrain your portfolio exposure to common risk:
from quantopian.pipeline.experimental import risk_loading_pipeline
import quantopian.optimize as opt
def initialize(context):
attach_pipeline(risk_loading_pipeline(), 'risk_loading_pipeline')
def before_trading_start(context, data):
context.risk_loading_pipeline = pipeline_output('risk_loading_pipeline')
def place_orders(context, data):
# Constrain our risk exposures. We're using the latest version of the default bounds,
# except for momentum, where we've overridden the default and set a bound of 10% on both
# longs and shorts.
constrain_sector_style_risk = opt.experimental.RiskModelExposure(
risk_model_loadings=context.risk_loading_pipeline,
version=opt.Newest,
min_momentum=-0.1,
max_momentum=0.1,
)
order_optimal_portfolio(
objective=some_objective,
constraints=[constrain_sector_style_risk],
)
By default, the RiskModelExposure constraint will constrain the sector exposures to 18%, and the style exposures to 36%. These are both slightly under the contest entry criteria of 20% and 40% respectively. These default values may change in the future (for example, in response to a change in the contest entry criteria). If you'd always like to use in the latest version of the defaults, pass in quantopian.optimize.Newest as the version, and your algorithm's behavior will automatically update to match the new default behavior. On the other hand, if you'd like to ensure that your algorithm's behavior doesn't change when the defaults change, you can pass version=0, and your algorithm will always use the version zero defaults. Right now, version 0 is the only version of the defaults available, but we may add more.
You can also override some (or all!) of the defaults if you'd like to change your exposure limits to various factors. The snippet above shows an example where my momentum exposure is constrained to 10%, instead of the default of 36%. The documentation contains the full list of parameters that you can pass in to RiskModelExposure.
What Else Can I Do With This?
We're looking forward to working that out together! While performance attribution was a tool we had been using internally for some time, the risk data in algorithms is a new feature to both of us. Optimization constraints are a good first step (particularly to help meet the contest entry criteria), but we'd like to work together on:
- Exploring use of the risk data in alpha generation and combination. While the risk model by itself is not an alpha model, there may be interesting approaches in 1) combining the risk factors with your existing alpha factors, 2) finding a combination of the risk factors that is an alpha factor, or 3) using the risk factors to design a factor-neutral pure alpha strategy.
- Refining best practices for using the risk data in optimization constraints.
In the weeks to come we'll be posting more examples of ways to use the risk data in algorithms, along with more lectures and educational material about how to use the risk model across the platform.
Putting It All Together
The attached backtest is an example of a full-featured algorithm using the risk model. Thanks to the risk constraints, it now meets the new contest entry criteria.