Option Basics - Calls, Puts, and Payoffs › Implementing a Payoff Calculator
Michael BrenndoerferNovember 15, 20254 min read
Quantitative FinanceAll chapters Part 3 of 15Forwards, Futures, and Options
Option Basics - Calls, Puts, and Payoffs › Implementing a Payoff CalculatorOne section · Sections in this chapter11 / 15 ↓
Sections in this chapter15
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Let's build a payoff calculator that handles any combination of options and stock positions. This tool will help you analyze strategies before putting capital at risk.
In[22]:
Codeimport numpy as np
class OptionPosition:
"""Represents a single option or stock position."""
def __init__(self, position_type, quantity, strike=None, premium=0):
"""
Initialize an option position.
Parameters:
-----------
position_type : str
'long_call', 'short_call', 'long_put', 'short_put', 'long_stock', 'short_stock'
quantity : float
Number of contracts (positive for long, but sign handled by position_type)
strike : float
Strike price (None for stock positions)
premium : float
Premium paid (positive) or received (negative handled by type)
"""
self.position_type = position_type
self.quantity = abs(quantity)
self.strike = strike
self.premium = premium
def payoff(self, S_T):
"""Calculate payoff at expiration price S_T."""
S_T = np.atleast_1d(S_T)
if self.position_type == "long_call":
return self.quantity * np.maximum(S_T - self.strike, 0)
elif self.position_type == "short_call":
return -self.quantity * np.maximum(S_T - self.strike, 0)
elif self.position_type == "long_put":
return self.quantity * np.maximum(self.strike - S_T, 0)
elif self.position_type == "short_put":
return -self.quantity * np.maximum(self.strike - S_T, 0)
elif self.position_type == "long_stock":
return self.quantity * S_T
elif self.position_type == "short_stock":
return -self.quantity * S_T
else:
raise ValueError(f"Unknown position type: {self.position_type}")
def profit(self, S_T):
"""Calculate profit at expiration price S_T."""
if "long" in self.position_type:
return self.payoff(S_T) - self.quantity * self.premium
else: # short position
return self.payoff(S_T) + self.quantity * self.premiumIn[23]:
Codedef analyze_strategy(positions, S_range):
"""
Analyze an option strategy across a range of expiration prices.
Parameters:
-----------
positions : list of OptionPosition
The positions in the strategy
S_range : array-like
Range of stock prices to analyze
Returns:
--------
dict with total_profit, max_profit, max_loss, breakevens
"""
S_range = np.array(S_range)
total_profit = np.zeros_like(S_range, dtype=float)
for pos in positions:
total_profit += pos.profit(S_range)
max_profit = np.max(total_profit)
max_loss = np.min(total_profit)
# Find breakeven points (where profit crosses zero)
sign_changes = np.where(np.diff(np.sign(total_profit)))[0]
breakevens = []
for idx in sign_changes:
# Linear interpolation to find exact breakeven
x1, x2 = S_range[idx], S_range[idx + 1]
y1, y2 = total_profit[idx], total_profit[idx + 1]
breakeven = x1 - y1 * (x2 - x1) / (y2 - y1)
breakevens.append(breakeven)
return {
"S_range": S_range,
"total_profit": total_profit,
"max_profit": max_profit,
"max_loss": max_loss,
"breakevens": breakevens,
}Let's use this calculator to analyze an iron condor, a popular income-generating strategy:
In[24]:
Codeimport numpy as np
# Iron Condor: Sell OTM call spread + Sell OTM put spread
# Profits if stock stays in a range
positions = [
OptionPosition("short_put", 1, strike=90, premium=2.0), # Sell 90 put
OptionPosition(
"long_put", 1, strike=85, premium=1.0
), # Buy 85 put (protection)
OptionPosition("short_call", 1, strike=110, premium=2.0), # Sell 110 call
OptionPosition(
"long_call", 1, strike=115, premium=1.0
), # Buy 115 call (protection)
]
S_range = np.linspace(70, 130, 500)
results = analyze_strategy(positions, S_range)
# Calculate initial credit (net premium received)
initial_credit = sum(
p.premium if "short" in p.position_type else -p.premium for p in positions
)Out[25]:
ConsoleIron Condor Analysis ================================================== Net credit received: $2.00 Maximum profit: $2.00 Maximum loss: $-3.00 Breakeven points: ['$88.00', '$112.00'] Profit zone: $88.00 to $112.00
Out[26]:
Visualization
The iron condor collects a net credit of $2.00 (the maximum profit) and risks a maximum loss of $3.00. It demonstrates several key option concepts:
- Defined risk: Maximum loss is capped by the long options (protection)
- Income generation: Profits from time decay if the stock stays in the range
- Probability trade-off: High probability of small profit vs. low probability of larger loss