Skip to content

fix(features): correct debit-spread max profit, width - debit (#16)#28

Open
bradsmithmba wants to merge 1 commit into
cloudtrainerwork:masterfrom
bradsmithmba:fix/debit-spread-max-profit
Open

fix(features): correct debit-spread max profit, width - debit (#16)#28
bradsmithmba wants to merge 1 commit into
cloudtrainerwork:masterfrom
bradsmithmba:fix/debit-spread-max-profit

Conversation

@bradsmithmba

Copy link
Copy Markdown

Summary

Position.calculate_max_profit() (src/features/position_models.py) computed the max profit of a BULL_CALL_SPREAD / BEAR_PUT_SPREAD as:

return max(spread_width * abs(self.quantities[0]) - abs(net_credit), abs(net_credit))

For a debit spread, max profit is always spread_width - net_debit. The max(..., debit) fallback returns the debit (the position's max loss) whenever the debit exceeds half the spread width — a value that can even exceed the spread width and is never a valid max profit.

Closes #16.

Fix

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

Verification

Exercised through the real calculate_max_profit():

Case width net debit old result new result correct
debit > half width 1000c 800c 800c (= max loss) 200c 200c
debit < half width 1000c 350c 650c 650c 650c

A regression test (test_debit_spread_max_profit_when_debit_exceeds_half_width) is added to tests/test_position_state.py using the 800c-debit case.

CI note

tests/test_position_state.py imports src.features.position_state, which transitively imports the data layer that is missing until #1 (PR #19) merges, so this test file cannot be collected on master yet. The fix itself was verified by invoking Position.calculate_max_profit() directly (the method has no such dependency). The new test will run in CI once #1 lands.

🤖 Generated with Claude Code

Position.calculate_max_profit() for BULL_CALL_SPREAD / BEAR_PUT_SPREAD used
max(spread_width - debit, debit). Whenever the net debit exceeded half the
spread width, this returned the debit itself — which is the position's max
loss, and can exceed the spread width — instead of the correct max profit.

Max profit for a debit spread is always spread_width - net_debit. Replace
the max() fallback with that formula and add a regression test using a
debit greater than half the width (1000c width, 800c debit -> 200c profit;
the old code returned 800c).

Verified via Position.calculate_max_profit(): bug case 200 (was 800),
normal case 650.

Closes cloudtrainerwork#16

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bradsmithmba

Copy link
Copy Markdown
Author

Merge-ordering note: the regression test added here lives in tests/test_position_state.py, which imports src.features.position_state → the data layer that is missing until #1 (PR #19) merges. On master today this test file fails at collection for that reason, not because of this change. Recommend merging #19 first; after that the new test runs and passes. The fix itself was verified by calling Position.calculate_max_profit() directly (see PR description).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant