Skip to content

calculate_max_profit() uses incorrect max() fallback for debit spreads in position_models.py #16

@bradsmithmba

Description

@bradsmithmba

Summary

Position.calculate_max_profit() in src/features/position_models.py uses max(spread_width - debit, debit) to compute max profit for BULL_CALL_SPREAD and BEAR_PUT_SPREAD. The correct formula is always spread_width - debit. The max() fallback returns a wrong value when the net debit exceeds half the spread width.

Location

src/features/position_models.py, lines 238–243:

elif self.strategy_type in [StrategyType.BULL_CALL_SPREAD, StrategyType.BEAR_PUT_SPREAD]:
    spread_width = abs(self.strikes[1] - self.strikes[0])
    net_credit = sum(
        -qty * price for qty, price in zip(self.quantities, self.entry_prices)
    )
    return max(spread_width * abs(self.quantities[0]) - abs(net_credit), abs(net_credit))

Why this is wrong

For a bull call spread (debit spread), max profit is always spread_width - net_debit. Example:

  • Spread width: $5.00 (500 cents), net debit paid: $4.00 (400 cents)
  • Correct max profit: $1.00 (100 cents)
  • Current formula: max(500 - 400, 400) = max(100, 400) = 400 cents (wrong by 4x)

Whenever the net debit is larger than half the spread width, the formula returns the net debit itself as max profit, which is the max loss of the position — the exact opposite of what it should return.

For well-priced spreads (debit < 50% of width) the formula coincidentally returns the right answer, which is why this hasn't been caught.

Secondary observation

The spread_width variable on line 280 in the calculate_max_loss() branch for BULL_CALL_SPREAD/BEAR_PUT_SPREAD is computed but never used. Max loss for a debit spread is correctly abs(net_debit), but the dead spread_width assignment suggests this code was written with the wrong formula in mind and then partially corrected.

Correct fix

elif self.strategy_type in [StrategyType.BULL_CALL_SPREAD, StrategyType.BEAR_PUT_SPREAD]:
    spread_width = abs(self.strikes[1] - self.strikes[0]) * abs(self.quantities[0])
    net_debit = abs(sum(
        qty * price for qty, price in zip(self.quantities, self.entry_prices)
    ))
    return spread_width - net_debit

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions