In the previous lesson we incorporated a data pipeline into our trading algorithm. Now its
time to define how our algorithm will use alpha scores generated by our pipeline to rebalance
its portfolio. Our goal will be to find a target portfolio that maximizes returns based on
alpha scores, while maintaining a specific structure defined by a set of rules or constraints.
This is usually referred to as a

portfolio optimization problem.

Quantopian's Optimize API makes it easy for us to turn the output of our pipeline into an
objective and a set of constraints. We can then use
order_optimal_portfolio to transition our current portfolio
to a target portfolio that satisfies our specifications.

The first step is to define an objective. We will use
MaximizeAlpha, which will attempt to allocate capital to
assets proportional to their alpha scores.

# Import Optimize API module
import quantopian.optimize as opt
def rebalance(context, data):
# Retrieve alpha from pipeline output
alpha = context.pipeline_data.sentiment_score
if not alpha.empty:
# Create MaximizeAlpha objective
objective = opt.MaximizeAlpha(alpha)

Next, we need to specify the list of constraints that we want our target portfolio to satisfy.
Let's start by defining some threshold values in initialize
and store them in our context variable:

# Constraint parameters
context.max_leverage = 1.0
context.max_pos_size = 0.015
context.max_turnover = 0.95

Now, we can specify our constraints in rebalance using the
thresholds we defined above:

# Import Optimize API module
import quantopian.optimize as opt
def rebalance(context, data):
# Retrieve alpha from pipeline output
alpha = context.pipeline_data.sentiment_score
if not alpha.empty:
# Create MaximizeAlpha objective
objective = opt.MaximizeAlpha(alpha)
# Create position size constraint
constrain_pos_size = opt.PositionConcentration.with_equal_bounds(
-context.max_pos_size,
context.max_pos_size
)
# Constrain target portfolio's leverage
max_leverage = opt.MaxGrossExposure(context.max_leverage)
# Ensure long and short books
# are roughly the same size
dollar_neutral = opt.DollarNeutral()
# Constrain portfolio turnover
max_turnover = opt.MaxTurnover(context.max_turnover)

Finally, we can pass our objective and list of constraints to
order_optimal_portfolio to calculate a target portfolio,
and issue the orders necessary to transition our current portfolio to an optimal state:

# Import Algorithm API
import quantopian.algorithm as algo
# Import Optimize API
import quantopian.optimize as opt
def rebalance(context, data):
# Retrieve alpha from pipeline output
alpha = context.pipeline_data.sentiment_score
if not alpha.empty:
# Create MaximizeAlpha objective
objective = opt.MaximizeAlpha(alpha)
# Create position size constraint
constrain_pos_size = opt.PositionConcentration.with_equal_bounds(
-context.max_pos_size,
context.max_pos_size
)
# Constrain target portfolio's leverage
max_leverage = opt.MaxGrossExposure(context.max_leverage)
# Ensure long and short books
# are roughly the same size
dollar_neutral = opt.DollarNeutral()
# Constrain portfolio turnover
max_turnover = opt.MaxTurnover(context.max_turnover)
# Rebalance portfolio using objective
# and list of constraints
algo.order_optimal_portfolio(
objective=objective,
constraints=[
constrain_pos_size,
max_leverage,
dollar_neutral,
max_turnover,
]
)

In addition to setting constraints on our target portfolio's structure, we will want to
limit its exposure to common risk factors that could have an impact on its performance.
For example, because of the transient nature of
stocktwits' sentiment data and our intent to capitalize
on sentiment score spikes, our algorithm could be exposed to short term reversal risk.

We will use Quantopian's

Risk Model to manage
our portfolio's exposure to common risk factors. The Risk Model calculates asset exposure
to 16 different risk factors: 11 sector factors and 5 style factors (including short term
reversal). We can easily access this data in our algorithm using the

risk_loading_pipeline function, which returns a data
pipeline that produces a column of output for each factor in the Risk Model.

Similar to our data pipeline, we will need to attach the risk data pipeline to our
algorithm, and provide a name to identify it. Then we can get its output in
before_trading_start and store it in
context:

# Import Algorithm API
import quantopian.algorithm as algo
# Import Risk API method
from quantopian.pipeline.experimental import risk_loading_pipeline
def initialize(context):
# Constraint parameters
context.max_leverage = 1.0
context.max_pos_size = 0.015
context.max_turnover = 0.95
# Attach data pipelines
algo.attach_pipeline(
make_pipeline(),
'data_pipe'
)
algo.attach_pipeline(
risk_loading_pipeline(),
'risk_pipe'
)
# Schedule rebalance function
algo.schedule_function(
rebalance,
algo.date_rules.week_start(),
algo.time_rules.market_open(),
)
def before_trading_start(context, data):
# Get pipeline outputs and
# store them in context
context.pipeline_data = algo.pipeline_output(
'data_pipe'
)
context.risk_factor_betas = algo.pipeline_output(
'risk_pipe'
)

The next step is to add a RiskModelExposure constraint
to our portfolio optimization logic. This constraint takes the data generated by the
Risk Model and sets a limit on the overall exposure of our target portfolio to each of
the factors included in the model.

# Constrain target portfolio's risk exposure
# By default, max sector exposure is set at
# 0.2, and max style exposure is set at 0.4
factor_risk_constraints = opt.experimental.RiskModelExposure(
context.risk_factor_betas,
version=opt.Newest
)

Finally, the following algorithm encompasses our strategy and portfolio construction logic,
and is ready to be backtested. After cloning the algorithm, run a full backtest by clicking
on "Run Full Backtest" in the top right corner of the IDE.

# Import Algorithm API
import quantopian.algorithm as algo
# Import Optimize API
import quantopian.optimize as opt
# Pipeline imports
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.psychsignal import stocktwits
from quantopian.pipeline.factors import SimpleMovingAverage
# Import built-in universe and Risk API method
from quantopian.pipeline.filters import QTradableStocksUS
from quantopian.pipeline.experimental import risk_loading_pipeline
def initialize(context):
# Constraint parameters
context.max_leverage = 1.0
context.max_pos_size = 0.015
context.max_turnover = 0.95
# Attach data pipelines
algo.attach_pipeline(
make_pipeline(),
'data_pipe'
)
algo.attach_pipeline(
risk_loading_pipeline(),
'risk_pipe'
)
# Schedule rebalance function
algo.schedule_function(
rebalance,
algo.date_rules.week_start(),
algo.time_rules.market_open(),
)
def before_trading_start(context, data):
# Get pipeline outputs and
# store them in context
context.pipeline_data = algo.pipeline_output('data_pipe')
context.risk_factor_betas = algo.pipeline_output('risk_pipe')
# Pipeline definition
def make_pipeline():
sentiment_score = SimpleMovingAverage(
inputs=[stocktwits.bull_minus_bear],
window_length=3,
mask=QTradableStocksUS()
)
return Pipeline(
columns={
'sentiment_score': sentiment_score,
},
screen=sentiment_score.notnull()
)
def rebalance(context, data):
# Retrieve alpha from pipeline output
alpha = context.pipeline_data.sentiment_score
if not alpha.empty:
# Create MaximizeAlpha objective
objective = opt.MaximizeAlpha(alpha)
# Create position size constraint
constrain_pos_size = opt.PositionConcentration.with_equal_bounds(
-context.max_pos_size,
context.max_pos_size
)
# Constrain target portfolio's leverage
max_leverage = opt.MaxGrossExposure(context.max_leverage)
# Ensure long and short books
# are roughly the same size
dollar_neutral = opt.DollarNeutral()
# Constrain portfolio turnover
max_turnover = opt.MaxTurnover(context.max_turnover)
# Constrain target portfolio's risk exposure
# By default, max sector exposure is set at
# 0.2, and max style exposure is set at 0.4
factor_risk_constraints = opt.experimental.RiskModelExposure(
context.risk_factor_betas,
version=opt.Newest
)
# Rebalance portfolio using objective
# and list of constraints
algo.order_optimal_portfolio(
objective=objective,
constraints=[
constrain_pos_size,
max_leverage,
dollar_neutral,
max_turnover,
factor_risk_constraints,
]
)

In the next lesson you will learn how to analyze your backtest results in more depth.