DollarNeutral and NetGroupExposure constraints

Hi Quantopian,
I'm starting to use the different constraints available alongside the objective functions and had questions on a couple of them:

DollarNeutral: "Requires that the sum of the weights (positive or negative) of all assets in the portfolio fall between +-(tolerance)."
i.e. -tol <= sum(weights) <= tol
This definition does not include the price of the assets to check for "dollar" neutrality. Shouldn't dollar neutral portfolio mean that the sum of (weights * prices) fall between +- tolerance? A stock with small weight (0.01) and bigger price/share ($6000) will have a bigger dollar impact than a stock with small weight and small price/share. e.g. if I have 3 stock [A, B, C] in the portfolio with weights [-0.02, 0.3, 0.15] and prices [1200, 14, 30], dollar neutral should mean either 1. (-0.02*1200+0.3*14+0.15*30) <= (abs(-0.02)*1200 + abs(0.3)*14 + abs(0.15)*30) * tol OR 2. (-0.02*1200+0.3*14+0.15*30) <= tol Same question for Net GroupExposure: Shouldn't price be included to calc sector neutrality in the definition below? "sum of the weights of assets mapped to that label should fall between a lower and upper bounds." Thanks 7 responses The 'DollarNeutral' and 'NetGroupExposure' ARE dollar weighted. Whenever 'weight' is used it is the 'price x shares' or total dollar value of a position. It is NOT the weight of quantity of shares. Assume one has a total portfolio value of$10,000. An order is placed with targets 'weights' of .50 AAPL and .50 IBM. This results in (approximately) $5,000 AAPL and$5,000 IBM. It does NOT result in an equal number of shares of each. One reason it's approximate is that one cannot order fractional shares. Additionally, it's approximate because the target shares are calculated at the current market price. The actual price (and therefore position value) may change as orders are filled and/or the value of a stock increases or decreases.

So yes, your definitions of 'DollarNeutral' and 'NetGroupExposure' are correct and that IS how they are implemented.

Dan,
Thanks for the explanation. So Dollar Neutral is really -tol <= ∑PiWi <= tol, where tol might be 0.01. In your example, the sum of IBM and AAPL's target weights should add up to the tolerance i.e. -0.01 <= (0.5 - 0.5) <= 0.01

@John Smith

Correct.

Note the optimize function will generate orders which meet the constraints. It assumes the last minute close price when calculating the orders. After filling the actual orders however, the weights may not be within the constraints.

Hi Dan, was going through the other available constraints and looks like it's the same treatment for :
MaxGrossLeverage : sum (abs(weight) * Price) <= % of portfolio value
PositionConcentration : each stock's (price * weight) in range (min, max)
BTW, are you a member of the Quantopian team?

@John Smith

Correct again. The 'weight' in the examples given above are all the total dollar weight, and again, not the share qty weight. Note that there isn't a 'MaxGrossLeverage' constraint. It's named 'MaxGrossExposure'. I often find it helpful to test out various functionality by writing small algorithms and logging the results. Attached is an algorithm with a MaxGrossExposure constraint varying between 1 and 2, and a DollarNeutral constraint of .1. Check out the custom data graph. It records those two metrics. Also check the logs.

Nope. Not a member of the Quantopian staff just an avid supporter.

Good luck.

8
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
 Returns 1 Month 3 Month 6 Month 12 Month
 Alpha 1 Month 3 Month 6 Month 12 Month
 Beta 1 Month 3 Month 6 Month 12 Month
 Sharpe 1 Month 3 Month 6 Month 12 Month
 Sortino 1 Month 3 Month 6 Month 12 Month
 Volatility 1 Month 3 Month 6 Month 12 Month
 Max Drawdown 1 Month 3 Month 6 Month 12 Month
'''
Simple algorithm showing how DollarNeutral and MaxGrossLeverage constraints behave.
'''

# import pipeline methods
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline

# import optimize
import quantopian.optimize as opt

# import any datasets we need
from quantopian.pipeline.data.builtin import USEquityPricing

# import numpy and pandas just in case
import numpy as np
import pandas as pd

# define any constants.
pass

def initialize(context):
"""
Called once at the start of the algorithm.
"""

schedule_function(
order_leverage_1,
date_rules.week_start(days_offset=0),
time_rules.market_open())

schedule_function(
order_leverage_2,
date_rules.week_start(days_offset=2),
time_rules.market_open())

schedule_function(
record_and_log,
date_rules.every_day(),
time_rules.market_close())

def order_leverage_1(context, data):
# Create a list of the securities to trade
# Anything NOT in this list will be closed
# Create a list of associated alphas (in the same order)
securities_to_trade_list = symbols('AAPL', 'IBM')
alpha_list = [1.0, -0.5]

# Create a pandas series with the securities and associated (anticipated) alphas
# Use this series to create a MaximizeAlpha objective
securities_to_trade_with_alphas = pd.Series(index = securities_to_trade_list, data = alpha_list)

# Create a MaxGrossExposure exposure constraint of 1 (no leverage)
leverage_1 = opt.MaxGrossExposure(1.0)
dollar_neutral = opt.DollarNeutral(.1)

# Execute the order_optimal_portfolio method with above objective and constraint
order_optimal_portfolio(objective = alpha_objective,
constraints = [leverage_1, dollar_neutral])

def order_leverage_2(context, data):
# Create a list of the securities to trade
# Anything NOT in this list will be closed
# Create a list of associated alphas (in the same order)
securities_to_trade_list = symbols('AAPL', 'IBM')
alpha_list = [1.0, -0.5]

# Create a pandas series with the securities and associated (anticipated) alphas
# Use this series to create a MaximizeAlpha objective
securities_to_trade_with_alphas = pd.Series(index = securities_to_trade_list, data = alpha_list)

# Create a MaxGrossExposure exposure constraint of 2
leverage_2 = opt.MaxGrossExposure(2.0)
dollar_neutral = opt.DollarNeutral(.1)

# Execute the order_optimal_portfolio method with above objective and constraint
order_optimal_portfolio(objective = alpha_objective,
constraints = [leverage_2, dollar_neutral])

def record_and_log(context, data):

# Log the leverage and long and short exposure
leverage = context.account.leverage
exposure = context.portfolio.positions_value / context.portfolio.portfolio_value
log_str = 'leverage = {} / long short exposure: {}'
log.info(log_str.format(leverage, exposure))

record(lev=leverage, exp=exposure)

There was a runtime error.

And then also in case one could ever think of a reason to dynamically modify the values we set, for MaxGrossExposure, PositionConcentration etc, or just for our own educational value to see what happens, this is a small edit that varies DollarNeutral() using a context variable to illustrate.

3
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
 Returns 1 Month 3 Month 6 Month 12 Month
 Alpha 1 Month 3 Month 6 Month 12 Month
 Beta 1 Month 3 Month 6 Month 12 Month
 Sharpe 1 Month 3 Month 6 Month 12 Month
 Sortino 1 Month 3 Month 6 Month 12 Month
 Volatility 1 Month 3 Month 6 Month 12 Month
 Max Drawdown 1 Month 3 Month 6 Month 12 Month
'''
Simple algorithm showing how DollarNeutral and MaxGrossLeverage constraints behave.
'''

# import pipeline methods
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline

# import optimize
import quantopian.optimize as opt

# import any datasets we need
from quantopian.pipeline.data.builtin import USEquityPricing

# import numpy and pandas just in case
import numpy as np
import pandas as pd

# define any constants.
pass

def initialize(context):
"""
Called once at the start of the algorithm.
"""
context.dollar_n = 0

schedule_function(
order_leverage_1,
date_rules.week_start(days_offset=0),
time_rules.market_open())

schedule_function(
order_leverage_2,
date_rules.week_start(days_offset=2),
time_rules.market_open())

schedule_function(
record_and_log,
date_rules.every_day(),
time_rules.market_close())

context.dollar_n += .1
if context.dollar_n > 1.4:
context.dollar_n = .1

def order_leverage_1(context, data):
# Create a list of the securities to trade
# Anything NOT in this list will be closed
# Create a list of associated alphas (in the same order)
securities_to_trade_list = symbols('AAPL', 'IBM')
alpha_list = [-.5, .5]

# Create a pandas series with the securities and associated (anticipated) alphas
# Use this series to create a MaximizeAlpha objective
securities_to_trade_with_alphas = pd.Series(index = securities_to_trade_list, data = alpha_list)

# Create a MaxGrossExposure exposure constraint of 1 (no leverage)
leverage_1 = opt.MaxGrossExposure(1.0)
dollar_neutral = opt.DollarNeutral(context.dollar_n)

# Execute the order_optimal_portfolio method with above objective and constraint
order_optimal_portfolio(objective = alpha_objective,
constraints = [leverage_1, dollar_neutral])

def order_leverage_2(context, data):
# Create a list of the securities to trade
# Anything NOT in this list will be closed
# Create a list of associated alphas (in the same order)
securities_to_trade_list = symbols('AAPL', 'IBM')
alpha_list = [-.5, .5]

# Create a pandas series with the securities and associated (anticipated) alphas
# Use this series to create a MaximizeAlpha objective
securities_to_trade_with_alphas = pd.Series(index = securities_to_trade_list, data = alpha_list)

# Create a MaxGrossExposure exposure constraint of 2
leverage_2 = opt.MaxGrossExposure(2.0)
dollar_neutral = opt.DollarNeutral(context.dollar_n)

# Execute the order_optimal_portfolio method with above objective and constraint
order_optimal_portfolio(objective = alpha_objective,
constraints = [leverage_2, dollar_neutral])

def record_and_log(context, data):

# Log the leverage and long and short exposure
leverage = context.account.leverage
exposure = context.portfolio.positions_value / context.portfolio.portfolio_value
log_str = 'leverage = {} / long short exposure: {}'
log.info(log_str.format(leverage, exposure))

record(lev=leverage, exp=exposure)
record(dollar_n = context.dollar_n)
There was a runtime error.

Thanks guys. Curious which python function does QT use for the objective functions with constraints. I have tried scipy.optimize.minimize and CVXOPT.lp but the weights results do not match against QTs weights results.
Also for CVXOPT functions, how do you specify that you want to work with absolute weights since weights are not an input you provide in the constraints.