QuantConnect/Lean · error · ValueError

MaximumSectorExposureRiskManagementModel: the maximum sector

Error message

MaximumSectorExposureRiskManagementModel: the maximum sector exposure cannot be a non-positive value.

What it means

MaximumSectorExposureRiskManagementModel (Python) caps exposure per sector at a fraction of total portfolio value. A non-positive maximum_sector_exposure (≤ 0) would zero-out or invert the cap, which is meaningless for a risk-limit model, so __init__ raises ValueError before any risk management runs.

Source

Thrown at Algorithm.Framework/Risk/MaximumSectorExposureRiskManagementModel.py:25

#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from AlgorithmImports import *
from itertools import groupby

class MaximumSectorExposureRiskManagementModel(RiskManagementModel):
    '''Provides an implementation of IRiskManagementModel that that limits the sector exposure to the specified percentage'''

    def __init__(self, maximum_sector_exposure = 0.20):
        '''Initializes a new instance of the MaximumSectorExposureRiskManagementModel class
        Args:
            maximum_drawdown_percent: The maximum exposure for any sector, defaults to 20% sector exposure.'''
        if maximum_sector_exposure <= 0:
            raise ValueError('MaximumSectorExposureRiskManagementModel: the maximum sector exposure cannot be a non-positive value.')

        self.maximum_sector_exposure = maximum_sector_exposure
        self.targets_collection = PortfolioTargetCollection()

    def manage_risk(self, algorithm, targets):
        '''Manages the algorithm's risk at each time step
        Args:
            algorithm: The algorithm instance'''
        maximum_sector_exposure_value = float(algorithm.portfolio.total_portfolio_value) * self.maximum_sector_exposure

        self.targets_collection.add_range(targets)

        risk_targets = list()

        # Group the securities by their sector
        filtered = list(filter(lambda x: x.value.fundamentals is not None and x.value.fundamentals.has_fundamental_data, algorithm.universe_manager.active_securities))
        filtered.sort(key = lambda x: x.value.fundamentals.company_reference.industry_template_code)
        group_by_sector = groupby(filtered, lambda x: x.value.fundamentals.company_reference.industry_template_code)

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Pass a positive fraction, e.g. 0.20 for 20% — not 20, not 0.
  2. If the value comes from a config/source, validate/coerce it to a positive float before construction.
  3. Confirm units: this is a fraction of total portfolio value, not a percent integer.

Example fix

# before
self.add_risk_management(MaximumSectorExposureRiskManagementModel(0))     # raises
# or
self.add_risk_management(MaximumSectorExposureRiskManagementModel(20))  # means 2000%, wrong unit

# after
self.add_risk_management(MaximumSectorExposureRiskManagementModel(0.20))  # 20% per sector
Defensive patterns

Strategy: validation

Validate before calling

def make_sector_model(max_exposure):
    max_exposure = float(max_exposure)
    if not (0 < max_exposure <= 1):
        raise ValueError('maximum_sector_exposure must be a fraction in (0, 1], e.g. 0.20')
    return MaximumSectorExposureRiskManagementModel(max_exposure)

Type guard

def is_valid_exposure_fraction(v) -> bool:
    try:
        return 0 < float(v) <= 1
    except (TypeError, ValueError):
        return False

Prevention

When it happens

Trigger: Instantiating MaximumSectorExposureRiskManagementModel(maximum_sector_exposure) with 0 or a negative number. Default is 0.20 (20%).

Common situations: Passing the value as a whole number (e.g., 20 meaning 20%) instead of a fraction (0.20); passing a config-decoded string/int that resolved to 0; or a percentage field that defaults to 0 when unset.

Related errors


AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13). Data as JSON: /api/errors/270319e803294ca3. Report an issue: GitHub.