The portfolio optimizer brings together the power of the Axioma Portfolio Optimizer with Marquee's risk analytics infrastructure to make minimizing your portfolio's factor risk possible within the same ecosystem.
To use the optimizer, you must have a license to the Axioma Portfolio Optimizer. Please reach out to the [Marquee sales team](mailto:gs-marquee-sales@ny.email.gs.com?Subject=Portfolio Optimizer Trial Request) to learn more about how to get a license or how to bring an existing license to Marquee.
First you will import the necessary modules and add your client id and client secret.
import datetime as dt
import pandas as pd
from IPython.display import display
from gs_quant.markets.optimizer import OptimizerUniverse, FactorConstraint, MaxFactorProportionOfRiskConstraint, \
AssetConstraint, \
SectorConstraint, OptimizerSettings, OptimizerStrategy, OptimizerConstraints, OptimizerObjective, OptimizerType
from gs_quant.markets.position_set import Position, PositionSet
from gs_quant.markets.securities import Asset, AssetIdentifier
from gs_quant.models.risk_model import FactorRiskModel
from gs_quant.session import GsSession, Environment
from gs_quant.target.hedge import CorporateActionsTypes
# Enter client id and secret
GsSession.use(Environment.PROD, client_id=None, client_secret=None)
print('GS Session initialized.')
Use the PositionSet
class in GS Quant to define the initial holdings to optimize.
You can define your positions as a list of identifiers with quantities or, alternatively, as a
list of identifiers with weights, along with a reference notional value.
GS Quant will resolve all identifiers (Bloomberg IDs, SEDOLs, RICs, etc) historically as of the optimization date.
position_set = PositionSet(
date=dt.date(day=23, month=9, year=2022),
positions=[
Position(identifier='AAPL UW', quantity=26),
Position(identifier='GS UN', quantity=51)
]
)
position_set.resolve()
An optimizer universe corresponds to the assets that can be used when constructing an optimization, which can be created
using the OptimizerUniverse
class:
Parameter | Description | Type | Default Value |
---|---|---|---|
assets |
Assets to include in the universe. | List[Asset] |
N/A |
explode_composites |
Explode indices, ETFs, and baskets and include their underliers in the universe. | boolean |
True |
exclude_initial_position_set_assets |
Exclude assets in the initial holdings from the optimization. | boolean |
False |
exclude_corporate_actions_types |
Set of of corporate actions to be excluded in the universe. | List[CorporateActionsTypes] |
[] |
exclude_hard_to_borrow_assets |
Exclude hard to borrow assets from the universe. | boolean |
False |
exclude_restricted_assets |
Exclude assets on restricted trading lists from the universe. | float |
False |
min_market_cap |
Lowest market cap allowed for any universe constituent. | float |
None |
max_market_cap |
Highest market cap allowed for any universe constituent. | float |
None |
universe = OptimizerUniverse(
assets=[Asset.get('SPX', AssetIdentifier.BLOOMBERG_ID)],
explode_composites=True,
exclude_corporate_actions_types=[CorporateActionsTypes.Mergers]
)
You can run the optimizer using a factor risk model of your choice, so long as you have a license for it, by leveraging
the FactorRiskModel
class. For any factor in the risk model, you can set more granular constraints on the optimized
portfolio's exposure to the factor.
If you'd like to limit your optimized portfolio's factor proportion of risk,
you can do so with the MaxFactorProportionOfRiskConstraint
class. It is also possible to create a bespoke list of
factors and set their collective maximum proportion of risk using the MaxProportionOfRiskByGroupConstraint
class.
In this example, let's use the Axioma AXUS4S model and limit the final exposure to Volatility be $10,000 and the final exposure of Market Sensitivity to be 5,000. Let's also set a constraint so that the final positions don't have more than 30% of its total risk to be factor risk.
risk_model = FactorRiskModel.get('AXIOMA_AXUS4S')
factor_constraints = [
FactorConstraint(risk_model.get_factor('Volatility'), 10000),
FactorConstraint(risk_model.get_factor('Market Sensitivity'), 5000)
]
prop_of_risk_constraint = MaxFactorProportionOfRiskConstraint(30)
Outside factor-specific constraints, it's also possible to limit the holding value of individual assets, assets belonging to a particular GICS sector, and/or assets in a particular country of domicile in the optimization.
In this example, let's constrain the optimization to have 0-5% Microsoft and Twitter and limit the optimization's notional coming from Energy and Health Care assets to each be 0-30%.
asset_constraints = [
AssetConstraint(Asset.get('MSFT UW', AssetIdentifier.BLOOMBERG_ID), 0, 5),
AssetConstraint(Asset.get('TWTR UN', AssetIdentifier.BLOOMBERG_ID), 0, 5)
]
sector_constraints = [
SectorConstraint('Energy', 0, 30),
SectorConstraint('Health Care', 0, 30)
]
All other settings for the optimization can be set via the OptimizerSettings
class:
Parameter | Description | Type | Default Value |
---|---|---|---|
notional |
Max gross notional value of the optimization | float |
10000000 |
allow_long_short |
Allow a long/short optimization | boolean |
False |
min_names |
Minimum number of assets in the optimization | float |
0 |
max_names |
Maximum number of assets in the optimization | float |
100 |
min_weight_per_constituent |
Minimum weight of each constituent in the optimization | float |
None |
max_weight_per_constituent |
Maximum weight of each constituent in the optimization | float |
None |
max_adv |
Maximum average daily volume of each constituent in the optimization (in percent) | float |
15 |
constraint_priorities |
Priorities to relax some or all constraints in the strategy | ConstraintPriorities |
None |
settings = OptimizerSettings(allow_long_short=False)
It's finally time to take all these parameters and construct an optimizer strategy using the OptimizerStrategy
class:
constraints = OptimizerConstraints(
asset_constraints=asset_constraints,
sector_constraints=sector_constraints,
factor_constraints=factor_constraints,
max_factor_proportion_of_risk=prop_of_risk_constraint
)
strategy = OptimizerStrategy(
initial_position_set=position_set,
constraints=constraints,
settings=settings,
universe=universe,
risk_model=risk_model,
objective=OptimizerObjective.MINIMIZE_FACTOR_RISK
)
strategy.run(optimizer_type=OptimizerType.AXIOMA_PORTFOLIO_OPTIMIZER)
optimization = strategy.get_optimization() # Returns just the optimization results as a PositionSet object
optimized_position_set = strategy.get_optimized_position_set()
print('OPTIIMZATION RESULTS')
result = [{'Asset': p.identifier, 'Quantity': p.quantity} for p in optimization.positions]
display(pd.DataFrame(result))
Now that you have a position set for your optimization and your optimized position set, you can upload either to a basket or portfolio by following the following tutorials:
Other questions? Reach out to the Portfolio Analytics team!