Module boosted.api.api_type
Expand source code
# Copyright (C) 2020 Gradient Boosted Investments, Inc. - All Rights Reserved
import copy
import datetime as dt
import json
import logging
import re
import string
from collections import defaultdict
from dataclasses import dataclass
from enum import Enum
from typing import Dict, List, Literal, Optional, Union
import numpy as np
import pandas as pd
logger = logging.getLogger("boosted.api.api_type")
BoostedDate = Union[dt.date, str]
class Status(Enum):
UNKNOWN = 0
FAIL = 1
SUCCESS = 2
class DataAddType(Enum):
CREATION = 1
HISTORICAL = 2
LIVE = 3
def __str__(self):
if self.name == "CREATION":
return "CREATION"
elif self.name == "HISTORICAL":
return "HISTORICAL"
elif self.name == "LIVE":
return "LIVE"
class ChunkStatus(Enum):
PROCESSING = "PROCESSING"
ABORTED = "ABORTED"
SUCCESS = "SUCCESS"
WARNING = "WARNING"
ERROR = "ERROR"
class DataSetType(Enum):
UNKNOWN = 0
STOCK = 1
GLOBAL = 2
STRATEGY = 3
def __str__(self):
if self.name == "STOCK":
return "STOCK"
elif self.name == "GLOBAL":
return "GLOBAL"
elif self.name == "STRATEGY":
return "STRATEGY"
else:
return "UNKNOWN"
class DataSetSubType(Enum):
UNKNOWN = 0
DENSE = 1
SPARSE_HIST = 2
SPARSE_FWD = 3
def __str__(self):
if self.name == "DENSE":
return "DENSE"
elif self.name == "SPARSE_HIST":
return "SPARSE_HIST"
elif self.name == "SPARSE_FWD":
return "SPARSE_FWD"
else:
return "UNKNOWN"
class DataSetFrequency(Enum):
UNKNOWN = 0
DAILY = 1
WEEKLY = 2
MONTHLY = 3
QUARTERLY = 4
SEMIANNUAL = 5
ANNUAL = 6
def __str__(self):
if self.name == "DAILY":
return "DAILY"
elif self.name == "WEEKLY":
return "WEEKLY"
elif self.name == "MONTHLY":
return "MONTHLY"
elif self.name == "QUARTERLY":
return "QUARTERLY"
elif self.name == "SEMIANNUAL":
return "SEMIANNUAL"
elif self.name == "ANNUAL":
return "ANNUAL"
else:
return "UNKNOWN"
class ColumnRole(Enum):
UNKNOWN = 0
VARIABLE = 1
GOAL = 2
METRIC = 3
IDENTIFIER = 4
def __str__(self):
if self.name == "VARIABLE":
return "VARIABLE"
elif self.name == "GOAL":
return "GOAL"
elif self.name == "METRIC":
return "METRIC"
elif self.name == "IDENTIFIER":
return "IDENTIFIER"
else:
return "UNKNOWN"
class ColumnSubRole(Enum):
UNKNOWN = 0
GBI_ID = 1
ISIN = 2
GVKEY = 3
IID = 4
SYMBOL = 5
COUNTRY = 6
CURRENCY = 7
DATE = 8
COMPANY_NAME = 9
REPORT_DATE = 10
REPORT_PERIOD = 11
def __str__(self):
if self.name == "GBI_ID":
return "GBI_ID"
elif self.name == "ISIN":
return "ISIN"
elif self.name == "GVKEY":
return "GVKEY"
elif self.name == "IID":
return "IID"
elif self.name == "SYMBOL":
return "SYMBOL"
elif self.name == "COUNTRY":
return "COUNTRY"
elif self.name == "CURRENCY":
return "CURRENCY"
elif self.name == "DATE":
return "DATE"
elif self.name == "COMPANY_NAME":
return "COMPANY_NAME"
elif self.name == "REPORT_DATE":
return "REPORT_DATE"
elif self.name == "REPORT_PERIOD":
return "REPORT_PERIOD"
else:
return "UNKNOWN"
def get_match(column_name):
def clean_str(name):
translation = str.maketrans("", "", string.punctuation)
clean_name = name.strip().translate(translation).lower()
return re.sub(r"\s+", "", clean_name)
identifiers = [
ColumnSubRole.GBI_ID,
ColumnSubRole.ISIN,
ColumnSubRole.GVKEY,
ColumnSubRole.IID,
ColumnSubRole.SYMBOL,
ColumnSubRole.COUNTRY,
ColumnSubRole.CURRENCY,
ColumnSubRole.COMPANY_NAME,
ColumnSubRole.REPORT_DATE,
ColumnSubRole.REPORT_PERIOD,
]
for identifier in identifiers:
if clean_str(str(identifier)) == clean_str(column_name):
return identifier
return None
class ColumnValueType(Enum):
UNKNOWN = 0
NUMBER = 1
STRING = 2
def __str__(self):
if self.name == "UNKNOWN":
return "UNKNOWN"
elif self.name == "NUMBER":
return "NUMBER"
elif self.name == "STRING":
return "STRING"
else:
return "UNKNOWN"
class ColumnConfig:
def __init__(
self,
name=None,
role=None,
sub_role=None,
value_type=ColumnValueType.NUMBER,
description="",
investment_horizon=None,
):
self.name = name
self.role = role
self.sub_role = sub_role
self.value_type = value_type
# More value_types will be supported in the future.
self.description = description
self.investment_horizon = investment_horizon
def __str__(self):
return 'Name: "{0}", Role: {1}, SubRole: {2}, Value type: {3}, Desc: {4} IH: {5}.'.format(
self.name,
self.role,
self.sub_role,
self.value_type,
self.description,
self.investment_horizon,
)
def __repr__(self):
return self.__str__()
class StrategyConfig:
def __init__(self, name=None, source_name=None):
self.name = name
self.source_name = source_name
def __str__(self):
return 'Name: "{0}", Source Name: {1}.'.format(self.name, self.source_name)
def __repr__(self):
return self.__str__()
class BoostedDataSetSchemaException(Exception):
def __init__(self, value, data=None):
self.value = value
self.data = data
def __str__(self):
return repr(self.value)
class DataSetConfig:
def __init__(
self,
name,
datasetType=DataSetType.STOCK,
datasetSubType=DataSetSubType.DENSE,
datasetFrequency=DataSetFrequency.DAILY,
):
self.name = name
self.type = datasetType
self.subtype = datasetSubType
self.frequency = datasetFrequency
self.columns = []
self.strategies = []
def addColumn(self, columnConfig):
self.columns.append(columnConfig)
def addStrategy(self, strategyConfig):
self.strategies.append(strategyConfig)
def __getColumnByName(self, col_name):
for column in self.columns:
if col_name == column.name:
return column
raise BoostedDataSetSchemaException(f"Unable to find column {col_name}")
def updateColumnToGoal(self, col_name, investment_horizon):
column = self.__getColumnByName(col_name)
column.role = ColumnRole.GOAL
column.investment_horizon = int(investment_horizon)
def updateColumnToMetric(self, col_name):
column = self.__getColumnByName(col_name)
column.role = ColumnRole.METRIC
def updateColumnToVariable(self, col_name):
column = self.__getColumnByName(col_name)
column.role = ColumnRole.VARIABLE
def __validateSchema(self):
num_goals = 0
num_metrics = 0
dt = self.type
dst = self.subtype
dsf = self.frequency
if len(self.columns) == 0:
msg = "No feature columns exist."
raise BoostedDataSetSchemaException(msg)
if dst in [DataSetSubType.SPARSE_HIST, DataSetSubType.SPARSE_FWD]:
if dsf not in [DataSetFrequency.QUARTERLY]:
msg = f"{dsf} frequency is not supported for {dst} sub data"
raise BoostedDataSetSchemaException(msg)
if dt not in [DataSetType.STOCK]:
msg = f"{dst} subtype is not supported for {dt} data"
raise BoostedDataSetSchemaException(msg)
for column in self.columns:
if column.role == ColumnRole.GOAL:
ih = column.investment_horizon
gn = column.name
if dt == DataSetType.GLOBAL:
msg = f"{dt} data can not have {ColumnRole.GOAL} type"
raise BoostedDataSetSchemaException(msg)
if not isinstance(ih, int):
msg = f"Investment horizon for {gn} must be an integer"
raise BoostedDataSetSchemaException(msg)
if ih < 1 or ih > 252:
msg = f"Investment horizon must be between 1 and 252 for {gn}"
raise BoostedDataSetSchemaException(msg)
num_goals += 1
elif column.role == ColumnRole.METRIC:
if dt in [DataSetType.GLOBAL, DataSetType.STOCK]:
msg = f"{dt} data can not have {ColumnRole.METRIC} type"
raise BoostedDataSetSchemaException(msg)
num_metrics += 1
if dt == DataSetType.STRATEGY:
if num_goals == 0:
msg = "Independent data requires at least one goal."
raise BoostedDataSetSchemaException(msg)
if num_metrics == 0:
msg = "Independent data requires at least one metric."
raise BoostedDataSetSchemaException(msg)
if len(self.strategies) <= 1:
msg = "Independent data requires more than 1 strategy"
raise BoostedDataSetSchemaException(msg)
def toDict(self):
self.__validateSchema()
config = {}
config["name"] = self.name
config["type"] = str(self.type)
config["subType"] = str(self.subtype)
config["frequency"] = str(self.frequency)
featureList = []
for i, f in enumerate(self.columns):
fm = {}
fm["name"] = f.name
fm["description"] = f.description
fm["type"] = str(f.role)
fm["role"] = str(f.role)
fm["subRole"] = str(f.sub_role) if f.sub_role is not None else None
fm["valuesType"] = str(f.value_type)
fm["columnName"] = f.name
fm["investmentHorizon"] = f.investment_horizon
featureList.append(fm)
config["features"] = featureList
strategyList = []
for i, s in enumerate(self.strategies):
sm = {}
sm["name"] = s.name
sm["sourceName"] = s.source_name
strategyList.append(sm)
config["strategies"] = strategyList
return config
def __str__(self):
a = ""
a += "Name: {0}\n".format(self.name)
a += "Columns: \n"
for c in self.columns:
a += " {0}\n".format(c)
return a
def __repr__(self):
return self.__str__()
def fromDict(schema):
subtype = DataSetSubType[schema.get("subtype", str(DataSetSubType.DENSE))]
frequency = DataSetFrequency[schema.get("frequency", str(DataSetFrequency.DAILY))]
config = DataSetConfig(
schema["name"],
datasetType=DataSetType[schema["type"]],
datasetSubType=subtype,
datasetFrequency=frequency,
)
# two things left to fill in - strategies and columns
for strategy_data in schema["strategies"]:
config.addStrategy(
StrategyConfig(name=strategy_data["name"], source_name=strategy_data["sourceName"])
)
for feature_data in schema["features"]:
config.addColumn(
ColumnConfig(
name=feature_data["columnName"],
description=feature_data["description"] or "",
investment_horizon=feature_data["investmentHorizon"],
value_type=ColumnValueType[feature_data["valuesType"]]
if feature_data["valuesType"] is not None
else ColumnValueType.NUMBER,
role=ColumnRole[feature_data["role"]]
if feature_data["role"] is not None
else None,
sub_role=ColumnSubRole[feature_data["subRole"]]
if feature_data["subRole"] is not None
else None,
)
)
config.__validateSchema()
return config
class GbiIdSecurityException(Exception):
def __init__(self, value, data=None):
self.value = value
self.data = data
def __str__(self):
return repr(self.value)
class GbiIdSecurity:
def __init__(self, gbi_id, isin_country_currency_date, ticker, company_name):
self.gbi_id = gbi_id
self.isin_info = isin_country_currency_date
self.ticker = ticker
self.company_name = company_name
def __str__(self):
return '\n(gbi_id: "{0}", isin_info: {1}, ticker: {2}, company_name: {3})'.format(
self.gbi_id, self.isin_info.__str__(), self.ticker, self.company_name
)
def __repr__(self):
return self.__str__()
def __eq__(self, __o: object) -> bool:
return self.gbi_id == __o.gbi_id
def __hash__(self):
return hash(self.gbi_id)
class DateIdentCountryCurrencyException(Exception):
def __init__(self, value, data=None):
self.value = value
self.data = data
def __str__(self):
return repr(self.value)
# for backward compatibility
class DateIsinCountryCurrencyException(DateIdentCountryCurrencyException):
pass
class DateIdentCountryCurrency:
def __init__(
self,
date: str,
identifier: str,
country: Optional[str] = None,
currency: Optional[str] = None,
id_type: ColumnSubRole = ColumnSubRole.ISIN,
):
self.date = date
self.identifier = identifier
self.id_type = id_type
self.country = country
self.currency = currency
self.__validateInput()
def __str__(self):
return '(date: "{0}", identifier: "{1}", country: {2}, currency: {3})'.format(
self.date, self.identifier, self.country, self.currency
)
def __repr__(self):
return self.__str__()
def __validateInput(self):
# simply ensure that at least isin and date are filled out.
# country/currency defaults to ANY if not filled out, but
# still is not recommended.
if self.identifier is None or self.date is None:
raise DateIdentCountryCurrencyException(
"ISIN or Date not provided while constructing DateIsinCountryCurrency"
)
if self.id_type not in (ColumnSubRole.ISIN, ColumnSubRole.SYMBOL):
raise DateIdentCountryCurrencyException(f"Invalid ID Type: {self.id_type}")
def check_is_str(value, fieldname):
if value is not None and not isinstance(value, str):
raise DateIdentCountryCurrencyException(
f"Field {fieldname} in DateIsinCountryCurrency was not a string."
)
check_is_str(self.date, "date")
check_is_str(self.identifier, "identifier")
check_is_str(self.country, "country")
check_is_str(self.currency, "currency")
# for backwards compatibility
class DateIsinCountryCurrency(DateIdentCountryCurrency):
def __init__(self, date, isin, country, currency):
super().__init__(date, isin, country, currency, ColumnSubRole.ISIN)
self.isin = isin
def __str__(self):
return '(date: "{0}", isin: "{1}", country: {2}, currency: {3})'.format(
self.date, self.isin, self.country, self.currency
)
class IdentifierToColumnMapping:
"""
IdentifierToColumnMapping is a mapping from string to boosted.api.api_type.ColumnSubRole.
The key of the mapping corresponds to a CSV column name, and the value corresponds
to the type of identifier that it maps to. Only columns specifying an identifying factor
for the stock needs to be specified (e.g. ISIN, Date, Symbol, Currency)
"""
def __init__(self, mapping):
self.mapping = mapping
self.__validateInput()
def __str__(self):
return repr(self.value)
def __validateInput(self):
def validate_key_val(key, val):
if not isinstance(key, str):
raise Exception(f"key in IdentifierToColumnMapping {key} was not a string!")
if not isinstance(val, ColumnSubRole):
raise Exception(f"val in IdentifierToColumnMapping {val} was not a ColumnSubRole!")
for key, val in self.mapping:
validate_key_val(key, val)
class PortfolioSettings:
"""
This config is a documentation wrapper around a dict representing the portfolio settings json.
Keys missing in the dict provided will fallback to the listed default when updating the
the portfolio settings on the server.
Parameters
----------
activeRisk: bool
- **Active Risk**:
Set the portfolio optimization parameters to be relative to the score from the stock
universe. i.e. if you set Momentum to 0.5 to 1.0 and the stock universe has a market cap
weighed score of 0.2 then the optimizer will solve for 0.7 to 1.2.
- Constraints:
Boolean value
- Default Value: false
allowCompanyCollisions: bool
- **Allow Multiple Securities Per Company**:
Allows the machine to trade multiple securities (at the same time) for the same company.
If it is ON and a company has listed PREF shares and COMMON shares, both will show up as
tradeable options for that company. If it is OFF then system will try to determine the
"correct" security to trade based on volume, liquidity, and share type.
- Constraints:
Boolean value
- Default Value: true
allowDR: bool
- **Allow Depositary Receipts**:
Companies with tickers ending in .Y and .F will be disallowed from trading within the
model with this setting off. Turn it on to allow trading in these securities.
- Constraints:
Boolean value
- Default Value: true
benchmark: array
- **Benchmark**:
Set one or more benchmarks and the weighting for each.
- Constraints:
Required.
List of dicts with keys:
gbi_id: integer representing the GBI ID of the benchmark index.
weight: floating point value between 0 and 1.
All gbi_id's in the dictionary must be unique, and the benchmark weights must sum to a
number between 0 and 1.
- Default Value: []
benchmarkAllocation: float
- **Benchmark Allocation**:
None
- Constraints:
Floating point value representing a percentage between -100 and 100
- Default Value: 0
beta_neutral: bool
- **Beta Neutral**:
Adjust the weights to get the ex-ante Beta to be the net exposure of the portfolio. i.e.
70% Long / 30% Short = 0.4 Beta"
- Constraints:
Boolean value
- Default Value: false
bounds_method: str | null
- **Optimizer Bounds**:
Tight: The optimizer will only allow the weightings of securities in your portfolio to
stay tight (close) to their initial weighting.
Loose: The optimizer will allow the weightings of securities in your portfolio to move a
medium amount more from their initial weighting.
Wide: The optimizer will allow almost any weight between the minimum and maximum weight
per long or short position.
- Constraints:
String enum of {loose, tight, wide}, or null
- Default Value: null
compounding: bool
- **Compound Returns**:
When toggled on, this will allow the portfolio to compound returns.
- Constraints:
Boolean value
- Default Value: true
currency: str
- **Currency**:
The currency you would like your portfolio to be calculated in. Note that if the
currency differs from its exchange (i.e. GBP on the S&P), the prior close foreign
exchange rate will be used for calculations.
- Constraints:
String representing a 3 digit ISO currency code.
- Default Value: "USD"
equalWeightBenchmark: bool
- **Equal Weight Benchmark**:
Instead of using the defined benchmarks, set the benchmark to be an equal weighted (per
rebalance period) version of the stock universe.
- Constraints:
Boolean value
- Default Value: false
executionDelay: int
- **Execution Delay**:
This option adds the number of days selected to the trade execution. If you select T+1
and your rebalance period is set to Weekly (Monday), it will trade on Tuesday, and so
on.
- Constraints:
Integer value between 0 and 100, inclusive.
- Default Value: 0
factorNeutralizeSignal: bool
- **Signal Optimizer**:
Turn on optimization at the signal level that will adjust signals and rankings according
to the specifications you set. This is done prior to portfolio construction and can help
reduce bias in the model.
- Constraints:
Boolean value
- Default Value: false
investmentHorizon: int
- **Investment Horizon**:
The investment horizon for the portfolio.
- Constraints:
An integer representing a number of days
- Default Value: 21
lower_turnover: bool
- **Lower Turnover**:
If toggled on the optimizer will try to solve the secondary goal of optimizing the
primary goals while limiting turnover.
- Constraints:
Boolean value
- Default Value: false
marketCapBounds: float
- **Maximum Market Cap Variation**:
How far from the target market cap weighted index each stock can deviate (positive or
negative).
- Constraints:
Floating point value representing a percentage between 0 and 10
- Default Value: 1
marketCapConstraint: bool
- **Constrain Market Cap**:
Force weighting to be near the target weights for a market cap weighted index of your
entire stock universe.
- Constraints:
Boolean value
- Default Value: false
marketCapFlex: float
- **Market Cap Flex**:
How far from the target market cap weighted the stock can deviate relative to the
existing weight (i.e. at 75% a 1% position cannot exceed 1.75%).
- Constraints:
Floating point value representing a percentage between 10 and 200
- Default Value: 100
maxLongPosition: float
- **Max Long Position per Stock**:
The maximum weighting of a long position within the portfolio.
- Constraints:
Required. Floating point value representing a percentage between 0.0 and 300.0
- Default Value: 10.0
maxNumStockLong: int
- **Maximum Number of Longs**:
The maximum number of stocks to buy. This is on a per “portfolio” basis, so if you have
sector neutral turned on it will be maximum per sector.
- Constraints:
Integer value between 0 and 1000, inclusive. Must be >= minNumStockLong
- Default Value: 50
maxNumStockShort: int
- **Maximum Number of Shorts**:
The maximum number of stocks to short. This is on a per “portfolio” basis, so if you
have sector neutral turned on it will be maximum per sector.
- Constraints:
Integer value between 0 and 1000, inclusive. Must be >= minNumStockShort
- Default Value: 50
maxOwnership: float
- **Maximum Ownership**:
The maximum percentage of a company the portfolio is allowed to own. For example, if a
company had a market cap of $100MM and this was 5% the portfolio could not own more than
$5MM of that company.
- Constraints:
Floating point value representing a percentage between 0 and 100
- Default Value: 5
maxShortPosition: float
- **Max Short Position per Stock**:
The maximum weighting of a short position within the portfolio.
- Constraints:
Required. Floating point value representing a percentage between 0.0 and 300.0
- Default Value: 5.0
minimumSharePrice: float
- **Minimum Share Price**:
The minimum share price you want the portfolio to allow.
- Constraints:
Floating point value between 0 and 100000
- Default Value: 2
minLongPosition: float
- **Min Long Position per Stock**:
The minimum weighting of a long position within the portfolio.
- Constraints:
Floating point value representing a percentage between 0.0 and 300.0
- Default Value: 0
minNumStockLong: int
- **Minimum Number of Longs**:
The minimum number of stocks to buy. This is on a per “portfolio” basis, so if you have
sector neutral turned on it will be minimum per sector.
- Constraints:
Integer value between 0 and 1000, inclusive. Must be <= maxNumStockLong
- Default Value: 5
minNumStockShort: int
- **Minimum Number of Shorts**:
The minimum number of stocks to short. This is on a per “portfolio” basis, so if you
have sector neutral turned on it will be minimum per sector.
- Constraints:
Integer value between 0 and 1000, inclusive. Must be <= maxNumStockShort
- Default Value: 5
minShortPosition: float
- **Min Short Position per Stock**:
The minimum weighting of a short position within the portfolio.
- Constraints:
Floating point value representing a percentage between 0.0 and 100.0
- Default Value: 0
missingMarketCap: float
- **Missing Market Cap**:
The amount in millions (MM) to replace any missing market capitalization information
with. This will impact the maximum ownership setting.
- Constraints:
Floating point value between 0 and 100
- Default Value: 10
nameBasedSectorWeighting: bool
- **Name Based Sector Weighting**:
Base sector weightings on number of names. For example, if there are 200 names in the
index and financials has 20 companies, the weight of financials should be 10% (20/200).
- Constraints:
Boolean value
- Default Value: false
netBenchmark: bool
- **Adjust Benchmark for Net Exposure**:
If your portfolio has a net exposure greater or less than 100%, this will adjust the
benchmark returns to match that net exposure.
- Constraints:
Boolean value
- Default Value: false
optimizer: str
- **Optimizer Type**:
No Optimization: None
Reduce Risk: Optimizer designed to reduce portfolio volatility. This tends to result in
the best overall performance.
Maximize Sharpe: Optimizer designed to maximize Sharpe using out of sample estimates for
expected return. Estimates for expected return have an outsized impact on the
performance of this optimizer.
Maximize Alpha: Optimizer designed to maximize expected return based on out-of-sample
estimates for expected return. Estimates for expected return have an outsized impact on
the performance of this optimizer.
Min VaR: Minimize Value at Risk by looking back at the proposed portfolio over the last
year and trying to minimize drawdowns.
Max VaR Sharpe: Maximize Value at Risk Sharpe by looking back at the proposed portfolio
over the last year and trying to maximize Sharpe by observed return for the portfolio vs
observed volatility.
Minimize Skew: Minimize the skew of the portfolio by trying to find a set of portfolio
weightings that results in returns that are closer to the mean.
- Constraints:
Required. String enum: one of
default: No Optimization
min_vol: Reduce Risk
max_sharpe: Maximize Sharpe
max_alpha: Maximize Alpha
min_var: Min VaR
max_var_sharpe: Max VaR Sharpe
min_skew: Minimize Skew
- Default Value: "default"
optimizeTurnover: bool
- **Turnover Optimization**:
Turnover optimization will reduce the turnover at the signal level, making that ranked
list more stable.
- Constraints:
Boolean value
- Default Value: false
pairsTrading: bool
- **Pairs Trading**:
Pairs Trading forces the portfolio to be created by going long / short an equal number
of stocks. The portfolio will try to find an offsetting position for every long by
finding securities that are both highly correlated and have a significant difference in
star rating.
- Constraints:
Boolean value
- Default Value: false
percentPortfolioLong: float
- **Percent of Portfolio Long**:
The exact sum of all long position weightings in the portfolio.
- Constraints:
Required. Floating point value representing a percentage between 0.0 and 300.0
- Default Value: 100.0
percentPortfolioShort: float
- **Percent of Portfolio Short**:
The exact sum of all short position weightings in the portfolio.
- Constraints:
Required. Floating point value representing a percentage between 0.0 and 300.0
- Default Value: 0.0
percentToLong: float
- **Percent to Long**:
The machine will attempt to buy the top X% of stocks, subject to the minimum and maximum
specified. This is on a per “portfolio” basis, so if you have sector neutral turned on
it will be top X% per sector. This will be subject to the maximum and minimum number of
securities you specify below.
- Constraints:
Floating point value representing a percentage between 0 and 100
- Default Value: 20.0
percentToShort: float
- **Percent to Short**:
The machine will attempt to short the bottom X% of stocks, subject to the minimum and
maximum specified. This is on a per “portfolio” basis, so if you have sector neutral
turned on it will be bottom X% per sector. This will be subject to the maximum and
minimum number of securities you specify below.
- Constraints:
Floating point value representing a percentage between 0 and 100
- Default Value: 20.0
portfolioStartingValue: float
- **Portfolio Starting Value**:
The value of the portfolio in MM when it begins trading. If its backtest spans from 2005
- 2020, the starting value in 2005 will be whatever you set here.
- Constraints:
Floating point value between 0 and 100000.
- Default Value: 100.0
priceCleaning: bool
- **Price Cleaning**:
Price Cleaning removes any very large price movements from the securities, specifically
with a daily change greater than +500% or -83.33%. There are some legitimate securities
with moves this large that will be impacted by turning this on, but the advantage is it
prevents these securities from overwhelming the backtest.
- Constraints:
Boolean value
- Default Value: true
priceType: str
- **Price Type**:
None
- Constraints:
Required. String enum of {CLOSE, OPEN, VWAP}
- Default Value: "VWAP"
rebalancePeriod: str
- **Rebalance Period**:
When the machine rebalances the portfolio. You can choose a specific day if you prefer
to execute trades on a specific day.
- Constraints:
Required.
An enum value in the following list:
"daily"
"weekly": Rebalances on every Monday.
Chooses the next available trade date to rebalance when Monday is a holiday
"weekly_wednesday": Rebalances on every Wednesday.
Chooses the next available trade date to rebalance when Wednesday is a holiday
"weekly_friday": Rebalances on every Friday.
Chooses the previous available trade date to rebalance when Friday is a holiday
"monthly"
"quarterly"
OR a string in the following format: "custom_x" where x is an integer between 1 and 252
(days).
- Default Value: "weekly"
sectorNeutral: bool
- **Sector Neutral**:
Will be constructed as a series of portfolios - each matching the market cap weighting
of each of the stock universe sectors. This means that min. and max. number of stocks
"per portfolio" becomes "per sector".
- Constraints:
Boolean value
- Default Value: false
sectorNeutralEqualWeightBenchmark: bool
- **Sector Neutral Equal Weight Benchmark**:
Instead of using the defined benchmarks, set the benchmark to be a "sector neutral"
equal weighted (per rebalance period) version of the stock universe. This takes the
sector weighting and divides by the number of stocks in that sector, so each sector will
have a different individual security weighting.
- Constraints:
Boolean value
- Default Value: false
sectorNeutralSpread: float
- **Sector Neutral Spread**:
The sector neutral spread is how far away from the benchmark sector allocation the
machine can deviate. This is adjusted for net exposure, i.e. if your net exposure is 0%
the target sector allocations will be 0%.
- Constraints:
Floating point value representing a percentage between 0 and 100
- Default Value: 0
signalIndustryNeutral: bool
- **Industry Neutral**:
Make the signal industry neutral, plus or minus the signal sector neutral spread.
- Constraints:
Boolean value
- Default Value: false
signalSectorNeutral: bool
- **Sector Neutral**:
Make the signal sector neutral, plus or minus the signal sector neutral spread.
- Constraints:
Boolean value
- Default Value: false
signalSectorNeutralSpread: float
- **Sector Neutral Spread**:
Make the signal sector neutral, plus or minus the signal sector neutral spread.
- Constraints:
Floating point value representing a percentage between 0 and 100
- Default Value: 1.0
smoothPeriods: int
- **Smooth Periods**:
The number of periods over which to smooth the signals
- Constraints:
Integer value between 1 and 100.
- Default Value: 1
smoothWeighting: str
- **Smooth Weighting Type**:
Alpha Weight: The strength of the signal matters in the smoothing. For example, if a
stock had a score of 5 stars in period A, 2 stars in period B, your 2 period smoothing
would be 7 / 2 = 3.5.
Equal Weight: Only the direction of the signal matters here. For example, if you were 5
stars in period A and 2 stars in period B, your 2 period smoothing would be 0 / 2 = 0
(+1 in period 1 and -1 in period 2)
- Constraints:
One of {alpha_weight, equal_weight}
- Default Value: "alpha_weight"
stopGain: bool
- **Enable Stop Gain**:
Turn on stop gains for the portfolio. This forces sales of securities that have exceeded
the stop gain level. All trades will occur at the next trading time (i.e. next day OPEN
for OPEN price models).
- Constraints:
Boolean value
- Default Value: false
stopGainAmount: float
- **Stop Gain Percentage to Sell**:
The percentage of the position the machine will sell if the stop gain is triggered.
- Constraints:
Floating point value representing a percentage between 1 and 100.
- Default Value: 50.0
stopGainLevel: float
- **Stop Gain Threshold**:
Will sell positions after they hit a threshold. For example, 10% / stop gain, when a
position gains 10% the machine will sell a portion of the position (defined by stop gain
percentage).
- Constraints:
Floating point value representing a percentage between 1 and 100.
- Default Value: 10.0
stopLoss: bool
- **Enable Stop Loss**:
Turn on stop losses for the portfolio. This forces sales of securities that have
exceeded the stop loss level. All trades will occur at the next trading time (i.e. next
day OPEN for OPEN price models).
- Constraints:
Boolean value
- Default Value: false
stopLossAmount: float
- **Stop Loss Percentage to Sell**:
The percentage of the position the machine will sell if the stop loss is triggered.
- Constraints:
Floating point value representing a percentage between 1 and 100.
- Default Value: 50.0
stopLossLevel: float
- **Stop Loss Threshold**:
Will sell positions after they hit a threshold. For example, 10% / stop loss, when a
position loses 10% the machine will sell a portion of the position (defined by stop loss
percentage).
- Constraints:
Floating point value representing a percentage between 1 and 100.
- Default Value: 10.0
stopLossReset: bool
- **Allow multiple stop loss/gain between rebalance days**:
Allows the machine to repeatedly trigger stop losses within a rebalance period. For
example, if you trade Mondays and the stock drops 15% per day during the week and the
stop loss trigger is 10%, then every day will trigger the stop loss. If toggled off the
stop loss can only be triggered once per stock per rebalance period.
- Constraints:
Boolean value
- Default Value: false
trackingConstraint: float
- **Maximum Tracking Error (vs Benchmark)**:
Amount of ex-ante tracking error to constrain the optimizer by.
- Constraints:
Floating point value representing a percentage between 0 and 30
- Default Value: 10
trackingError: bool
- **Allocate Weight to Benchmark**:
Allocate a portion (%) of portfolio to the benchmark
- Constraints:
Boolean value
- Default Value: false
trackingErrorOptimizer: bool
- **Tracking Error**:
If toggled on the optimizer will try to solve for an expected tracking error less than
the amount specified. This is done out-of-sample and the ex post tracking error will
likely be higher.
- Constraints:
Boolean value
- Default Value: false
tradingCost: float
- **Trading Cost**:
A cost that will be applied to every trade.
- Constraints:
Required. Floating point value representing a percentage between 0.0 and 5.0
- Default Value: 0.01
turnoverImportance: float
- **Turnover Importance**:
High importance means the algorithm will aim to keep turnover low, whereas low
importance of turnover will let it vary more.
- Constraints:
Floating point value between 0.2 and 5 nonlinearly mapped onto the 1-9 interval.
- Default Value: 1
useHoldingPeriod: bool
- **Use Holding Period**:
Set any covariance or other calculations within the optimizer to use a holding period
return instead of daily return series
- Constraints:
Boolean value
- Default Value: false
weightingType: str
- **Initial Weighting**:
Alpha Weight: The initial weighting will be done such that higher ranked stocks have a
higher weight.
Equal Weight: The initial weighting will be done such that all stocks selected to be in
the portfolio will have an equal weight.
Market Cap Weight: The initial weighting will be done such that all stocks are weighted
according to their previous day’s market capitalization.
- Constraints:
One of {alpha_weight, equal_weight, market_cap_weight}
- Default Value: "alpha_weight"
"""
# note, this is just the collection of portfolio settings
# that is exposed to the API:
# SELECT key, default_value
# FROM portfolio_settings_validation
# WHERE exposed_to_client_api;
# TODO: expand to entire settings? why this way initially?
__default_settings = {
"maxOwnership": "5",
"pairsTrading": "false",
"minNumStockLong": "5",
"stopLossAmount": "50.0",
"stopGainLevel": "10.0",
"smoothWeighting": '"alpha_weight"',
"minimumSharePrice": "2",
"maxShortPosition": "5.0",
"tradingCost": "0.01",
"stopGainAmount": "50.0",
"smoothPeriods": "1",
"executionDelay": "0",
"benchmarkAllocation": "0",
"sectorNeutralSpread": "0",
"percentToLong": "20.0",
"maxNumStockLong": "50",
"trackingConstraint": "10",
"compounding": "true",
"priceCleaning": "true",
"lower_turnover": "false",
"missingMarketCap": "10",
"allowCompanyCollisions": "true",
"allowDR": "true",
"sectorNeutral": "false",
"marketCapFlex": "100",
"marketCapBounds": "1",
"turnoverImportance": "1",
"minNumStockShort": "5",
"maxNumStockShort": "50",
"beta_neutral": "false",
"nameBasedSectorWeighting": "false",
"trackingError": "false",
"stopLoss": "false",
"stopGain": "false",
"stopLossReset": "false",
"netBenchmark": "false",
"equalWeightBenchmark": "false",
"marketCapConstraint": "false",
"signalIndustryNeutral": "false",
"signalSectorNeutral": "false",
"sectorNeutralEqualWeightBenchmark": "false",
"percentToShort": "20.0",
"trackingErrorOptimizer": "false",
"currency": '"USD"',
"rebalancePeriod": '"weekly"',
"benchmark": "[]",
"signalSectorNeutralSpread": "1.0",
"optimizeTurnover": "false",
"minShortPosition": "0",
"minLongPosition": "0",
"percentPortfolioLong": "100.0",
"portfolioStartingValue": "100.0",
"stopLossLevel": "10.0",
"maxLongPosition": "10.0",
"percentPortfolioShort": "0.0",
"bounds_method": None,
"optimizer": '"default"',
"activeRisk": "false",
"useHoldingPeriod": "false",
"weightingType": '"alpha_weight"',
"priceType": '"VWAP"',
"factorNeutralizeSignal": "false",
"investmentHorizon": "21",
}
def __init__(self, settings: Optional[Dict] = None):
# TODO: i'd rather have kwargs for the different keys and then allow
# for per-key overrides, but we start with this more conservative approach
if settings is None:
self.settings = copy.deepcopy(PortfolioSettings.__default_settings)
else:
self.settings = settings
def toDict(self):
return self.settings
def __str__(self):
return json.dumps(self.settings)
def __repr__(self):
return self.__str__()
def fromDict(settings):
return PortfolioSettings(settings)
hedge_experiment_type = Literal["HEDGE", "MIMIC"]
@dataclass(frozen=True) # from _python client's_ perspective, instances will never change!
class HedgeExperiment:
id: str # really a uuid, which we represent as a string
name: str
user_id: str # see id comment
description: str
experiment_type: hedge_experiment_type # TODO: enum worth it?
last_calculated: dt.datetime
last_modified: dt.datetime
status: str # TODO: enum worth it?
portfolio_calculation_status: str # TODO: enum worth it?
selected_models: List[str] # see id comment
target_securities: Dict[GbiIdSecurity, float] # Security is a hashable type because frozen=True
target_portfolios: List[str]
selected_stock_universe_ids: List[str] # see id comment
baseline_model: Optional[str] = None
baseline_stock_universe_id: Optional[str] = None
baseline_scenario: Optional["HedgeExperimentScenario"] = None
@property
def config(self) -> Dict:
"""
Returns a hedge experiment configuration dictionary, ready for JSON-serialization and
submission.
"""
weights_by_gbiid = [
{"gbiId": sec.id, "weight": weight} for sec, weight in self.target_securities.items()
]
return {
"experimentType": self.experiment_type,
"selectedModels": self.selected_models,
"targetSecurities": weights_by_gbiid,
"selectedStockUniverseIds": self._selected_stock_universe_ids,
}
@classmethod
def from_json_dict(cls, d: Dict) -> "HedgeExperiment":
# drafts arent fully populated
if d["status"] == "DRAFT":
weights_by_security = {}
selected_models = []
selected_stock_universe_ids = []
else:
weights_by_security = {
GbiIdSecurity(
sec_dict["gbiId"],
None,
sec_dict["security"]["symbol"],
sec_dict["security"]["name"],
): sec_dict["weight"]
for sec_dict in d["targetSecurities"]
}
experiment_config = json.loads(d["config"])
selected_models = experiment_config["selectedModels"]
selected_stock_universe_ids = experiment_config["selectedStockUniverseIds"]
baseline_scenario = None
if d["baselineScenario"] is not None:
baseline_scenario = HedgeExperimentScenario.from_json_dict(d["baselineScenario"])
return cls(
d["hedgeExperimentId"],
d["experimentName"],
d["userId"],
d["description"],
d["experimentType"],
d["lastCalculated"],
d["lastModified"],
d["status"],
d["portfolioCalcStatus"],
selected_models,
weights_by_security,
d.get("targetPortfolios"),
selected_stock_universe_ids or [],
d.get("baselineModel"),
d.get("baselineStockUniverseId"),
baseline_scenario,
)
@dataclass(frozen=True)
class HedgeExperimentScenario:
id: str # really a uuid, which we represent as a string;
name: str
description: str
status: str # TODO: enum worth it?
settings: PortfolioSettings
summary: pd.DataFrame
# we currently a portfolios field. they get wrapped up into the summary table
@classmethod
def from_json_dict(cls, d: Dict) -> "HedgeExperimentScenario":
return cls(
d["hedgeExperimentScenarioId"],
d["scenarioName"],
d["description"],
d.get("status"),
PortfolioSettings(json.loads(d["portfolioSettingsJson"])),
HedgeExperimentScenario._build_portfolio_summary(d.get("hedgeExperimentPortfolios")),
)
@staticmethod
def _build_portfolio_summary(portfolios: Dict) -> pd.DataFrame:
# this happens for added scenarios that havent run yet
if portfolios is None:
return pd.DataFrame()
df_dict = defaultdict(list)
for pf in portfolios:
p = pf["portfolio"]
df_dict["portfolio_id"].append(p["id"])
df_dict["scenario_name"].append(p["name"])
df_dict["model_id"].append(p["modelId"])
df_dict["status"].append(p["status"])
if p["status"] != "READY": # data isn't yet available
df_dict["1M"].append(np.nan)
df_dict["3M"].append(np.nan)
df_dict["1Y"].append(np.nan)
df_dict["sharpe"].append(np.nan)
df_dict["vol"].append(np.nan)
else:
# NOTE:
# these are the magic indices/keys of these values; inspect passed arg to see
# if upstream changes the order of (sub-)elements in the response...uh oh
# TODO: specific key search? wrap with try/except and fail loudly?
df_dict["1M"].append(p["performanceGrid"][0][4])
df_dict["3M"].append(p["performanceGrid"][0][5])
df_dict["1Y"].append(p["performanceGrid"][0][7])
df_dict["sharpe"].append(p["tearSheet"][0]["members"][1]["value"])
pf_stddev = p["tearSheet"][1]["members"][1]["value"]
bnchmk_stddev = p["tearSheet"][1]["members"][2]["value"]
df_dict["vol"].append((pf_stddev - bnchmk_stddev) * 100)
# TODO: this is how it is done on the front-end,
# should be pulled out of both here and there
df = pd.DataFrame(df_dict)
return df
@dataclass(frozen=True) # from _python client's_ perspective, instances will never change!
class HedgeExperimentDetails:
# this will probably lead to violations of the law of demeter, but it's easy to do right now,
# particularly for an unproven API
experiment: HedgeExperiment
scenarios: Dict[str, HedgeExperimentScenario]
@property
def summary(self) -> pd.DataFrame:
"""
Returns a dataframe corresponding to the Hedge Experiment "flat view" - all portfolio
options are displayed
"""
if len(self.scenarios) == 0 or all(s.status != "READY" for s in self.scenarios.values()):
return pd.DataFrame()
return pd.concat(scenario.summary for scenario in self.scenarios.values()).sort_values(
["scenario_name"]
) # sort for stable presentation
@classmethod
def from_json_dict(cls, d: Dict) -> "HedgeExperimentDetails":
he = HedgeExperiment.from_json_dict(d)
scenarios = {
scenario_dict["hedgeExperimentScenarioId"]: HedgeExperimentScenario.from_json_dict(
scenario_dict
)
for scenario_dict in d["hedgeExperimentScenarios"]
}
return cls(he, scenarios)
Classes
class BoostedDataSetSchemaException (value, data=None)
-
Common base class for all non-exit exceptions.
Expand source code
class BoostedDataSetSchemaException(Exception): def __init__(self, value, data=None): self.value = value self.data = data def __str__(self): return repr(self.value)
Ancestors
- builtins.Exception
- builtins.BaseException
class ChunkStatus (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code
class ChunkStatus(Enum): PROCESSING = "PROCESSING" ABORTED = "ABORTED" SUCCESS = "SUCCESS" WARNING = "WARNING" ERROR = "ERROR"
Ancestors
- enum.Enum
Class variables
var ABORTED
var ERROR
var PROCESSING
var SUCCESS
var WARNING
class ColumnConfig (name=None, role=None, sub_role=None, value_type=NUMBER, description='', investment_horizon=None)
-
Expand source code
class ColumnConfig: def __init__( self, name=None, role=None, sub_role=None, value_type=ColumnValueType.NUMBER, description="", investment_horizon=None, ): self.name = name self.role = role self.sub_role = sub_role self.value_type = value_type # More value_types will be supported in the future. self.description = description self.investment_horizon = investment_horizon def __str__(self): return 'Name: "{0}", Role: {1}, SubRole: {2}, Value type: {3}, Desc: {4} IH: {5}.'.format( self.name, self.role, self.sub_role, self.value_type, self.description, self.investment_horizon, ) def __repr__(self): return self.__str__()
class ColumnRole (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code
class ColumnRole(Enum): UNKNOWN = 0 VARIABLE = 1 GOAL = 2 METRIC = 3 IDENTIFIER = 4 def __str__(self): if self.name == "VARIABLE": return "VARIABLE" elif self.name == "GOAL": return "GOAL" elif self.name == "METRIC": return "METRIC" elif self.name == "IDENTIFIER": return "IDENTIFIER" else: return "UNKNOWN"
Ancestors
- enum.Enum
Class variables
var GOAL
var IDENTIFIER
var METRIC
var UNKNOWN
var VARIABLE
class ColumnSubRole (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code
class ColumnSubRole(Enum): UNKNOWN = 0 GBI_ID = 1 ISIN = 2 GVKEY = 3 IID = 4 SYMBOL = 5 COUNTRY = 6 CURRENCY = 7 DATE = 8 COMPANY_NAME = 9 REPORT_DATE = 10 REPORT_PERIOD = 11 def __str__(self): if self.name == "GBI_ID": return "GBI_ID" elif self.name == "ISIN": return "ISIN" elif self.name == "GVKEY": return "GVKEY" elif self.name == "IID": return "IID" elif self.name == "SYMBOL": return "SYMBOL" elif self.name == "COUNTRY": return "COUNTRY" elif self.name == "CURRENCY": return "CURRENCY" elif self.name == "DATE": return "DATE" elif self.name == "COMPANY_NAME": return "COMPANY_NAME" elif self.name == "REPORT_DATE": return "REPORT_DATE" elif self.name == "REPORT_PERIOD": return "REPORT_PERIOD" else: return "UNKNOWN" def get_match(column_name): def clean_str(name): translation = str.maketrans("", "", string.punctuation) clean_name = name.strip().translate(translation).lower() return re.sub(r"\s+", "", clean_name) identifiers = [ ColumnSubRole.GBI_ID, ColumnSubRole.ISIN, ColumnSubRole.GVKEY, ColumnSubRole.IID, ColumnSubRole.SYMBOL, ColumnSubRole.COUNTRY, ColumnSubRole.CURRENCY, ColumnSubRole.COMPANY_NAME, ColumnSubRole.REPORT_DATE, ColumnSubRole.REPORT_PERIOD, ] for identifier in identifiers: if clean_str(str(identifier)) == clean_str(column_name): return identifier return None
Ancestors
- enum.Enum
Class variables
var COMPANY_NAME
var COUNTRY
var CURRENCY
var DATE
var GBI_ID
var GVKEY
var IID
var ISIN
var REPORT_DATE
var REPORT_PERIOD
var SYMBOL
var UNKNOWN
Methods
def get_match(column_name)
-
Expand source code
def get_match(column_name): def clean_str(name): translation = str.maketrans("", "", string.punctuation) clean_name = name.strip().translate(translation).lower() return re.sub(r"\s+", "", clean_name) identifiers = [ ColumnSubRole.GBI_ID, ColumnSubRole.ISIN, ColumnSubRole.GVKEY, ColumnSubRole.IID, ColumnSubRole.SYMBOL, ColumnSubRole.COUNTRY, ColumnSubRole.CURRENCY, ColumnSubRole.COMPANY_NAME, ColumnSubRole.REPORT_DATE, ColumnSubRole.REPORT_PERIOD, ] for identifier in identifiers: if clean_str(str(identifier)) == clean_str(column_name): return identifier return None
class ColumnValueType (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code
class ColumnValueType(Enum): UNKNOWN = 0 NUMBER = 1 STRING = 2 def __str__(self): if self.name == "UNKNOWN": return "UNKNOWN" elif self.name == "NUMBER": return "NUMBER" elif self.name == "STRING": return "STRING" else: return "UNKNOWN"
Ancestors
- enum.Enum
Class variables
var NUMBER
var STRING
var UNKNOWN
class DataAddType (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code
class DataAddType(Enum): CREATION = 1 HISTORICAL = 2 LIVE = 3 def __str__(self): if self.name == "CREATION": return "CREATION" elif self.name == "HISTORICAL": return "HISTORICAL" elif self.name == "LIVE": return "LIVE"
Ancestors
- enum.Enum
Class variables
var CREATION
var HISTORICAL
var LIVE
class DataSetConfig (name, datasetType=STOCK, datasetSubType=DENSE, datasetFrequency=DAILY)
-
Expand source code
class DataSetConfig: def __init__( self, name, datasetType=DataSetType.STOCK, datasetSubType=DataSetSubType.DENSE, datasetFrequency=DataSetFrequency.DAILY, ): self.name = name self.type = datasetType self.subtype = datasetSubType self.frequency = datasetFrequency self.columns = [] self.strategies = [] def addColumn(self, columnConfig): self.columns.append(columnConfig) def addStrategy(self, strategyConfig): self.strategies.append(strategyConfig) def __getColumnByName(self, col_name): for column in self.columns: if col_name == column.name: return column raise BoostedDataSetSchemaException(f"Unable to find column {col_name}") def updateColumnToGoal(self, col_name, investment_horizon): column = self.__getColumnByName(col_name) column.role = ColumnRole.GOAL column.investment_horizon = int(investment_horizon) def updateColumnToMetric(self, col_name): column = self.__getColumnByName(col_name) column.role = ColumnRole.METRIC def updateColumnToVariable(self, col_name): column = self.__getColumnByName(col_name) column.role = ColumnRole.VARIABLE def __validateSchema(self): num_goals = 0 num_metrics = 0 dt = self.type dst = self.subtype dsf = self.frequency if len(self.columns) == 0: msg = "No feature columns exist." raise BoostedDataSetSchemaException(msg) if dst in [DataSetSubType.SPARSE_HIST, DataSetSubType.SPARSE_FWD]: if dsf not in [DataSetFrequency.QUARTERLY]: msg = f"{dsf} frequency is not supported for {dst} sub data" raise BoostedDataSetSchemaException(msg) if dt not in [DataSetType.STOCK]: msg = f"{dst} subtype is not supported for {dt} data" raise BoostedDataSetSchemaException(msg) for column in self.columns: if column.role == ColumnRole.GOAL: ih = column.investment_horizon gn = column.name if dt == DataSetType.GLOBAL: msg = f"{dt} data can not have {ColumnRole.GOAL} type" raise BoostedDataSetSchemaException(msg) if not isinstance(ih, int): msg = f"Investment horizon for {gn} must be an integer" raise BoostedDataSetSchemaException(msg) if ih < 1 or ih > 252: msg = f"Investment horizon must be between 1 and 252 for {gn}" raise BoostedDataSetSchemaException(msg) num_goals += 1 elif column.role == ColumnRole.METRIC: if dt in [DataSetType.GLOBAL, DataSetType.STOCK]: msg = f"{dt} data can not have {ColumnRole.METRIC} type" raise BoostedDataSetSchemaException(msg) num_metrics += 1 if dt == DataSetType.STRATEGY: if num_goals == 0: msg = "Independent data requires at least one goal." raise BoostedDataSetSchemaException(msg) if num_metrics == 0: msg = "Independent data requires at least one metric." raise BoostedDataSetSchemaException(msg) if len(self.strategies) <= 1: msg = "Independent data requires more than 1 strategy" raise BoostedDataSetSchemaException(msg) def toDict(self): self.__validateSchema() config = {} config["name"] = self.name config["type"] = str(self.type) config["subType"] = str(self.subtype) config["frequency"] = str(self.frequency) featureList = [] for i, f in enumerate(self.columns): fm = {} fm["name"] = f.name fm["description"] = f.description fm["type"] = str(f.role) fm["role"] = str(f.role) fm["subRole"] = str(f.sub_role) if f.sub_role is not None else None fm["valuesType"] = str(f.value_type) fm["columnName"] = f.name fm["investmentHorizon"] = f.investment_horizon featureList.append(fm) config["features"] = featureList strategyList = [] for i, s in enumerate(self.strategies): sm = {} sm["name"] = s.name sm["sourceName"] = s.source_name strategyList.append(sm) config["strategies"] = strategyList return config def __str__(self): a = "" a += "Name: {0}\n".format(self.name) a += "Columns: \n" for c in self.columns: a += " {0}\n".format(c) return a def __repr__(self): return self.__str__() def fromDict(schema): subtype = DataSetSubType[schema.get("subtype", str(DataSetSubType.DENSE))] frequency = DataSetFrequency[schema.get("frequency", str(DataSetFrequency.DAILY))] config = DataSetConfig( schema["name"], datasetType=DataSetType[schema["type"]], datasetSubType=subtype, datasetFrequency=frequency, ) # two things left to fill in - strategies and columns for strategy_data in schema["strategies"]: config.addStrategy( StrategyConfig(name=strategy_data["name"], source_name=strategy_data["sourceName"]) ) for feature_data in schema["features"]: config.addColumn( ColumnConfig( name=feature_data["columnName"], description=feature_data["description"] or "", investment_horizon=feature_data["investmentHorizon"], value_type=ColumnValueType[feature_data["valuesType"]] if feature_data["valuesType"] is not None else ColumnValueType.NUMBER, role=ColumnRole[feature_data["role"]] if feature_data["role"] is not None else None, sub_role=ColumnSubRole[feature_data["subRole"]] if feature_data["subRole"] is not None else None, ) ) config.__validateSchema() return config
Methods
def addColumn(self, columnConfig)
-
Expand source code
def addColumn(self, columnConfig): self.columns.append(columnConfig)
def addStrategy(self, strategyConfig)
-
Expand source code
def addStrategy(self, strategyConfig): self.strategies.append(strategyConfig)
def fromDict(schema)
-
Expand source code
def fromDict(schema): subtype = DataSetSubType[schema.get("subtype", str(DataSetSubType.DENSE))] frequency = DataSetFrequency[schema.get("frequency", str(DataSetFrequency.DAILY))] config = DataSetConfig( schema["name"], datasetType=DataSetType[schema["type"]], datasetSubType=subtype, datasetFrequency=frequency, ) # two things left to fill in - strategies and columns for strategy_data in schema["strategies"]: config.addStrategy( StrategyConfig(name=strategy_data["name"], source_name=strategy_data["sourceName"]) ) for feature_data in schema["features"]: config.addColumn( ColumnConfig( name=feature_data["columnName"], description=feature_data["description"] or "", investment_horizon=feature_data["investmentHorizon"], value_type=ColumnValueType[feature_data["valuesType"]] if feature_data["valuesType"] is not None else ColumnValueType.NUMBER, role=ColumnRole[feature_data["role"]] if feature_data["role"] is not None else None, sub_role=ColumnSubRole[feature_data["subRole"]] if feature_data["subRole"] is not None else None, ) ) config.__validateSchema() return config
def toDict(self)
-
Expand source code
def toDict(self): self.__validateSchema() config = {} config["name"] = self.name config["type"] = str(self.type) config["subType"] = str(self.subtype) config["frequency"] = str(self.frequency) featureList = [] for i, f in enumerate(self.columns): fm = {} fm["name"] = f.name fm["description"] = f.description fm["type"] = str(f.role) fm["role"] = str(f.role) fm["subRole"] = str(f.sub_role) if f.sub_role is not None else None fm["valuesType"] = str(f.value_type) fm["columnName"] = f.name fm["investmentHorizon"] = f.investment_horizon featureList.append(fm) config["features"] = featureList strategyList = [] for i, s in enumerate(self.strategies): sm = {} sm["name"] = s.name sm["sourceName"] = s.source_name strategyList.append(sm) config["strategies"] = strategyList return config
def updateColumnToGoal(self, col_name, investment_horizon)
-
Expand source code
def updateColumnToGoal(self, col_name, investment_horizon): column = self.__getColumnByName(col_name) column.role = ColumnRole.GOAL column.investment_horizon = int(investment_horizon)
def updateColumnToMetric(self, col_name)
-
Expand source code
def updateColumnToMetric(self, col_name): column = self.__getColumnByName(col_name) column.role = ColumnRole.METRIC
def updateColumnToVariable(self, col_name)
-
Expand source code
def updateColumnToVariable(self, col_name): column = self.__getColumnByName(col_name) column.role = ColumnRole.VARIABLE
class DataSetFrequency (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code
class DataSetFrequency(Enum): UNKNOWN = 0 DAILY = 1 WEEKLY = 2 MONTHLY = 3 QUARTERLY = 4 SEMIANNUAL = 5 ANNUAL = 6 def __str__(self): if self.name == "DAILY": return "DAILY" elif self.name == "WEEKLY": return "WEEKLY" elif self.name == "MONTHLY": return "MONTHLY" elif self.name == "QUARTERLY": return "QUARTERLY" elif self.name == "SEMIANNUAL": return "SEMIANNUAL" elif self.name == "ANNUAL": return "ANNUAL" else: return "UNKNOWN"
Ancestors
- enum.Enum
Class variables
var ANNUAL
var DAILY
var MONTHLY
var QUARTERLY
var SEMIANNUAL
var UNKNOWN
var WEEKLY
class DataSetSubType (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code
class DataSetSubType(Enum): UNKNOWN = 0 DENSE = 1 SPARSE_HIST = 2 SPARSE_FWD = 3 def __str__(self): if self.name == "DENSE": return "DENSE" elif self.name == "SPARSE_HIST": return "SPARSE_HIST" elif self.name == "SPARSE_FWD": return "SPARSE_FWD" else: return "UNKNOWN"
Ancestors
- enum.Enum
Class variables
var DENSE
var SPARSE_FWD
var SPARSE_HIST
var UNKNOWN
class DataSetType (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code
class DataSetType(Enum): UNKNOWN = 0 STOCK = 1 GLOBAL = 2 STRATEGY = 3 def __str__(self): if self.name == "STOCK": return "STOCK" elif self.name == "GLOBAL": return "GLOBAL" elif self.name == "STRATEGY": return "STRATEGY" else: return "UNKNOWN"
Ancestors
- enum.Enum
Class variables
var GLOBAL
var STOCK
var STRATEGY
var UNKNOWN
class DateIdentCountryCurrency (date: str, identifier: str, country: Optional[str] = None, currency: Optional[str] = None, id_type: ColumnSubRole = ISIN)
-
Expand source code
class DateIdentCountryCurrency: def __init__( self, date: str, identifier: str, country: Optional[str] = None, currency: Optional[str] = None, id_type: ColumnSubRole = ColumnSubRole.ISIN, ): self.date = date self.identifier = identifier self.id_type = id_type self.country = country self.currency = currency self.__validateInput() def __str__(self): return '(date: "{0}", identifier: "{1}", country: {2}, currency: {3})'.format( self.date, self.identifier, self.country, self.currency ) def __repr__(self): return self.__str__() def __validateInput(self): # simply ensure that at least isin and date are filled out. # country/currency defaults to ANY if not filled out, but # still is not recommended. if self.identifier is None or self.date is None: raise DateIdentCountryCurrencyException( "ISIN or Date not provided while constructing DateIsinCountryCurrency" ) if self.id_type not in (ColumnSubRole.ISIN, ColumnSubRole.SYMBOL): raise DateIdentCountryCurrencyException(f"Invalid ID Type: {self.id_type}") def check_is_str(value, fieldname): if value is not None and not isinstance(value, str): raise DateIdentCountryCurrencyException( f"Field {fieldname} in DateIsinCountryCurrency was not a string." ) check_is_str(self.date, "date") check_is_str(self.identifier, "identifier") check_is_str(self.country, "country") check_is_str(self.currency, "currency")
Subclasses
class DateIdentCountryCurrencyException (value, data=None)
-
Common base class for all non-exit exceptions.
Expand source code
class DateIdentCountryCurrencyException(Exception): def __init__(self, value, data=None): self.value = value self.data = data def __str__(self): return repr(self.value)
Ancestors
- builtins.Exception
- builtins.BaseException
Subclasses
class DateIsinCountryCurrency (date, isin, country, currency)
-
Expand source code
class DateIsinCountryCurrency(DateIdentCountryCurrency): def __init__(self, date, isin, country, currency): super().__init__(date, isin, country, currency, ColumnSubRole.ISIN) self.isin = isin def __str__(self): return '(date: "{0}", isin: "{1}", country: {2}, currency: {3})'.format( self.date, self.isin, self.country, self.currency )
Ancestors
class DateIsinCountryCurrencyException (value, data=None)
-
Common base class for all non-exit exceptions.
Expand source code
class DateIsinCountryCurrencyException(DateIdentCountryCurrencyException): pass
Ancestors
- DateIdentCountryCurrencyException
- builtins.Exception
- builtins.BaseException
class GbiIdSecurity (gbi_id, isin_country_currency_date, ticker, company_name)
-
Expand source code
class GbiIdSecurity: def __init__(self, gbi_id, isin_country_currency_date, ticker, company_name): self.gbi_id = gbi_id self.isin_info = isin_country_currency_date self.ticker = ticker self.company_name = company_name def __str__(self): return '\n(gbi_id: "{0}", isin_info: {1}, ticker: {2}, company_name: {3})'.format( self.gbi_id, self.isin_info.__str__(), self.ticker, self.company_name ) def __repr__(self): return self.__str__() def __eq__(self, __o: object) -> bool: return self.gbi_id == __o.gbi_id def __hash__(self): return hash(self.gbi_id)
class GbiIdSecurityException (value, data=None)
-
Common base class for all non-exit exceptions.
Expand source code
class GbiIdSecurityException(Exception): def __init__(self, value, data=None): self.value = value self.data = data def __str__(self): return repr(self.value)
Ancestors
- builtins.Exception
- builtins.BaseException
class HedgeExperiment (id: str, name: str, user_id: str, description: str, experiment_type: Literal['HEDGE', 'MIMIC'], last_calculated: datetime.datetime, last_modified: datetime.datetime, status: str, portfolio_calculation_status: str, selected_models: List[str], target_securities: Dict[GbiIdSecurity, float], target_portfolios: List[str], selected_stock_universe_ids: List[str], baseline_model: Optional[str] = None, baseline_stock_universe_id: Optional[str] = None, baseline_scenario: Optional[ForwardRef('HedgeExperimentScenario')] = None)
-
HedgeExperiment(id: str, name: str, user_id: str, description: str, experiment_type: Literal['HEDGE', 'MIMIC'], last_calculated: datetime.datetime, last_modified: datetime.datetime, status: str, portfolio_calculation_status: str, selected_models: List[str], target_securities: Dict[boosted.api.api_type.GbiIdSecurity, float], target_portfolios: List[str], selected_stock_universe_ids: List[str], baseline_model: Union[str, NoneType] = None, baseline_stock_universe_id: Union[str, NoneType] = None, baseline_scenario: Union[ForwardRef('HedgeExperimentScenario'), NoneType] = None)
Expand source code
class HedgeExperiment: id: str # really a uuid, which we represent as a string name: str user_id: str # see id comment description: str experiment_type: hedge_experiment_type # TODO: enum worth it? last_calculated: dt.datetime last_modified: dt.datetime status: str # TODO: enum worth it? portfolio_calculation_status: str # TODO: enum worth it? selected_models: List[str] # see id comment target_securities: Dict[GbiIdSecurity, float] # Security is a hashable type because frozen=True target_portfolios: List[str] selected_stock_universe_ids: List[str] # see id comment baseline_model: Optional[str] = None baseline_stock_universe_id: Optional[str] = None baseline_scenario: Optional["HedgeExperimentScenario"] = None @property def config(self) -> Dict: """ Returns a hedge experiment configuration dictionary, ready for JSON-serialization and submission. """ weights_by_gbiid = [ {"gbiId": sec.id, "weight": weight} for sec, weight in self.target_securities.items() ] return { "experimentType": self.experiment_type, "selectedModels": self.selected_models, "targetSecurities": weights_by_gbiid, "selectedStockUniverseIds": self._selected_stock_universe_ids, } @classmethod def from_json_dict(cls, d: Dict) -> "HedgeExperiment": # drafts arent fully populated if d["status"] == "DRAFT": weights_by_security = {} selected_models = [] selected_stock_universe_ids = [] else: weights_by_security = { GbiIdSecurity( sec_dict["gbiId"], None, sec_dict["security"]["symbol"], sec_dict["security"]["name"], ): sec_dict["weight"] for sec_dict in d["targetSecurities"] } experiment_config = json.loads(d["config"]) selected_models = experiment_config["selectedModels"] selected_stock_universe_ids = experiment_config["selectedStockUniverseIds"] baseline_scenario = None if d["baselineScenario"] is not None: baseline_scenario = HedgeExperimentScenario.from_json_dict(d["baselineScenario"]) return cls( d["hedgeExperimentId"], d["experimentName"], d["userId"], d["description"], d["experimentType"], d["lastCalculated"], d["lastModified"], d["status"], d["portfolioCalcStatus"], selected_models, weights_by_security, d.get("targetPortfolios"), selected_stock_universe_ids or [], d.get("baselineModel"), d.get("baselineStockUniverseId"), baseline_scenario, )
Class variables
var baseline_model : Optional[str]
var baseline_scenario : Optional[HedgeExperimentScenario]
var baseline_stock_universe_id : Optional[str]
var description : str
var experiment_type : Literal['HEDGE', 'MIMIC']
var id : str
var last_calculated : datetime.datetime
var last_modified : datetime.datetime
var name : str
var portfolio_calculation_status : str
var selected_models : List[str]
var selected_stock_universe_ids : List[str]
var status : str
var target_portfolios : List[str]
var target_securities : Dict[GbiIdSecurity, float]
var user_id : str
Static methods
def from_json_dict(d: Dict[~KT, ~VT]) ‑> HedgeExperiment
-
Expand source code
@classmethod def from_json_dict(cls, d: Dict) -> "HedgeExperiment": # drafts arent fully populated if d["status"] == "DRAFT": weights_by_security = {} selected_models = [] selected_stock_universe_ids = [] else: weights_by_security = { GbiIdSecurity( sec_dict["gbiId"], None, sec_dict["security"]["symbol"], sec_dict["security"]["name"], ): sec_dict["weight"] for sec_dict in d["targetSecurities"] } experiment_config = json.loads(d["config"]) selected_models = experiment_config["selectedModels"] selected_stock_universe_ids = experiment_config["selectedStockUniverseIds"] baseline_scenario = None if d["baselineScenario"] is not None: baseline_scenario = HedgeExperimentScenario.from_json_dict(d["baselineScenario"]) return cls( d["hedgeExperimentId"], d["experimentName"], d["userId"], d["description"], d["experimentType"], d["lastCalculated"], d["lastModified"], d["status"], d["portfolioCalcStatus"], selected_models, weights_by_security, d.get("targetPortfolios"), selected_stock_universe_ids or [], d.get("baselineModel"), d.get("baselineStockUniverseId"), baseline_scenario, )
Instance variables
var config : Dict[~KT, ~VT]
-
Returns a hedge experiment configuration dictionary, ready for JSON-serialization and submission.
Expand source code
@property def config(self) -> Dict: """ Returns a hedge experiment configuration dictionary, ready for JSON-serialization and submission. """ weights_by_gbiid = [ {"gbiId": sec.id, "weight": weight} for sec, weight in self.target_securities.items() ] return { "experimentType": self.experiment_type, "selectedModels": self.selected_models, "targetSecurities": weights_by_gbiid, "selectedStockUniverseIds": self._selected_stock_universe_ids, }
class HedgeExperimentDetails (experiment: HedgeExperiment, scenarios: Dict[str, HedgeExperimentScenario])
-
HedgeExperimentDetails(experiment: boosted.api.api_type.HedgeExperiment, scenarios: Dict[str, boosted.api.api_type.HedgeExperimentScenario])
Expand source code
class HedgeExperimentDetails: # this will probably lead to violations of the law of demeter, but it's easy to do right now, # particularly for an unproven API experiment: HedgeExperiment scenarios: Dict[str, HedgeExperimentScenario] @property def summary(self) -> pd.DataFrame: """ Returns a dataframe corresponding to the Hedge Experiment "flat view" - all portfolio options are displayed """ if len(self.scenarios) == 0 or all(s.status != "READY" for s in self.scenarios.values()): return pd.DataFrame() return pd.concat(scenario.summary for scenario in self.scenarios.values()).sort_values( ["scenario_name"] ) # sort for stable presentation @classmethod def from_json_dict(cls, d: Dict) -> "HedgeExperimentDetails": he = HedgeExperiment.from_json_dict(d) scenarios = { scenario_dict["hedgeExperimentScenarioId"]: HedgeExperimentScenario.from_json_dict( scenario_dict ) for scenario_dict in d["hedgeExperimentScenarios"] } return cls(he, scenarios)
Class variables
var experiment : HedgeExperiment
var scenarios : Dict[str, HedgeExperimentScenario]
Static methods
def from_json_dict(d: Dict[~KT, ~VT]) ‑> HedgeExperimentDetails
-
Expand source code
@classmethod def from_json_dict(cls, d: Dict) -> "HedgeExperimentDetails": he = HedgeExperiment.from_json_dict(d) scenarios = { scenario_dict["hedgeExperimentScenarioId"]: HedgeExperimentScenario.from_json_dict( scenario_dict ) for scenario_dict in d["hedgeExperimentScenarios"] } return cls(he, scenarios)
Instance variables
var summary : pandas.core.frame.DataFrame
-
Returns a dataframe corresponding to the Hedge Experiment "flat view" - all portfolio options are displayed
Expand source code
@property def summary(self) -> pd.DataFrame: """ Returns a dataframe corresponding to the Hedge Experiment "flat view" - all portfolio options are displayed """ if len(self.scenarios) == 0 or all(s.status != "READY" for s in self.scenarios.values()): return pd.DataFrame() return pd.concat(scenario.summary for scenario in self.scenarios.values()).sort_values( ["scenario_name"] ) # sort for stable presentation
class HedgeExperimentScenario (id: str, name: str, description: str, status: str, settings: PortfolioSettings, summary: pandas.core.frame.DataFrame)
-
HedgeExperimentScenario(id: str, name: str, description: str, status: str, settings: boosted.api.api_type.PortfolioSettings, summary: pandas.core.frame.DataFrame)
Expand source code
class HedgeExperimentScenario: id: str # really a uuid, which we represent as a string; name: str description: str status: str # TODO: enum worth it? settings: PortfolioSettings summary: pd.DataFrame # we currently a portfolios field. they get wrapped up into the summary table @classmethod def from_json_dict(cls, d: Dict) -> "HedgeExperimentScenario": return cls( d["hedgeExperimentScenarioId"], d["scenarioName"], d["description"], d.get("status"), PortfolioSettings(json.loads(d["portfolioSettingsJson"])), HedgeExperimentScenario._build_portfolio_summary(d.get("hedgeExperimentPortfolios")), ) @staticmethod def _build_portfolio_summary(portfolios: Dict) -> pd.DataFrame: # this happens for added scenarios that havent run yet if portfolios is None: return pd.DataFrame() df_dict = defaultdict(list) for pf in portfolios: p = pf["portfolio"] df_dict["portfolio_id"].append(p["id"]) df_dict["scenario_name"].append(p["name"]) df_dict["model_id"].append(p["modelId"]) df_dict["status"].append(p["status"]) if p["status"] != "READY": # data isn't yet available df_dict["1M"].append(np.nan) df_dict["3M"].append(np.nan) df_dict["1Y"].append(np.nan) df_dict["sharpe"].append(np.nan) df_dict["vol"].append(np.nan) else: # NOTE: # these are the magic indices/keys of these values; inspect passed arg to see # if upstream changes the order of (sub-)elements in the response...uh oh # TODO: specific key search? wrap with try/except and fail loudly? df_dict["1M"].append(p["performanceGrid"][0][4]) df_dict["3M"].append(p["performanceGrid"][0][5]) df_dict["1Y"].append(p["performanceGrid"][0][7]) df_dict["sharpe"].append(p["tearSheet"][0]["members"][1]["value"]) pf_stddev = p["tearSheet"][1]["members"][1]["value"] bnchmk_stddev = p["tearSheet"][1]["members"][2]["value"] df_dict["vol"].append((pf_stddev - bnchmk_stddev) * 100) # TODO: this is how it is done on the front-end, # should be pulled out of both here and there df = pd.DataFrame(df_dict) return df
Class variables
var description : str
var id : str
var name : str
var settings : PortfolioSettings
var status : str
var summary : pandas.core.frame.DataFrame
Static methods
def from_json_dict(d: Dict[~KT, ~VT]) ‑> HedgeExperimentScenario
-
Expand source code
@classmethod def from_json_dict(cls, d: Dict) -> "HedgeExperimentScenario": return cls( d["hedgeExperimentScenarioId"], d["scenarioName"], d["description"], d.get("status"), PortfolioSettings(json.loads(d["portfolioSettingsJson"])), HedgeExperimentScenario._build_portfolio_summary(d.get("hedgeExperimentPortfolios")), )
class IdentifierToColumnMapping (mapping)
-
IdentifierToColumnMapping is a mapping from string to boosted.api.api_type.ColumnSubRole. The key of the mapping corresponds to a CSV column name, and the value corresponds to the type of identifier that it maps to. Only columns specifying an identifying factor for the stock needs to be specified (e.g. ISIN, Date, Symbol, Currency)
Expand source code
class IdentifierToColumnMapping: """ IdentifierToColumnMapping is a mapping from string to boosted.api.api_type.ColumnSubRole. The key of the mapping corresponds to a CSV column name, and the value corresponds to the type of identifier that it maps to. Only columns specifying an identifying factor for the stock needs to be specified (e.g. ISIN, Date, Symbol, Currency) """ def __init__(self, mapping): self.mapping = mapping self.__validateInput() def __str__(self): return repr(self.value) def __validateInput(self): def validate_key_val(key, val): if not isinstance(key, str): raise Exception(f"key in IdentifierToColumnMapping {key} was not a string!") if not isinstance(val, ColumnSubRole): raise Exception(f"val in IdentifierToColumnMapping {val} was not a ColumnSubRole!") for key, val in self.mapping: validate_key_val(key, val)
class PortfolioSettings (settings: Optional[Dict[~KT, ~VT]] = None)
-
This config is a documentation wrapper around a dict representing the portfolio settings json. Keys missing in the dict provided will fallback to the listed default when updating the the portfolio settings on the server.
Parameters
activeRisk
:bool
-
- Active Risk: Set the portfolio optimization parameters to be relative to the score from the stock universe. i.e. if you set Momentum to 0.5 to 1.0 and the stock universe has a market cap weighed score of 0.2 then the optimizer will solve for 0.7 to 1.2.
- Constraints: Boolean value
- Default Value: false
allowCompanyCollisions
:bool
-
- Allow Multiple Securities Per Company: Allows the machine to trade multiple securities (at the same time) for the same company. If it is ON and a company has listed PREF shares and COMMON shares, both will show up as tradeable options for that company. If it is OFF then system will try to determine the "correct" security to trade based on volume, liquidity, and share type.
- Constraints: Boolean value
- Default Value: true
allowDR
:bool
-
- Allow Depositary Receipts: Companies with tickers ending in .Y and .F will be disallowed from trading within the model with this setting off. Turn it on to allow trading in these securities.
- Constraints: Boolean value
- Default Value: true
benchmark
:array
-
- Benchmark: Set one or more benchmarks and the weighting for each.
- Constraints: Required. List of dicts with keys: gbi_id: integer representing the GBI ID of the benchmark index. weight: floating point value between 0 and 1. All gbi_id's in the dictionary must be unique, and the benchmark weights must sum to a number between 0 and 1.
- Default Value: []
benchmarkAllocation
:float
-
- Benchmark Allocation: None
- Constraints: Floating point value representing a percentage between -100 and 100
- Default Value: 0
beta_neutral
:bool
-
- Beta Neutral: Adjust the weights to get the ex-ante Beta to be the net exposure of the portfolio. i.e. 70% Long / 30% Short = 0.4 Beta"
- Constraints: Boolean value
- Default Value: false
bounds_method
:str | null
-
- Optimizer Bounds: Tight: The optimizer will only allow the weightings of securities in your portfolio to stay tight (close) to their initial weighting. Loose: The optimizer will allow the weightings of securities in your portfolio to move a medium amount more from their initial weighting. Wide: The optimizer will allow almost any weight between the minimum and maximum weight per long or short position.
- Constraints: String enum of {loose, tight, wide}, or null
- Default Value: null
compounding
:bool
-
- Compound Returns: When toggled on, this will allow the portfolio to compound returns.
- Constraints: Boolean value
- Default Value: true
currency
:str
-
- Currency: The currency you would like your portfolio to be calculated in. Note that if the currency differs from its exchange (i.e. GBP on the S&P), the prior close foreign exchange rate will be used for calculations.
- Constraints: String representing a 3 digit ISO currency code.
- Default Value: "USD"
equalWeightBenchmark
:bool
-
- Equal Weight Benchmark: Instead of using the defined benchmarks, set the benchmark to be an equal weighted (per rebalance period) version of the stock universe.
- Constraints: Boolean value
- Default Value: false
executionDelay
:int
-
- Execution Delay: This option adds the number of days selected to the trade execution. If you select T+1 and your rebalance period is set to Weekly (Monday), it will trade on Tuesday, and so on.
- Constraints: Integer value between 0 and 100, inclusive.
- Default Value: 0
factorNeutralizeSignal
:bool
-
- Signal Optimizer: Turn on optimization at the signal level that will adjust signals and rankings according to the specifications you set. This is done prior to portfolio construction and can help reduce bias in the model.
- Constraints: Boolean value
- Default Value: false
investmentHorizon
:int
-
- Investment Horizon: The investment horizon for the portfolio.
- Constraints: An integer representing a number of days
- Default Value: 21
lower_turnover
:bool
-
- Lower Turnover: If toggled on the optimizer will try to solve the secondary goal of optimizing the primary goals while limiting turnover.
- Constraints: Boolean value
- Default Value: false
marketCapBounds
:float
-
- Maximum Market Cap Variation: How far from the target market cap weighted index each stock can deviate (positive or negative).
- Constraints: Floating point value representing a percentage between 0 and 10
- Default Value: 1
marketCapConstraint
:bool
-
- Constrain Market Cap: Force weighting to be near the target weights for a market cap weighted index of your entire stock universe.
- Constraints: Boolean value
- Default Value: false
marketCapFlex
:float
-
- Market Cap Flex: How far from the target market cap weighted the stock can deviate relative to the existing weight (i.e. at 75% a 1% position cannot exceed 1.75%).
- Constraints: Floating point value representing a percentage between 10 and 200
- Default Value: 100
maxLongPosition
:float
-
- Max Long Position per Stock: The maximum weighting of a long position within the portfolio.
- Constraints: Required. Floating point value representing a percentage between 0.0 and 300.0
- Default Value: 10.0
maxNumStockLong
:int
-
- Maximum Number of Longs: The maximum number of stocks to buy. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be maximum per sector.
- Constraints: Integer value between 0 and 1000, inclusive. Must be >= minNumStockLong
- Default Value: 50
maxNumStockShort
:int
-
- Maximum Number of Shorts: The maximum number of stocks to short. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be maximum per sector.
- Constraints: Integer value between 0 and 1000, inclusive. Must be >= minNumStockShort
- Default Value: 50
maxOwnership
:float
-
- Maximum Ownership: The maximum percentage of a company the portfolio is allowed to own. For example, if a company had a market cap of $100MM and this was 5% the portfolio could not own more than $5MM of that company.
- Constraints: Floating point value representing a percentage between 0 and 100
- Default Value: 5
maxShortPosition
:float
-
- Max Short Position per Stock: The maximum weighting of a short position within the portfolio.
- Constraints: Required. Floating point value representing a percentage between 0.0 and 300.0
- Default Value: 5.0
minimumSharePrice
:float
-
- Minimum Share Price: The minimum share price you want the portfolio to allow.
- Constraints: Floating point value between 0 and 100000
- Default Value: 2
minLongPosition
:float
-
- Min Long Position per Stock: The minimum weighting of a long position within the portfolio.
- Constraints: Floating point value representing a percentage between 0.0 and 300.0
- Default Value: 0
minNumStockLong
:int
-
- Minimum Number of Longs: The minimum number of stocks to buy. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be minimum per sector.
- Constraints: Integer value between 0 and 1000, inclusive. Must be <= maxNumStockLong
- Default Value: 5
minNumStockShort
:int
-
- Minimum Number of Shorts: The minimum number of stocks to short. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be minimum per sector.
- Constraints: Integer value between 0 and 1000, inclusive. Must be <= maxNumStockShort
- Default Value: 5
minShortPosition
:float
-
- Min Short Position per Stock: The minimum weighting of a short position within the portfolio.
- Constraints: Floating point value representing a percentage between 0.0 and 100.0
- Default Value: 0
missingMarketCap
:float
-
- Missing Market Cap: The amount in millions (MM) to replace any missing market capitalization information with. This will impact the maximum ownership setting.
- Constraints: Floating point value between 0 and 100
- Default Value: 10
nameBasedSectorWeighting
:bool
-
- Name Based Sector Weighting: Base sector weightings on number of names. For example, if there are 200 names in the index and financials has 20 companies, the weight of financials should be 10% (20/200).
- Constraints: Boolean value
- Default Value: false
netBenchmark
:bool
-
- Adjust Benchmark for Net Exposure: If your portfolio has a net exposure greater or less than 100%, this will adjust the benchmark returns to match that net exposure.
- Constraints: Boolean value
- Default Value: false
optimizer
:str
-
- Optimizer Type: No Optimization: None Reduce Risk: Optimizer designed to reduce portfolio volatility. This tends to result in the best overall performance. Maximize Sharpe: Optimizer designed to maximize Sharpe using out of sample estimates for expected return. Estimates for expected return have an outsized impact on the performance of this optimizer. Maximize Alpha: Optimizer designed to maximize expected return based on out-of-sample estimates for expected return. Estimates for expected return have an outsized impact on the performance of this optimizer. Min VaR: Minimize Value at Risk by looking back at the proposed portfolio over the last year and trying to minimize drawdowns. Max VaR Sharpe: Maximize Value at Risk Sharpe by looking back at the proposed portfolio over the last year and trying to maximize Sharpe by observed return for the portfolio vs observed volatility. Minimize Skew: Minimize the skew of the portfolio by trying to find a set of portfolio weightings that results in returns that are closer to the mean.
- Constraints: Required. String enum: one of default: No Optimization min_vol: Reduce Risk max_sharpe: Maximize Sharpe max_alpha: Maximize Alpha min_var: Min VaR max_var_sharpe: Max VaR Sharpe min_skew: Minimize Skew
- Default Value: "default"
optimizeTurnover
:bool
-
- Turnover Optimization: Turnover optimization will reduce the turnover at the signal level, making that ranked list more stable.
- Constraints: Boolean value
- Default Value: false
pairsTrading
:bool
-
- Pairs Trading: Pairs Trading forces the portfolio to be created by going long / short an equal number of stocks. The portfolio will try to find an offsetting position for every long by finding securities that are both highly correlated and have a significant difference in star rating.
- Constraints: Boolean value
- Default Value: false
percentPortfolioLong
:float
-
- Percent of Portfolio Long: The exact sum of all long position weightings in the portfolio.
- Constraints: Required. Floating point value representing a percentage between 0.0 and 300.0
- Default Value: 100.0
percentPortfolioShort
:float
-
- Percent of Portfolio Short: The exact sum of all short position weightings in the portfolio.
- Constraints: Required. Floating point value representing a percentage between 0.0 and 300.0
- Default Value: 0.0
percentToLong
:float
-
- Percent to Long: The machine will attempt to buy the top X% of stocks, subject to the minimum and maximum specified. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be top X% per sector. This will be subject to the maximum and minimum number of securities you specify below.
- Constraints: Floating point value representing a percentage between 0 and 100
- Default Value: 20.0
percentToShort
:float
-
- Percent to Short: The machine will attempt to short the bottom X% of stocks, subject to the minimum and maximum specified. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be bottom X% per sector. This will be subject to the maximum and minimum number of securities you specify below.
- Constraints: Floating point value representing a percentage between 0 and 100
- Default Value: 20.0
portfolioStartingValue
:float
-
- Portfolio Starting Value:
The value of the portfolio in MM when it begins trading. If its backtest spans from 2005
- 2020, the starting value in 2005 will be whatever you set here.
- Constraints: Floating point value between 0 and 100000.
- Default Value: 100.0
- Portfolio Starting Value:
The value of the portfolio in MM when it begins trading. If its backtest spans from 2005
priceCleaning
:bool
-
- Price Cleaning: Price Cleaning removes any very large price movements from the securities, specifically with a daily change greater than +500% or -83.33%. There are some legitimate securities with moves this large that will be impacted by turning this on, but the advantage is it prevents these securities from overwhelming the backtest.
- Constraints: Boolean value
- Default Value: true
priceType
:str
-
- Price Type: None
- Constraints: Required. String enum of {CLOSE, OPEN, VWAP}
- Default Value: "VWAP"
rebalancePeriod
:str
-
- Rebalance Period: When the machine rebalances the portfolio. You can choose a specific day if you prefer to execute trades on a specific day.
- Constraints: Required. An enum value in the following list: "daily" "weekly": Rebalances on every Monday. Chooses the next available trade date to rebalance when Monday is a holiday "weekly_wednesday": Rebalances on every Wednesday. Chooses the next available trade date to rebalance when Wednesday is a holiday "weekly_friday": Rebalances on every Friday. Chooses the previous available trade date to rebalance when Friday is a holiday "monthly" "quarterly" OR a string in the following format: "custom_x" where x is an integer between 1 and 252 (days).
- Default Value: "weekly"
sectorNeutral
:bool
-
- Sector Neutral: Will be constructed as a series of portfolios - each matching the market cap weighting of each of the stock universe sectors. This means that min. and max. number of stocks "per portfolio" becomes "per sector".
- Constraints: Boolean value
- Default Value: false
sectorNeutralEqualWeightBenchmark
:bool
-
- Sector Neutral Equal Weight Benchmark: Instead of using the defined benchmarks, set the benchmark to be a "sector neutral" equal weighted (per rebalance period) version of the stock universe. This takes the sector weighting and divides by the number of stocks in that sector, so each sector will have a different individual security weighting.
- Constraints: Boolean value
- Default Value: false
sectorNeutralSpread
:float
-
- Sector Neutral Spread: The sector neutral spread is how far away from the benchmark sector allocation the machine can deviate. This is adjusted for net exposure, i.e. if your net exposure is 0% the target sector allocations will be 0%.
- Constraints: Floating point value representing a percentage between 0 and 100
- Default Value: 0
signalIndustryNeutral
:bool
-
- Industry Neutral: Make the signal industry neutral, plus or minus the signal sector neutral spread.
- Constraints: Boolean value
- Default Value: false
signalSectorNeutral
:bool
-
- Sector Neutral: Make the signal sector neutral, plus or minus the signal sector neutral spread.
- Constraints: Boolean value
- Default Value: false
signalSectorNeutralSpread
:float
-
- Sector Neutral Spread: Make the signal sector neutral, plus or minus the signal sector neutral spread.
- Constraints: Floating point value representing a percentage between 0 and 100
- Default Value: 1.0
smoothPeriods
:int
-
- Smooth Periods: The number of periods over which to smooth the signals
- Constraints: Integer value between 1 and 100.
- Default Value: 1
smoothWeighting
:str
-
- Smooth Weighting Type: Alpha Weight: The strength of the signal matters in the smoothing. For example, if a stock had a score of 5 stars in period A, 2 stars in period B, your 2 period smoothing would be 7 / 2 = 3.5. Equal Weight: Only the direction of the signal matters here. For example, if you were 5 stars in period A and 2 stars in period B, your 2 period smoothing would be 0 / 2 = 0 (+1 in period 1 and -1 in period 2)
- Constraints: One of {alpha_weight, equal_weight}
- Default Value: "alpha_weight"
stopGain
:bool
-
- Enable Stop Gain: Turn on stop gains for the portfolio. This forces sales of securities that have exceeded the stop gain level. All trades will occur at the next trading time (i.e. next day OPEN for OPEN price models).
- Constraints: Boolean value
- Default Value: false
stopGainAmount
:float
-
- Stop Gain Percentage to Sell: The percentage of the position the machine will sell if the stop gain is triggered.
- Constraints: Floating point value representing a percentage between 1 and 100.
- Default Value: 50.0
stopGainLevel
:float
-
- Stop Gain Threshold: Will sell positions after they hit a threshold. For example, 10% / stop gain, when a position gains 10% the machine will sell a portion of the position (defined by stop gain percentage).
- Constraints: Floating point value representing a percentage between 1 and 100.
- Default Value: 10.0
stopLoss
:bool
-
- Enable Stop Loss: Turn on stop losses for the portfolio. This forces sales of securities that have exceeded the stop loss level. All trades will occur at the next trading time (i.e. next day OPEN for OPEN price models).
- Constraints: Boolean value
- Default Value: false
stopLossAmount
:float
-
- Stop Loss Percentage to Sell: The percentage of the position the machine will sell if the stop loss is triggered.
- Constraints: Floating point value representing a percentage between 1 and 100.
- Default Value: 50.0
stopLossLevel
:float
-
- Stop Loss Threshold: Will sell positions after they hit a threshold. For example, 10% / stop loss, when a position loses 10% the machine will sell a portion of the position (defined by stop loss percentage).
- Constraints: Floating point value representing a percentage between 1 and 100.
- Default Value: 10.0
stopLossReset
:bool
-
- Allow multiple stop loss/gain between rebalance days: Allows the machine to repeatedly trigger stop losses within a rebalance period. For example, if you trade Mondays and the stock drops 15% per day during the week and the stop loss trigger is 10%, then every day will trigger the stop loss. If toggled off the stop loss can only be triggered once per stock per rebalance period.
- Constraints: Boolean value
- Default Value: false
trackingConstraint
:float
-
- Maximum Tracking Error (vs Benchmark): Amount of ex-ante tracking error to constrain the optimizer by.
- Constraints: Floating point value representing a percentage between 0 and 30
- Default Value: 10
trackingError
:bool
-
- Allocate Weight to Benchmark: Allocate a portion (%) of portfolio to the benchmark
- Constraints: Boolean value
- Default Value: false
trackingErrorOptimizer
:bool
-
- Tracking Error: If toggled on the optimizer will try to solve for an expected tracking error less than the amount specified. This is done out-of-sample and the ex post tracking error will likely be higher.
- Constraints: Boolean value
- Default Value: false
tradingCost
:float
-
- Trading Cost: A cost that will be applied to every trade.
- Constraints: Required. Floating point value representing a percentage between 0.0 and 5.0
- Default Value: 0.01
turnoverImportance
:float
-
- Turnover Importance: High importance means the algorithm will aim to keep turnover low, whereas low importance of turnover will let it vary more.
- Constraints: Floating point value between 0.2 and 5 nonlinearly mapped onto the 1-9 interval.
- Default Value: 1
useHoldingPeriod
:bool
-
- Use Holding Period: Set any covariance or other calculations within the optimizer to use a holding period return instead of daily return series
- Constraints: Boolean value
- Default Value: false
weightingType
:str
-
- Initial Weighting: Alpha Weight: The initial weighting will be done such that higher ranked stocks have a higher weight. Equal Weight: The initial weighting will be done such that all stocks selected to be in the portfolio will have an equal weight. Market Cap Weight: The initial weighting will be done such that all stocks are weighted according to their previous day’s market capitalization.
- Constraints: One of {alpha_weight, equal_weight, market_cap_weight}
- Default Value: "alpha_weight"
Expand source code
class PortfolioSettings: """ This config is a documentation wrapper around a dict representing the portfolio settings json. Keys missing in the dict provided will fallback to the listed default when updating the the portfolio settings on the server. Parameters ---------- activeRisk: bool - **Active Risk**: Set the portfolio optimization parameters to be relative to the score from the stock universe. i.e. if you set Momentum to 0.5 to 1.0 and the stock universe has a market cap weighed score of 0.2 then the optimizer will solve for 0.7 to 1.2. - Constraints: Boolean value - Default Value: false allowCompanyCollisions: bool - **Allow Multiple Securities Per Company**: Allows the machine to trade multiple securities (at the same time) for the same company. If it is ON and a company has listed PREF shares and COMMON shares, both will show up as tradeable options for that company. If it is OFF then system will try to determine the "correct" security to trade based on volume, liquidity, and share type. - Constraints: Boolean value - Default Value: true allowDR: bool - **Allow Depositary Receipts**: Companies with tickers ending in .Y and .F will be disallowed from trading within the model with this setting off. Turn it on to allow trading in these securities. - Constraints: Boolean value - Default Value: true benchmark: array - **Benchmark**: Set one or more benchmarks and the weighting for each. - Constraints: Required. List of dicts with keys: gbi_id: integer representing the GBI ID of the benchmark index. weight: floating point value between 0 and 1. All gbi_id's in the dictionary must be unique, and the benchmark weights must sum to a number between 0 and 1. - Default Value: [] benchmarkAllocation: float - **Benchmark Allocation**: None - Constraints: Floating point value representing a percentage between -100 and 100 - Default Value: 0 beta_neutral: bool - **Beta Neutral**: Adjust the weights to get the ex-ante Beta to be the net exposure of the portfolio. i.e. 70% Long / 30% Short = 0.4 Beta" - Constraints: Boolean value - Default Value: false bounds_method: str | null - **Optimizer Bounds**: Tight: The optimizer will only allow the weightings of securities in your portfolio to stay tight (close) to their initial weighting. Loose: The optimizer will allow the weightings of securities in your portfolio to move a medium amount more from their initial weighting. Wide: The optimizer will allow almost any weight between the minimum and maximum weight per long or short position. - Constraints: String enum of {loose, tight, wide}, or null - Default Value: null compounding: bool - **Compound Returns**: When toggled on, this will allow the portfolio to compound returns. - Constraints: Boolean value - Default Value: true currency: str - **Currency**: The currency you would like your portfolio to be calculated in. Note that if the currency differs from its exchange (i.e. GBP on the S&P), the prior close foreign exchange rate will be used for calculations. - Constraints: String representing a 3 digit ISO currency code. - Default Value: "USD" equalWeightBenchmark: bool - **Equal Weight Benchmark**: Instead of using the defined benchmarks, set the benchmark to be an equal weighted (per rebalance period) version of the stock universe. - Constraints: Boolean value - Default Value: false executionDelay: int - **Execution Delay**: This option adds the number of days selected to the trade execution. If you select T+1 and your rebalance period is set to Weekly (Monday), it will trade on Tuesday, and so on. - Constraints: Integer value between 0 and 100, inclusive. - Default Value: 0 factorNeutralizeSignal: bool - **Signal Optimizer**: Turn on optimization at the signal level that will adjust signals and rankings according to the specifications you set. This is done prior to portfolio construction and can help reduce bias in the model. - Constraints: Boolean value - Default Value: false investmentHorizon: int - **Investment Horizon**: The investment horizon for the portfolio. - Constraints: An integer representing a number of days - Default Value: 21 lower_turnover: bool - **Lower Turnover**: If toggled on the optimizer will try to solve the secondary goal of optimizing the primary goals while limiting turnover. - Constraints: Boolean value - Default Value: false marketCapBounds: float - **Maximum Market Cap Variation**: How far from the target market cap weighted index each stock can deviate (positive or negative). - Constraints: Floating point value representing a percentage between 0 and 10 - Default Value: 1 marketCapConstraint: bool - **Constrain Market Cap**: Force weighting to be near the target weights for a market cap weighted index of your entire stock universe. - Constraints: Boolean value - Default Value: false marketCapFlex: float - **Market Cap Flex**: How far from the target market cap weighted the stock can deviate relative to the existing weight (i.e. at 75% a 1% position cannot exceed 1.75%). - Constraints: Floating point value representing a percentage between 10 and 200 - Default Value: 100 maxLongPosition: float - **Max Long Position per Stock**: The maximum weighting of a long position within the portfolio. - Constraints: Required. Floating point value representing a percentage between 0.0 and 300.0 - Default Value: 10.0 maxNumStockLong: int - **Maximum Number of Longs**: The maximum number of stocks to buy. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be maximum per sector. - Constraints: Integer value between 0 and 1000, inclusive. Must be >= minNumStockLong - Default Value: 50 maxNumStockShort: int - **Maximum Number of Shorts**: The maximum number of stocks to short. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be maximum per sector. - Constraints: Integer value between 0 and 1000, inclusive. Must be >= minNumStockShort - Default Value: 50 maxOwnership: float - **Maximum Ownership**: The maximum percentage of a company the portfolio is allowed to own. For example, if a company had a market cap of $100MM and this was 5% the portfolio could not own more than $5MM of that company. - Constraints: Floating point value representing a percentage between 0 and 100 - Default Value: 5 maxShortPosition: float - **Max Short Position per Stock**: The maximum weighting of a short position within the portfolio. - Constraints: Required. Floating point value representing a percentage between 0.0 and 300.0 - Default Value: 5.0 minimumSharePrice: float - **Minimum Share Price**: The minimum share price you want the portfolio to allow. - Constraints: Floating point value between 0 and 100000 - Default Value: 2 minLongPosition: float - **Min Long Position per Stock**: The minimum weighting of a long position within the portfolio. - Constraints: Floating point value representing a percentage between 0.0 and 300.0 - Default Value: 0 minNumStockLong: int - **Minimum Number of Longs**: The minimum number of stocks to buy. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be minimum per sector. - Constraints: Integer value between 0 and 1000, inclusive. Must be <= maxNumStockLong - Default Value: 5 minNumStockShort: int - **Minimum Number of Shorts**: The minimum number of stocks to short. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be minimum per sector. - Constraints: Integer value between 0 and 1000, inclusive. Must be <= maxNumStockShort - Default Value: 5 minShortPosition: float - **Min Short Position per Stock**: The minimum weighting of a short position within the portfolio. - Constraints: Floating point value representing a percentage between 0.0 and 100.0 - Default Value: 0 missingMarketCap: float - **Missing Market Cap**: The amount in millions (MM) to replace any missing market capitalization information with. This will impact the maximum ownership setting. - Constraints: Floating point value between 0 and 100 - Default Value: 10 nameBasedSectorWeighting: bool - **Name Based Sector Weighting**: Base sector weightings on number of names. For example, if there are 200 names in the index and financials has 20 companies, the weight of financials should be 10% (20/200). - Constraints: Boolean value - Default Value: false netBenchmark: bool - **Adjust Benchmark for Net Exposure**: If your portfolio has a net exposure greater or less than 100%, this will adjust the benchmark returns to match that net exposure. - Constraints: Boolean value - Default Value: false optimizer: str - **Optimizer Type**: No Optimization: None Reduce Risk: Optimizer designed to reduce portfolio volatility. This tends to result in the best overall performance. Maximize Sharpe: Optimizer designed to maximize Sharpe using out of sample estimates for expected return. Estimates for expected return have an outsized impact on the performance of this optimizer. Maximize Alpha: Optimizer designed to maximize expected return based on out-of-sample estimates for expected return. Estimates for expected return have an outsized impact on the performance of this optimizer. Min VaR: Minimize Value at Risk by looking back at the proposed portfolio over the last year and trying to minimize drawdowns. Max VaR Sharpe: Maximize Value at Risk Sharpe by looking back at the proposed portfolio over the last year and trying to maximize Sharpe by observed return for the portfolio vs observed volatility. Minimize Skew: Minimize the skew of the portfolio by trying to find a set of portfolio weightings that results in returns that are closer to the mean. - Constraints: Required. String enum: one of default: No Optimization min_vol: Reduce Risk max_sharpe: Maximize Sharpe max_alpha: Maximize Alpha min_var: Min VaR max_var_sharpe: Max VaR Sharpe min_skew: Minimize Skew - Default Value: "default" optimizeTurnover: bool - **Turnover Optimization**: Turnover optimization will reduce the turnover at the signal level, making that ranked list more stable. - Constraints: Boolean value - Default Value: false pairsTrading: bool - **Pairs Trading**: Pairs Trading forces the portfolio to be created by going long / short an equal number of stocks. The portfolio will try to find an offsetting position for every long by finding securities that are both highly correlated and have a significant difference in star rating. - Constraints: Boolean value - Default Value: false percentPortfolioLong: float - **Percent of Portfolio Long**: The exact sum of all long position weightings in the portfolio. - Constraints: Required. Floating point value representing a percentage between 0.0 and 300.0 - Default Value: 100.0 percentPortfolioShort: float - **Percent of Portfolio Short**: The exact sum of all short position weightings in the portfolio. - Constraints: Required. Floating point value representing a percentage between 0.0 and 300.0 - Default Value: 0.0 percentToLong: float - **Percent to Long**: The machine will attempt to buy the top X% of stocks, subject to the minimum and maximum specified. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be top X% per sector. This will be subject to the maximum and minimum number of securities you specify below. - Constraints: Floating point value representing a percentage between 0 and 100 - Default Value: 20.0 percentToShort: float - **Percent to Short**: The machine will attempt to short the bottom X% of stocks, subject to the minimum and maximum specified. This is on a per “portfolio” basis, so if you have sector neutral turned on it will be bottom X% per sector. This will be subject to the maximum and minimum number of securities you specify below. - Constraints: Floating point value representing a percentage between 0 and 100 - Default Value: 20.0 portfolioStartingValue: float - **Portfolio Starting Value**: The value of the portfolio in MM when it begins trading. If its backtest spans from 2005 - 2020, the starting value in 2005 will be whatever you set here. - Constraints: Floating point value between 0 and 100000. - Default Value: 100.0 priceCleaning: bool - **Price Cleaning**: Price Cleaning removes any very large price movements from the securities, specifically with a daily change greater than +500% or -83.33%. There are some legitimate securities with moves this large that will be impacted by turning this on, but the advantage is it prevents these securities from overwhelming the backtest. - Constraints: Boolean value - Default Value: true priceType: str - **Price Type**: None - Constraints: Required. String enum of {CLOSE, OPEN, VWAP} - Default Value: "VWAP" rebalancePeriod: str - **Rebalance Period**: When the machine rebalances the portfolio. You can choose a specific day if you prefer to execute trades on a specific day. - Constraints: Required. An enum value in the following list: "daily" "weekly": Rebalances on every Monday. Chooses the next available trade date to rebalance when Monday is a holiday "weekly_wednesday": Rebalances on every Wednesday. Chooses the next available trade date to rebalance when Wednesday is a holiday "weekly_friday": Rebalances on every Friday. Chooses the previous available trade date to rebalance when Friday is a holiday "monthly" "quarterly" OR a string in the following format: "custom_x" where x is an integer between 1 and 252 (days). - Default Value: "weekly" sectorNeutral: bool - **Sector Neutral**: Will be constructed as a series of portfolios - each matching the market cap weighting of each of the stock universe sectors. This means that min. and max. number of stocks "per portfolio" becomes "per sector". - Constraints: Boolean value - Default Value: false sectorNeutralEqualWeightBenchmark: bool - **Sector Neutral Equal Weight Benchmark**: Instead of using the defined benchmarks, set the benchmark to be a "sector neutral" equal weighted (per rebalance period) version of the stock universe. This takes the sector weighting and divides by the number of stocks in that sector, so each sector will have a different individual security weighting. - Constraints: Boolean value - Default Value: false sectorNeutralSpread: float - **Sector Neutral Spread**: The sector neutral spread is how far away from the benchmark sector allocation the machine can deviate. This is adjusted for net exposure, i.e. if your net exposure is 0% the target sector allocations will be 0%. - Constraints: Floating point value representing a percentage between 0 and 100 - Default Value: 0 signalIndustryNeutral: bool - **Industry Neutral**: Make the signal industry neutral, plus or minus the signal sector neutral spread. - Constraints: Boolean value - Default Value: false signalSectorNeutral: bool - **Sector Neutral**: Make the signal sector neutral, plus or minus the signal sector neutral spread. - Constraints: Boolean value - Default Value: false signalSectorNeutralSpread: float - **Sector Neutral Spread**: Make the signal sector neutral, plus or minus the signal sector neutral spread. - Constraints: Floating point value representing a percentage between 0 and 100 - Default Value: 1.0 smoothPeriods: int - **Smooth Periods**: The number of periods over which to smooth the signals - Constraints: Integer value between 1 and 100. - Default Value: 1 smoothWeighting: str - **Smooth Weighting Type**: Alpha Weight: The strength of the signal matters in the smoothing. For example, if a stock had a score of 5 stars in period A, 2 stars in period B, your 2 period smoothing would be 7 / 2 = 3.5. Equal Weight: Only the direction of the signal matters here. For example, if you were 5 stars in period A and 2 stars in period B, your 2 period smoothing would be 0 / 2 = 0 (+1 in period 1 and -1 in period 2) - Constraints: One of {alpha_weight, equal_weight} - Default Value: "alpha_weight" stopGain: bool - **Enable Stop Gain**: Turn on stop gains for the portfolio. This forces sales of securities that have exceeded the stop gain level. All trades will occur at the next trading time (i.e. next day OPEN for OPEN price models). - Constraints: Boolean value - Default Value: false stopGainAmount: float - **Stop Gain Percentage to Sell**: The percentage of the position the machine will sell if the stop gain is triggered. - Constraints: Floating point value representing a percentage between 1 and 100. - Default Value: 50.0 stopGainLevel: float - **Stop Gain Threshold**: Will sell positions after they hit a threshold. For example, 10% / stop gain, when a position gains 10% the machine will sell a portion of the position (defined by stop gain percentage). - Constraints: Floating point value representing a percentage between 1 and 100. - Default Value: 10.0 stopLoss: bool - **Enable Stop Loss**: Turn on stop losses for the portfolio. This forces sales of securities that have exceeded the stop loss level. All trades will occur at the next trading time (i.e. next day OPEN for OPEN price models). - Constraints: Boolean value - Default Value: false stopLossAmount: float - **Stop Loss Percentage to Sell**: The percentage of the position the machine will sell if the stop loss is triggered. - Constraints: Floating point value representing a percentage between 1 and 100. - Default Value: 50.0 stopLossLevel: float - **Stop Loss Threshold**: Will sell positions after they hit a threshold. For example, 10% / stop loss, when a position loses 10% the machine will sell a portion of the position (defined by stop loss percentage). - Constraints: Floating point value representing a percentage between 1 and 100. - Default Value: 10.0 stopLossReset: bool - **Allow multiple stop loss/gain between rebalance days**: Allows the machine to repeatedly trigger stop losses within a rebalance period. For example, if you trade Mondays and the stock drops 15% per day during the week and the stop loss trigger is 10%, then every day will trigger the stop loss. If toggled off the stop loss can only be triggered once per stock per rebalance period. - Constraints: Boolean value - Default Value: false trackingConstraint: float - **Maximum Tracking Error (vs Benchmark)**: Amount of ex-ante tracking error to constrain the optimizer by. - Constraints: Floating point value representing a percentage between 0 and 30 - Default Value: 10 trackingError: bool - **Allocate Weight to Benchmark**: Allocate a portion (%) of portfolio to the benchmark - Constraints: Boolean value - Default Value: false trackingErrorOptimizer: bool - **Tracking Error**: If toggled on the optimizer will try to solve for an expected tracking error less than the amount specified. This is done out-of-sample and the ex post tracking error will likely be higher. - Constraints: Boolean value - Default Value: false tradingCost: float - **Trading Cost**: A cost that will be applied to every trade. - Constraints: Required. Floating point value representing a percentage between 0.0 and 5.0 - Default Value: 0.01 turnoverImportance: float - **Turnover Importance**: High importance means the algorithm will aim to keep turnover low, whereas low importance of turnover will let it vary more. - Constraints: Floating point value between 0.2 and 5 nonlinearly mapped onto the 1-9 interval. - Default Value: 1 useHoldingPeriod: bool - **Use Holding Period**: Set any covariance or other calculations within the optimizer to use a holding period return instead of daily return series - Constraints: Boolean value - Default Value: false weightingType: str - **Initial Weighting**: Alpha Weight: The initial weighting will be done such that higher ranked stocks have a higher weight. Equal Weight: The initial weighting will be done such that all stocks selected to be in the portfolio will have an equal weight. Market Cap Weight: The initial weighting will be done such that all stocks are weighted according to their previous day’s market capitalization. - Constraints: One of {alpha_weight, equal_weight, market_cap_weight} - Default Value: "alpha_weight" """ # note, this is just the collection of portfolio settings # that is exposed to the API: # SELECT key, default_value # FROM portfolio_settings_validation # WHERE exposed_to_client_api; # TODO: expand to entire settings? why this way initially? __default_settings = { "maxOwnership": "5", "pairsTrading": "false", "minNumStockLong": "5", "stopLossAmount": "50.0", "stopGainLevel": "10.0", "smoothWeighting": '"alpha_weight"', "minimumSharePrice": "2", "maxShortPosition": "5.0", "tradingCost": "0.01", "stopGainAmount": "50.0", "smoothPeriods": "1", "executionDelay": "0", "benchmarkAllocation": "0", "sectorNeutralSpread": "0", "percentToLong": "20.0", "maxNumStockLong": "50", "trackingConstraint": "10", "compounding": "true", "priceCleaning": "true", "lower_turnover": "false", "missingMarketCap": "10", "allowCompanyCollisions": "true", "allowDR": "true", "sectorNeutral": "false", "marketCapFlex": "100", "marketCapBounds": "1", "turnoverImportance": "1", "minNumStockShort": "5", "maxNumStockShort": "50", "beta_neutral": "false", "nameBasedSectorWeighting": "false", "trackingError": "false", "stopLoss": "false", "stopGain": "false", "stopLossReset": "false", "netBenchmark": "false", "equalWeightBenchmark": "false", "marketCapConstraint": "false", "signalIndustryNeutral": "false", "signalSectorNeutral": "false", "sectorNeutralEqualWeightBenchmark": "false", "percentToShort": "20.0", "trackingErrorOptimizer": "false", "currency": '"USD"', "rebalancePeriod": '"weekly"', "benchmark": "[]", "signalSectorNeutralSpread": "1.0", "optimizeTurnover": "false", "minShortPosition": "0", "minLongPosition": "0", "percentPortfolioLong": "100.0", "portfolioStartingValue": "100.0", "stopLossLevel": "10.0", "maxLongPosition": "10.0", "percentPortfolioShort": "0.0", "bounds_method": None, "optimizer": '"default"', "activeRisk": "false", "useHoldingPeriod": "false", "weightingType": '"alpha_weight"', "priceType": '"VWAP"', "factorNeutralizeSignal": "false", "investmentHorizon": "21", } def __init__(self, settings: Optional[Dict] = None): # TODO: i'd rather have kwargs for the different keys and then allow # for per-key overrides, but we start with this more conservative approach if settings is None: self.settings = copy.deepcopy(PortfolioSettings.__default_settings) else: self.settings = settings def toDict(self): return self.settings def __str__(self): return json.dumps(self.settings) def __repr__(self): return self.__str__() def fromDict(settings): return PortfolioSettings(settings)
Methods
def fromDict(settings)
-
Expand source code
def fromDict(settings): return PortfolioSettings(settings)
def toDict(self)
-
Expand source code
def toDict(self): return self.settings
class Status (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code
class Status(Enum): UNKNOWN = 0 FAIL = 1 SUCCESS = 2
Ancestors
- enum.Enum
Class variables
var FAIL
var SUCCESS
var UNKNOWN
class StrategyConfig (name=None, source_name=None)
-
Expand source code
class StrategyConfig: def __init__(self, name=None, source_name=None): self.name = name self.source_name = source_name def __str__(self): return 'Name: "{0}", Source Name: {1}.'.format(self.name, self.source_name) def __repr__(self): return self.__str__()