Welcome to Quantopian! The Getting Started Tutorial will guide you through researching and developing a quantitative trading strategy in Quantopian. It covers many of the basics of Quantopian's API, and is designed for those who are new to the platform. All you need to get started on this tutorial is to have some basic Python programming skills.
What is a Trading Algorithm?
A trading algorithm is a computer program that defines a set of rules for buying and selling assets. Most trading algorithms make decisions based on mathematical or statistical models that are derived from research conducted on historical data.
Where do I start?
The first step to writing a trading algorithm is to find an economic or statistical relationship on which we can base our strategy. To do this, we can use Quantopian's Research environment to access and analyze historical datasets available in the platform. Research is a Jupyter Notebook environment that allows us to run Python code in units called "cells." For example, the following code plots the daily closing price for Apple Inc. (AAPL), along with its 20 and 50 day moving averages:
```# Research environment functions
from quantopian.research import prices, symbols

# Pandas library: https://pandas.pydata.org/
import pandas as pd

# Query historical pricing data for AAPL
aapl_close = prices(
assets=symbols('AAPL'),
start='2013-01-01',
end='2016-01-01',
)

# Compute 20 and 50 day moving averages on
# AAPL's pricing data
aapl_sma20 = aapl_close.rolling(20).mean()
aapl_sma50 = aapl_close.rolling(50).mean()

# Combine results into a pandas DataFrame and plot
pd.DataFrame({
'AAPL': aapl_close,
'SMA20': aapl_sma20,
'SMA50': aapl_sma50
}).plot(
title='AAPL Close Price / SMA Crossover'
);
```
To use the above code copy and paste it into a new notebook in Research, or click the Get Notebook button at the top right corner of this lesson. Once you are in Research, press Shift+Enter to run the cell. The output should look like this:
In the next lesson we will use Research to explore Quantopian's datasets. Then, we will define our trading strategy and test whether it can effectively predict returns based on historical data. Finally, we will use our findings to develop and test a trading algorithm in the Interactive Development Environment (IDE).
Lessons 2-4 will be conducted in the Research environment. To get set up in Research, create a new notebook or clone the notebook version of this lesson by clicking Get Notebook below.
Data Exploration
Research provides utility functions to query pricing, volume, and returns data for 8000+ US equities, from 2002 up to the most recently completed trading day. These functions take an asset (or list of assets) along with a start and end date, and return a pandas Series (or DataFrame) indexed by date.
Let's define the period of time we want to explore and use the returns function to query data for AAPL:
```# Research environment functions
from quantopian.research import returns, symbols

# Select a time range to inspect
period_start = '2014-01-01'
period_end = '2014-12-31'

# Query returns data for AAPL
# over the selected time range
aapl_returns = returns(
assets=symbols('AAPL'),
start=period_start,
end=period_end,
)

# Display first 10 rows
aapl_returns.head(10)
```
Alternative Data
In addition to pricing and volume data, Quantopian integrates a number of other datasets that include corporate fundamentals, stock sentiment analysis, and consensus estimates, to name a few. You can find the complete list of datasets in Quantopian's Data Reference.
Our goal in this tutorial will be to build an algorithm that selects and trades assets based on sentiment data, so let's take a look at Sentdex's News Sentiment dataset. The sentiment scores are generated from four simple moving average (SMA) factors over the last 100, 250, 500, and 5000 news events for each stock. News events are pulled from over 20 sources including The Wall Street Journal, CNBC, Forbes, Business Insider, and Yahoo Finance.
We can start by inspecting the sentiment_signal from the sentiment dataset. We will query the data using Quantopian's Pipeline API, which is a powerful tool you will use over and over again to access and analyze data in Research. You will learn a lot more about the Pipeline API in the next lesson and a later tutorial. For now all you need to know is that the following code uses a data pipeline to query sentiment and returns data, and plots the results for AAPL:
```# Pipeline imports
from quantopian.research import run_pipeline
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.sentdex import sentiment
from quantopian.pipeline.domain import US_EQUITIES
from quantopian.pipeline.factors import Returns

# Pipeline definition
def make_pipeline():

returns = Returns(window_length=2)
sentiment_score = sentiment.sentiment_signal.latest

return Pipeline(
columns={
'daily_returns': returns,
'sentiment_score': sentiment_score,
},
domain=US_EQUITIES,
)

# Pipeline execution
data_output = run_pipeline(
make_pipeline(),
start_date=period_start,
end_date=period_end
)

# Filter results for AAPL
aapl_output = data_output.xs(
symbols('AAPL'),
level=1
)

# Plot results for AAPL
aapl_output.plot(subplots=True);
```
When exploring a dataset, try to look for patterns that might serve as the basis for a trading strategy. For example, the above plot shows some matching spikes between daily returns and stocktwits message volume, and in some cases the direction of the spikes in returns match the direction of AAPL's sentiment score. This looks interesting enough that we should conduct more rigorous statistical tests to confirm our hypotheses.
In the next lesson we will cover the Pipeline API in more depth.
Pipeline API
The Pipeline API is a powerful tool for cross-sectional analysis of asset data. It allows us to define a set of calculations on multiple data inputs and analyze a large amount of assets at a time. Some common uses for the Pipeline API include:
• Selecting assets based on filtering rules
• Ranking assets based on a scoring function
• Calculating portfolio allocations
To begin, let's import the Pipeline class and create a function that returns an empty pipeline. Putting our pipeline definition inside a function helps us keep things organized as our pipeline grows in complexity. This is particularly helpful when transferring data pipelines between Research and the IDE.
```# Pipeline class
from quantopian.pipeline import Pipeline

def make_pipeline():
# Create and return an empty Pipeline
return Pipeline()
```
To add an output to our pipeline we need to include a reference to a dataset, and specify the computations we want to carry out on that data. For example, we will add a reference to the close column from the EquityPricing dataset. Then, we can define our output to be the latest value from this column as follows:
```# Import Pipeline class and EquityPricing dataset
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import EquityPricing
from quantopian.pipeline.domain import US_EQUITIES

def make_pipeline():
# Get latest closing price
close_price = EquityPricing.close.latest

# Return Pipeline containing latest closing price
return Pipeline(
columns={
'close_price': close_price,
},
domain=US_EQUITIES,
)
```
The Pipeline API also provides a number of built-in calculations, some of which are computed over trailing windows of data. For example, the following code imports Sentdex's sentiment dataset and defines an output as the 3 day moving average of its sentiment_signal column:
```# Import Pipeline class, datasets, and domain.
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import EquityPricing
from quantopian.pipeline.data.sentdex import sentiment
from quantopian.pipeline.domain import US_EQUITIES

# Import built-in moving average calculation
from quantopian.pipeline.factors import SimpleMovingAverage

def make_pipeline():
# Get latest closing price
close_price = EquityPricing.close.latest

# Calculate 3 day average of sentiment_signal.
sentiment_score = SimpleMovingAverage(
inputs=[sentiment.sentiment_signal],
window_length=3,
)

# Return Pipeline containing close_price
# and sentiment_score
return Pipeline(
columns={
'close_price': close_price,
'sentiment_score': sentiment_score,
},
domain=US_EQUITIES,
)```
Universe Selection
An important part of developing a strategy is defining the set of assets that we want to consider trading in our portfolio. We usually refer to this set of assets as our trading universe. A trading universe should be as large as possible, while also excluding assets that aren't appropriate for our portfolio. For example, we might want to exclude stocks that are illiquid or difficult to trade.
Quantopian's QTradableStocksUS universe offers this characteristic. We can set QTradableStocksUS as our trading universe using the screen parameter of our pipeline constructor:
```# Import Pipeline class and datasets
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import EquityPricing
from quantopian.pipeline.data.sentdex import sentiment
from quantopian.pipeline.domain import US_EQUITIES

# Import built-in moving average calculation
from quantopian.pipeline.factors import SimpleMovingAverage

# Import built-in trading universe
from quantopian.pipeline.filters import QTradableStocksUS

def make_pipeline():
# Create a reference to our trading universe
base_universe = QTradableStocksUS()

# Get latest closing price
close_price = EquityPricing.close.latest

# Calculate 3 day average sentiment signal.
sentiment_score = SimpleMovingAverage(
inputs=[sentiment.sentiment_signal],
window_length=3,
)

# Return Pipeline containing close_price and
# sentiment_score that has our trading universe as screen
return Pipeline(
columns={
'close_price': close_price,
'sentiment_score': sentiment_score,
},
screen=base_universe,
domain=US_EQUITIES,
)
```
Now that our pipeline definition is complete, we can execute it over a specific period of time using run_pipeline. The output will be a pandas DataFrame indexed by date and asset, with columns corresponding to the outputs we added to our pipeline definition:
```# Import run_pipeline method
from quantopian.research import run_pipeline

# Execute pipeline created by make_pipeline
# between start_date and end_date
pipeline_output = run_pipeline(
make_pipeline(),
start_date='2013-01-01',
end_date='2013-12-31'
)

# Display last 10 rows
pipeline_output.tail(10)
```
In the next lesson we will formalize the strategy our algorithm will use to select assets to trade. Then, we will use a factor analysis tool to evaluate the predictive power of our strategy over historical data.
Strategy Definition
Now that we have learned how to access and manipulate data in Quantopian, let's construct a data pipeline for our long-short equity strategy. In general, long-short equity strategies consist of modeling the relative value of assets with respect to each other, and placing bets on the sets of assets that we are confident will increase (long) and decrease (short) the most in value.
Long-short equity strategies profit as the spread in returns between the sets of high and low value assets increases. The quality of a long-short equity strategy relies entirely on the quality of its underlying ranking model. In this tutorial we will use a simple ranking schema for our strategy:
Strategy: We will consider assets with a high 3 day average sentiment score as high value, and assets with a low 3 day average sentiment score as low value.
Strategy Analysis
We can define the strategy above using SimpleMovingAverage and sentdex's sentiment_signal data, similar to the pipeline we created in the previous lesson:
```# Pipeline imports
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.sentdex import sentiment
from quantopian.pipeline.domain import US_EQUITIES
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.filters import QTradableStocksUS

# Pipeline definition
def  make_pipeline():

base_universe = QTradableStocksUS()

sentiment_score = SimpleMovingAverage(
inputs=[sentdex.sentiment_signal],
window_length=3,
)

return Pipeline(
columns={
'sentiment_score': sentiment_score,
},
screen=base_universe
)```
For simplicity, we will only analyze the top 350 and bottom 350 stocks ranked by sentiment_score. We can create pipeline filters for these sets using the top and bottom methods of our sentiment_score output, and combine them using the | operator to get their union. Then, we will remove anything outside of our tradable universe by using the & operator to get the intersection between our filter and our universe:
```# Pipeline imports
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.sentdex import sentiment
from quantopian.pipeline.domain import US_EQUITIES
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.filters import QTradableStocksUS

# Pipeline definition
def  make_pipeline():

base_universe = QTradableStocksUS()

sentiment_score = SimpleMovingAverage(
inputs=[sentdex.sentiment],
window_length=3,
)

# Create filter for top 350 and bottom 350
# assets based on their sentiment scores
top_bottom_scores = (
sentiment_score.top(350) | sentiment_score.bottom(350)
)

return Pipeline(
columns={
'sentiment_score': sentiment_score,
},
# Set screen as the intersection between our filter
# and trading universe
screen=(
base_universe
& top_bottom_scores
)
)```
Next, let's run our pipeline over a 3 year period to get an output we can use for our analysis. This will take ~3 minutes.
```# Import run_pipeline method
from quantopian.research import run_pipeline

# Specify a time range to evaluate
period_start = '2013-01-01'
period_end = '2016-01-01'

# Execute pipeline over evaluation period
pipeline_output = run_pipeline(
make_pipeline(),
start_date=period_start,
end_date=period_end
)```
In addition to our pipeline's data, we will need pricing data for all assets present in this period. We can get a list of these assets from our pipeline output's index, and pass that list to prices to get the pricing data we need:
```# Import prices function
from quantopian.research import prices

# Get list of unique assets from the pipeline output
asset_list = pipeline_output.index.levels[1].unique()

# Query pricing data for all assets present during
# evaluation period
asset_prices = prices(
asset_list,
start=period_start,
end=period_end
)```
Now we can use Quantopian's open source factor analysis tool, Alphalens, to test the quality of our selection strategy. First, let's combine our factor and pricing data using get_clean_factor_and_forward_returns. This function classifies our factor data into quantiles and computes forward returns for each security for multiple holding periods. We will separate our factor data into 2 quantiles (the top and bottom half), and use 1, 5 and 10 day holding periods:
```# Import Alphalens
import alphalens as al

# Get asset forward returns and quantile classification
# based on sentiment scores
factor_data = al.utils.get_clean_factor_and_forward_returns(
factor=pipeline_output['sentiment_score'],
prices=asset_prices,
quantiles=2,
periods=(1,5,10),
)

# Display first 5 rows
factor_data.head(5)
```
Having our data in this format allows us to use several of Alphalens's analysis and plotting tools. Let's start by looking at the mean returns by quantile over the entire period. Because our goal is to build a long-short strategy, we want to see the lower quantile (1) have negative returns and the upper quantile(2) have positive returns:
```# Calculate mean return by factor quantile
mean_return_by_q, std_err_by_q = al.performance.mean_return_by_quantile(factor_data)

# Plot mean returns by quantile and holding period
# over evaluation time range
al.plotting.plot_quantile_returns_bar(
mean_return_by_q.apply(
al.utils.rate_of_return,
axis=0,
args=('1D',)
)
);
```
We can also plot the cumulative returns of a factor-weighted long-short portfolio with a 5 day holding period using the following code:
```import pandas as pd
# Calculate factor-weighted long-short portfolio returns
ls_factor_returns = al.performance.factor_returns(factor_data)

# Plot cumulative returns for 5 day holding period
al.plotting.plot_cumulative_returns(ls_factor_returns['5D'], '5D', freq=pd.tseries.offsets.BDay());
```
The plot above shows some drawdown periods, and this analysis does not yet take into account transaction costs or market impact. With a cumulative return of only about 3% over four years, it is not a very promising strategy. At this point we really should conduct a deeper analysis using Alphalens and then iterate on our strategy idea. But for the sake of this tutorial, let's continue with our strategy as it is.
Having defined and tested a strategy, let's use it to build and test a long-short equity algorithm. The rest of the tutorial will cover the Algorithm API and will take place in the Interactive Development Environment (IDE).
In the previous lesson we created a data pipeline that selects assets to consider for our portfolio, and calculates alpha scores for those assets. The remaining lessons will be conducted in Quantopian's Interactive Development Environment (IDE) where we will build a trading algorithm, attach our data pipeline to it, and use alpha scores for portfolio construction. Then, we will analyze our algorithm's performance under more realistic conditions by simulating it over historical data. This type of historical simulation is commonly known as "backtesting."
Algorithm API & Core Functions
The next step will be to build a basic structure for our trading algorithm using Quantopian's Algorithm API. The Algorithm API provides functions that facilitate order scheduling and execution, and allow us to initialize and manage parameters in our algorithms.
Let's take a look at some of the core functions we will use in our algorithm:
initialize(context)
initialize is called exactly once when our algorithm starts running and requires context as input. Any parameter initialization and one-time startup logic should go here.
context is an augmented Python dictionary used for maintaining state throughout the simulation process, and can be referenced in different parts of our algorithm. Any variables that we want to persist between function calls should be stored in context instead of using global variables. Context variables can be accessed and initialized using dot notation (context.some_attribute).
before_trading_start(context, data)
before_trading_start is called once per day before the market opens and requires context and data as input. context is a reference to the same dictionary from initialize, and data is an object that stores several API functions that allow us to look up current or historical pricing and volume data for any asset.
before_trading_start is also where we get our pipeline's output, and do any pre-processing of the resulting data before using it for portfolio construction. We will cover this in the next lesson.
schedule_function(func, day_rule, time_rule)
On Quantopian, algorithms can trade equities between 9:30AM-4PM Eastern Time on regular trading days, following the NYSE trading calendar. schedule_function allows us to execute custom functions at specified dates and times. For example, we can schedule a function to rebalance our portfolio at market open on the first day of each week as follows:
```schedule_function(
rebalance,
date_rule=date_rules.week_start(),
time_rule=time_rules.market_open()
)
```
Scheduling functions should be done in initialize, and custom functions scheduled with this method need to take context and data as arguments. For a full list of date_rules and time_rules available, check out the documentation.
Next, let's build a skeleton for our trading algorithm. For now we will have our algorithm keep track of the number of days that have passed in the simulation and log different messages depending on the date and time. In the next couple of lessons we will integrate our data pipeline and add trading logic.
To run this example algorithm, create a copy by clicking the "Clone" button. Once you are in the IDE, run a backtest by clicking "Build Algorithm" (top left) or "Run Full Backtest" (top right).
```# Import Algorithm API
import quantopian.algorithm as algo

def initialize(context):
# Initialize algorithm parameters
context.day_count = 0
context.daily_message = "Day {}."
context.weekly_message = "Time to place some trades!"

# Schedule rebalance function
algo.schedule_function(
rebalance,
date_rule=algo.date_rules.week_start(),
time_rule=algo.time_rules.market_open()
)

def before_trading_start(context, data):
# Execute any daily actions that need to happen
# before the start of a trading session
context.day_count += 1
log.info(context.daily_message, context.day_count)

def rebalance(context, data):
# Execute rebalance logic
log.info(context.weekly_message)

```
Now that we have a basic structure for a trading algorithm, let's add the data pipeline we created in the previous lesson to our algorithm.
Data Processing in Algorithms
The next step will be to integrate the data pipeline we built in Research into our algorithm. One important distinction from Research is that during a backtest our pipeline will be executed each day as the simulation progresses, so we won't need to include start_date and end_date arguments.
In order to use our data pipeline in an algorithm, the first step is to add a reference to it in the algorithm's initialize function. This is done using the attach_pipeline method, which requires two inputs: a reference to our Pipeline object (which we construct using make_pipeline), and a String name to identify it.
```# Import Algorithm API
import quantopian.algorithm as algo

def initialize(context):
# Attach pipeline to algorithm
algo.attach_pipeline(
make_pipeline(),
'data_pipe'
)

# Schedule rebalance function
algo.schedule_function(
rebalance,
date_rule=algo.date_rules.week_start(),
time_rule=algo.time_rules.market_open()
)

def before_trading_start(context, data):
pass

def rebalance(context, data):
pass```
As mentioned above, our pipeline will process data streams and generate an output before the market opens each day. We can get our pipeline's output in before_trading_start using the pipeline_output function, which takes the pipeline name we specified in initialize, and returns the pandas DataFrame generated by our pipeline. For now we can use our rebalance function to log the top 10 rows from our pipeline's output.
```# Import Algorithm API
import quantopian.algorithm as algo

def initialize(context):
# Attach pipeline to algorithm
algo.attach_pipeline(
make_pipeline(),
'data_pipe'
)

# Schedule rebalance function
algo.schedule_function(
rebalance,
date_rule=algo.date_rules.week_start(),
time_rule=algo.time_rules.market_open()
)

def before_trading_start(context, data):
# Get pipeline output and
# store it in context
context.pipeline_data = algo.pipeline_output(
'data_pipe'
)

def rebalance(context, data):
# Display first 10 rows
# of pipeline output
log.info(context.pipeline_data.head(10))```
Now, let's add the make_pipeline function we built in Research to our algorithm. Instead of limiting the number of assets like we did for our analysis, our algorithm should consider all assets in the trading universe for which it has a sentiment score. For this we can use the notnull method of our sentiment_score output to create a filter, and get its intersection with the tradable universe using the & operator:
```# Import Algorithm API
import quantopian.algorithm as algo

# Import Pipeline class and datasets
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import EquityPricing
from quantopian.pipeline.domain import US_EQUITIES
from quantopian.pipeline.data.sentdex import sentiment

# Import built-in moving average calculation
from quantopian.pipeline.factors import SimpleMovingAverage

# Import built-in trading universe
from quantopian.pipeline.filters import QTradableStocksUS

def initialize(context):
# Attach pipeline to algorithm
algo.attach_pipeline(
make_pipeline(),
'data_pipe'
)

# Schedule rebalance function
algo.schedule_function(
rebalance,
date_rule=algo.date_rules.week_start(),
time_rule=algo.time_rules.market_open()
)

def rebalance(context, data):
# Get pipeline output
pipeline_data = algo.pipeline_output('data_pipe')

# Display first 10 rows of pipeline output
log.info(pipeline_data.head(10))

def make_pipeline():
"""
Pipeline definition
"""
# Create a reference to our trading universe
base_universe = QTradableStocksUS()

# Calculate 3 day average of sentiment scores
sentiment_score = SimpleMovingAverage(
inputs=[sentiment.sentiment_signal],
window_length=3,
)

# Return Pipeline containing sentiment_score that has our trading universe as a screen
return Pipeline(
columns={
'sentiment_score': sentiment_score,
},
screen=base_universe & sentiment_score.notnull(),
domain=US_EQUITIES,
)

```
Our algorithm now selects a tradable universe of assets each day, and produces alpha scores we can use to determine asset allocation within our portfolio. In the next lesson we will learn how to construct an optimal portfolio based on the alpha scores generated by our data pipeline.
Portfolio Management
In the previous lesson we incorporated a data pipeline into our trading algorithm. Now it's 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,
]
)```
Risk Management
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 sentdex's 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

# Import Pipeline class and datasets
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import EquityPricing
from quantopian.pipeline.domain import US_EQUITIES
from quantopian.pipeline.data.sentdex import sentiment

# Import built-in moving average calculation
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(),
)

# Pipeline definition
def make_pipeline():
"""
Pipeline definition
"""
# Create a reference to our trading universe
base_universe = QTradableStocksUS()

# Calculate 3 day average of sentiment scores
sentiment_score = SimpleMovingAverage(
inputs=[sentiment.sentiment_signal],
window_length=3,
)
# Return Pipeline containing sentiment_score that has our trading universe as a screen
return Pipeline(
columns={
'sentiment_score': sentiment_score,
},
screen=base_universe & sentiment_score.notnull(),
domain=US_EQUITIES,
)

def rebalance(context, data):
# Get pipeline outputs
pipeline_data = algo.pipeline_output('data_pipe')
risk_factor_betas = algo.pipeline_output('risk_pipe')

# Retrieve alpha from pipeline output
alpha = 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(
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.
Backtest Analysis
Once your backtest has finished running, click on the "Notebook" tab.
This will display a Research notebook with the following code:
```bt = get_backtest('5a4e4faec73c4e44f218170a')
bt.create_full_tear_sheet()
```
Note: The alpha-numeric string in your notebook will be different than the one shown above. This string is your backtest's unique identifier within Quantopian. It can also be found in the URL of the full backtest results page.
Executing this cell (Shift+Enter) will load data generated by your backtest into the research notebook, and use it to create a Pyfolio tear sheet. Pyfolio is Quantopian's open source tool for portfolio and risk analysis. It provides a number of visualization tools that are designed to help you better understand your algorithm's behavior and risk exposures over time.
For example, the plot below shows our portfolio's rolling exposure to the market over time. One of the reasons we wanted to construct a long-short equity trading algorithm was to maintain a low correlation to the market, so we want this plot to be consistently around 0 over the entire backtesting period.
Another interesting part of the tear sheet is the Performance Attribution section. The plot below uses Quantopian's Risk Model to illustrate how much of the returns can be attributed to your strategy, and how much of it comes from common risk factors.
We can see above that most of our portfolio's total returns come from specific returns. This suggests the algorithm's performance isn't coming from exposure to common risk factors, which is a good thing.
Congratulations on completing the Getting Started Tutorial on Quantopian! Now that you are familiar with the platform's API, try researching and developing your own strategies and submit them to the contest.
If you need ideas, check out the Lecture Series to learn more about quantitative finance, or look at ideas that other members have shared in the community.

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian.

In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.