
Options Strategy Advisor
- 1.2k installs
- 2.5k repo stars
- Updated July 26, 2026
- tradermonty/claude-trading-skills
options-strategy-advisor provides documented workflows for Options trading strategy analysis and simulation tool. Provides theoretical pricing using Black-Scholes model, Greeks calculation, strategy P/L simulation, and
About
The options-strategy-advisor skill options trading strategy analysis and simulation tool Provides theoretical pricing using Black-Scholes model Greeks calculation strategy P L simulation and risk management guidance Use when user requests options strategy analysis covered calls protective puts spreads iron condors earnings plays or options risk management Includes volatility analysis position sizing and earnings-based strategy recommendations Educational focus with practical trade simulation Options Strategy Advisor Overview This skill provides comprehensive options strategy analysis and education using theoretical pricing models It helps traders understand analyze and simulate options strategies without requiring real-time market data subscriptions Core Capabilities Black-Scholes Pricing Theoretical option prices and Greeks calculation Strategy Simulation P L analysis for major options strategies Earnings Strategies Pre-earnings volatility plays integrated with Earnings Calendar Risk Management Position sizing Greeks exposure max loss profit analysis Educational Focus Detailed explanations of strategies and risk metrics Data Sources FMP API Stock prices historical volatility divi.
- **Black-Scholes Pricing**: Theoretical option prices and Greeks calculation
- **Strategy Simulation**: P/L analysis for major options strategies
- **Earnings Strategies**: Pre-earnings volatility plays integrated with Earnings Calendar
- **Risk Management**: Position sizing, Greeks exposure, max loss/profit analysis
- **Educational Focus**: Detailed explanations of strategies and risk metrics
Options Strategy Advisor by the numbers
- 1,247 all-time installs (skills.sh)
- +51 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #187 of 1,048 Mobile Development skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
options-strategy-advisor capabilities & compatibility
- Capabilities
- **black scholes pricing**: theoretical option pr · **strategy simulation**: p/l analysis for major · **earnings strategies**: pre earnings volatility · **risk management**: position sizing, greeks exp · **educational focus**: detailed explanations of
- Use cases
- documentation
What options-strategy-advisor says it does
# Options Strategy Advisor ## Overview This skill provides comprehensive options strategy analysis and education using theoretical pricing models.
It helps traders understand, analyze, and simulate options strategies without requiring real-time market data subscriptions.
npx skills add https://github.com/tradermonty/claude-trading-skills --skill options-strategy-advisorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.2k |
|---|---|
| repo stars | ★ 2.5k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 26, 2026 |
| Repository | tradermonty/claude-trading-skills ↗ |
How do I use options-strategy-advisor for the task described in its SKILL.md triggers?
Options trading strategy analysis and simulation tool. Provides theoretical pricing using Black-Scholes model, Greeks calculation, strategy P/L simulation, and risk management guidance. Use when user.
Who is it for?
Teams invoking options-strategy-advisor when the user request matches documented triggers and prerequisites.
Skip if: Skip when cached docs are missing, the request is a negative trigger, or another sibling skill owns the workflow.
When should I use this skill?
Options trading strategy analysis and simulation tool. Provides theoretical pricing using Black-Scholes model, Greeks calculation, strategy P/L simulation, and risk management guidance. Use when user requests options str
What you get
Step-by-step guidance grounded in options-strategy-advisor documentation and reference files.
- Theoretical prices
- Greeks breakdown
- Strategy P/L simulation
By the numbers
- Supports 17+ options strategies
- Calculates five Greeks: Delta, Gamma, Theta, Vega, and Rho
Files
Options Strategy Advisor
Overview
This skill provides comprehensive options strategy analysis and education using theoretical pricing models. It helps traders understand, analyze, and simulate options strategies without requiring real-time market data subscriptions.
Core Capabilities:
- Black-Scholes Pricing: Theoretical option prices and Greeks calculation
- Strategy Simulation: P/L analysis for major options strategies
- Earnings Strategies: Pre-earnings volatility plays integrated with Earnings Calendar
- Risk Management: Position sizing, Greeks exposure, max loss/profit analysis
- Educational Focus: Detailed explanations of strategies and risk metrics
Data Sources:
- FMP API: Stock prices, historical volatility, dividends, earnings dates
- User Input: Implied volatility (IV), risk-free rate
- Theoretical Models: Black-Scholes for pricing and Greeks
Prerequisites
Required:
- Python 3.9+ with
numpy,scipy,requests
Optional:
- FMP API key (for real-time stock prices and historical volatility)
- Set via
FMP_API_KEYenvironment variable or--api-keyargument - Without API key: Use manual inputs for stock price and volatility
Installation:
pip install numpy scipy requestsQuick Start Examples:
# Basic call option pricing (no API key needed)
python3 scripts/black_scholes.py
# With FMP API key for real-time data
python3 scripts/black_scholes.py --ticker AAPL --api-key $FMP_API_KEY
# Custom option parameters
python3 scripts/black_scholes.py --stock-price 180 --strike 185 --days 30 --volatility 0.25
# Put option analysis
python3 scripts/black_scholes.py --stock-price 180 --strike 175 --days 30 --option-type putWhen to Use This Skill
Use this skill when:
- User asks about options strategies ("What's a covered call?", "How does an iron condor work?")
- User wants to simulate strategy P/L ("What's my max profit on a bull call spread?")
- User needs Greeks analysis ("What's my delta exposure?")
- User asks about earnings strategies ("Should I buy a straddle before earnings?")
- User wants to compare strategies ("Covered call vs protective put?")
- User needs position sizing guidance ("How many contracts should I trade?")
- User asks about volatility ("Is IV high right now?")
Example requests:
- "Analyze a covered call on AAPL"
- "What's the P/L on a $100/$105 bull call spread on MSFT?"
- "Should I trade a straddle before NVDA earnings?"
- "Calculate Greeks for my iron condor position"
- "Compare protective put vs covered call for downside protection"
Supported Strategies
Income Strategies
1. Covered Call - Own stock, sell call (generate income, cap upside) 2. Cash-Secured Put - Sell put with cash backing (collect premium, willing to buy stock) 3. Poor Man's Covered Call - LEAPS call + short near-term call (capital efficient)
Protection Strategies
4. Protective Put - Own stock, buy put (insurance, limited downside) 5. Collar - Own stock, sell call + buy put (limited upside/downside)
Directional Strategies
6. Bull Call Spread - Buy lower strike call, sell higher strike call (limited risk/reward bullish) 7. Bull Put Spread - Sell higher strike put, buy lower strike put (credit spread, bullish) 8. Bear Call Spread - Sell lower strike call, buy higher strike call (credit spread, bearish) 9. Bear Put Spread - Buy higher strike put, sell lower strike put (limited risk/reward bearish)
Volatility Strategies
10. Long Straddle - Buy ATM call + ATM put (profit from big move either direction) 11. Long Strangle - Buy OTM call + OTM put (cheaper than straddle, bigger move needed) 12. Short Straddle - Sell ATM call + ATM put (profit from no movement, unlimited risk) 13. Short Strangle - Sell OTM call + OTM put (profit from no movement, wider range)
Range-Bound Strategies
14. Iron Condor - Bull put spread + bear call spread (profit from range-bound movement) 15. Iron Butterfly - Sell ATM straddle, buy OTM strangle (profit from tight range)
Advanced Strategies
16. Calendar Spread - Sell near-term option, buy longer-term option (profit from time decay) 17. Diagonal Spread - Calendar spread with different strikes (directional + time decay) 18. Ratio Spread - Unbalanced spread (more contracts on one leg)
Analysis Workflow
Step 1: Gather Input Data
Required from User:
- Ticker symbol
- Strategy type
- Strike prices
- Expiration date(s)
- Position size (number of contracts)
Optional from User:
- Implied Volatility (IV) - if not provided, use Historical Volatility (HV)
- Risk-free rate - default to current 3-month T-bill rate (~5.3% as of 2025)
Fetched from FMP API:
- Current stock price
- Historical prices (for HV calculation)
- Dividend yield
- Upcoming earnings date (for earnings strategies)
Example User Input:
Ticker: AAPL
Strategy: Bull Call Spread
Long Strike: $180
Short Strike: $185
Expiration: 30 days
Contracts: 10
IV: 25% (or use HV if not provided)Step 2: Calculate Historical Volatility (if IV not provided)
Objective: Estimate volatility from historical price movements.
Method:
# Fetch 90 days of price data
prices = get_historical_prices("AAPL", days=90)
# Calculate daily returns
returns = np.log(prices / prices.shift(1))
# Annualized volatility
HV = returns.std() * np.sqrt(252) # 252 trading daysOutput:
- Historical Volatility (annualized percentage)
- Note to user: "HV = 24.5%, consider using current market IV for more accuracy"
User Can Override:
- Provide IV from broker platform (ThinkorSwim, TastyTrade, etc.)
- Script accepts
--iv 28.0parameter
Step 3: Price Options Using Black-Scholes
Black-Scholes Model:
For European-style options:
Call Price = S * N(d1) - K * e^(-r*T) * N(d2)
Put Price = K * e^(-r*T) * N(-d2) - S * N(-d1)
Where:
d1 = [ln(S/K) + (r + σ²/2) * T] / (σ * √T)
d2 = d1 - σ * √T
S = Current stock price
K = Strike price
r = Risk-free rate
T = Time to expiration (years)
σ = Volatility (IV or HV)
N() = Cumulative standard normal distributionAdjustments:
- Subtract present value of dividends from S for calls
- American options: Use approximation or note "European pricing, may undervalue American options"
Python Implementation:
from scipy.stats import norm
import numpy as np
def black_scholes_call(S, K, T, r, sigma, q=0):
"""
S: Stock price
K: Strike price
T: Time to expiration (years)
r: Risk-free rate
sigma: Volatility
q: Dividend yield
"""
d1 = (np.log(S/K) + (r - q + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
d2 = d1 - sigma*np.sqrt(T)
call_price = S*np.exp(-q*T)*norm.cdf(d1) - K*np.exp(-r*T)*norm.cdf(d2)
return call_price
def black_scholes_put(S, K, T, r, sigma, q=0):
d1 = (np.log(S/K) + (r - q + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
d2 = d1 - sigma*np.sqrt(T)
put_price = K*np.exp(-r*T)*norm.cdf(-d2) - S*np.exp(-q*T)*norm.cdf(-d1)
return put_priceOutput for Each Option Leg:
- Theoretical price
- Note: "Market price may differ due to bid-ask spread and American vs European pricing"
Step 4: Calculate Greeks
The Greeks measure option price sensitivity to various factors:
Delta (Δ): Change in option price per $1 change in stock price
def delta_call(S, K, T, r, sigma, q=0):
d1 = (np.log(S/K) + (r - q + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
return np.exp(-q*T) * norm.cdf(d1)
def delta_put(S, K, T, r, sigma, q=0):
d1 = (np.log(S/K) + (r - q + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
return np.exp(-q*T) * (norm.cdf(d1) - 1)Gamma (Γ): Change in delta per $1 change in stock price
def gamma(S, K, T, r, sigma, q=0):
d1 = (np.log(S/K) + (r - q + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
return np.exp(-q*T) * norm.pdf(d1) / (S * sigma * np.sqrt(T))Theta (Θ): Change in option price per day (time decay)
def theta_call(S, K, T, r, sigma, q=0):
d1 = (np.log(S/K) + (r - q + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
d2 = d1 - sigma*np.sqrt(T)
theta = (-S*norm.pdf(d1)*sigma*np.exp(-q*T)/(2*np.sqrt(T))
- r*K*np.exp(-r*T)*norm.cdf(d2)
+ q*S*norm.cdf(d1)*np.exp(-q*T))
return theta / 365 # Per dayVega (ν): Change in option price per 1% change in volatility
def vega(S, K, T, r, sigma, q=0):
d1 = (np.log(S/K) + (r - q + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
return S * np.exp(-q*T) * norm.pdf(d1) * np.sqrt(T) / 100 # Per 1%Rho (ρ): Change in option price per 1% change in interest rate
def rho_call(S, K, T, r, sigma, q=0):
d2 = (np.log(S/K) + (r - q + 0.5*sigma**2)*T) / (sigma*np.sqrt(T)) - sigma*np.sqrt(T)
return K * T * np.exp(-r*T) * norm.cdf(d2) / 100 # Per 1%Position Greeks:
For a strategy with multiple legs, sum Greeks across all legs:
# Example: Bull Call Spread
# Long 1x $180 call
# Short 1x $185 call
delta_position = (1 * delta_long) + (-1 * delta_short)
gamma_position = (1 * gamma_long) + (-1 * gamma_short)
theta_position = (1 * theta_long) + (-1 * theta_short)
vega_position = (1 * vega_long) + (-1 * vega_short)Greeks Interpretation:
| Greek | Meaning | Example |
|---|---|---|
| Delta | Directional exposure | Δ = 0.50 → $50 profit if stock +$1 |
| Gamma | Delta acceleration | Γ = 0.05 → Delta increases by 0.05 if stock +$1 |
| Theta | Daily time decay | Θ = -$5 → Lose $5/day from time passing |
| Vega | Volatility sensitivity | ν = $10 → Gain $10 if IV increases 1% |
| Rho | Interest rate sensitivity | ρ = $2 → Gain $2 if rates increase 1% |
Step 5: Simulate Strategy P/L
Objective: Calculate profit/loss at various stock prices at expiration.
Method:
Generate stock price range (e.g., ±30% from current price):
current_price = 180
price_range = np.linspace(current_price * 0.7, current_price * 1.3, 100)For each price point, calculate P/L:
def calculate_pnl(strategy, stock_price_at_expiration):
pnl = 0
for leg in strategy.legs:
if leg.type == 'call':
intrinsic_value = max(0, stock_price_at_expiration - leg.strike)
else: # put
intrinsic_value = max(0, leg.strike - stock_price_at_expiration)
if leg.position == 'long':
pnl += (intrinsic_value - leg.premium_paid) * 100 # Per contract
else: # short
pnl += (leg.premium_received - intrinsic_value) * 100
return pnl * num_contractsKey Metrics:
- Max Profit: Highest possible P/L
- Max Loss: Worst possible P/L
- Breakeven Point(s): Stock price(s) where P/L = 0
- Profit Probability: Percentage of price range that's profitable (simplified)
Example Output:
Bull Call Spread: $180/$185 on AAPL (30 DTE, 10 contracts)
Current Price: $180.00
Net Debit: $2.50 per spread ($2,500 total)
Max Profit: $2,500 (at $185+)
Max Loss: -$2,500 (at $180-)
Breakeven: $182.50
Risk/Reward: 1:1
Probability Profit: ~55% (if stock stays above $182.50)Step 6: Generate P/L Diagram (ASCII Art)
Visual representation of P/L across stock prices:
def generate_pnl_diagram(price_range, pnl_values, current_price, width=60, height=15):
"""Generate ASCII P/L diagram"""
# Normalize to chart dimensions
max_pnl = max(pnl_values)
min_pnl = min(pnl_values)
lines = []
lines.append(f"\nP/L Diagram: {strategy_name}")
lines.append("-" * width)
# Y-axis levels
levels = np.linspace(max_pnl, min_pnl, height)
for level in levels:
if abs(level) < (max_pnl - min_pnl) * 0.05:
label = f" 0 |" # Zero line
else:
label = f"{level:6.0f} |"
row = label
for i in range(width - len(label)):
idx = int(i / (width - len(label)) * len(price_range))
pnl = pnl_values[idx]
price = price_range[idx]
# Determine character
if abs(pnl - level) < (max_pnl - min_pnl) / height:
if pnl > 0:
char = '█' # Profit
elif pnl < 0:
char = '░' # Loss
else:
char = '─' # Breakeven
elif abs(level) < (max_pnl - min_pnl) * 0.05:
char = '─' # Zero line
elif abs(price - current_price) < (price_range[-1] - price_range[0]) * 0.02:
char = '│' # Current price line
else:
char = ' '
row += char
lines.append(row)
lines.append(" " * 6 + "|" + "-" * (width - 6))
lines.append(" " * 6 + f"${price_range[0]:.0f}" + " " * (width - 20) + f"${price_range[-1]:.0f}")
lines.append(" " * (width // 2 - 5) + "Stock Price")
return "\n".join(lines)Example Output:
P/L Diagram: Bull Call Spread $180/$185
------------------------------------------------------------
+2500 | ████████████████████
| ██████
| ██████
| ██████
0 | ──────
| ░░░░░░
|░░░░░░
-2500 |░░░░░
|____________________________________________________________
$126 $180 $234
Stock Price
Legend: █ Profit ░ Loss ── Breakeven │ Current PriceStep 7: Strategy-Specific Analysis
Provide tailored guidance based on strategy type:
Covered Call:
Income Strategy: Generate premium while capping upside
Setup:
- Own 100 shares of AAPL @ $180
- Sell 1x $185 call (30 DTE) for $3.50
Max Profit: $850 (Stock at $185+ = $5 stock gain + $3.50 premium)
Max Loss: Unlimited downside (stock ownership)
Breakeven: $176.50 (Cost basis - premium received)
Greeks:
- Delta: -0.30 (reduces stock delta from 1.00 to 0.70)
- Theta: +$8/day (time decay benefit)
Assignment Risk: If AAPL > $185 at expiration, shares called away
When to Use:
- Neutral to slightly bullish
- Want income in sideways market
- Willing to sell stock at $185
Exit Plan:
- Buy back call if stock rallies strongly (preserve upside)
- Let expire if stock stays below $185
- Roll to next month if want to keep sharesProtective Put:
Insurance Strategy: Limit downside while keeping upside
Setup:
- Own 100 shares of AAPL @ $180
- Buy 1x $175 put (30 DTE) for $2.00
Max Profit: Unlimited (stock can rise infinitely)
Max Loss: -$7 per share = ($5 stock loss + $2 premium)
Breakeven: $182 (Cost basis + premium paid)
Greeks:
- Delta: +0.80 (stock delta 1.00 - put delta 0.20)
- Theta: -$6/day (time decay cost)
Protection: Guaranteed to sell at $175, no matter how far stock falls
When to Use:
- Own stock, worried about short-term drop
- Earnings coming up, want protection
- Alternative to stop-loss (can't be stopped out)
Cost: "Insurance premium" - typically 1-3% of stock value
Exit Plan:
- Let expire worthless if stock rises (cost of insurance)
- Exercise put if stock falls below $175
- Sell put if stock drops but want to keep sharesIron Condor:
Range-Bound Strategy: Profit from low volatility
Setup (example on AAPL @ $180):
- Sell $175 put for $1.50
- Buy $170 put for $0.50
- Sell $185 call for $1.50
- Buy $190 call for $0.50
Net Credit: $2.00 ($200 per iron condor)
Max Profit: $200 (if stock stays between $175-$185)
Max Loss: $300 (if stock moves outside $170-$190)
Breakevens: $173 and $187
Profit Range: $175 to $185 (58% probability)
Greeks:
- Delta: ~0 (market neutral)
- Theta: +$15/day (time decay benefit)
- Vega: -$25 (short volatility)
When to Use:
- Expect low volatility, range-bound movement
- After big move, think consolidation
- High IV environment (sell expensive options)
Risk: Unlimited if one side tested
- Use stop loss at 2x credit received (exit at -$400)
Adjustments:
- If tested on one side, roll that side out in time
- Close early at 50% max profit to reduce tail riskStep 8: Earnings Strategy Analysis
Integration with Earnings Calendar:
When user asks about earnings strategies, fetch earnings date:
from earnings_calendar import get_next_earnings_date
earnings_date = get_next_earnings_date("AAPL")
days_to_earnings = (earnings_date - today).daysPre-Earnings Strategies:
Long Straddle/Strangle:
Setup (AAPL @ $180, earnings in 7 days):
- Buy $180 call for $5.00
- Buy $180 put for $4.50
- Total Cost: $9.50
Thesis: Expect big move (>5%) but unsure of direction
Breakevens: $170.50 and $189.50
Profit if: Stock moves >$9.50 in either direction
Greeks:
- Delta: ~0 (neutral)
- Vega: +$50 (long volatility)
- Theta: -$25/day (time decay hurts)
IV Crush Risk: ⚠️ CRITICAL
- Pre-earnings IV: 40% (elevated)
- Post-earnings IV: 25% (typical)
- IV drop: -15 points = -$750 loss even if stock doesn't move!
Analysis:
- Implied Move: √(DTE/365) × IV × Stock Price
= √(7/365) × 0.40 × 180 = ±$10.50
- Breakeven Move Needed: ±$9.50
- Probability Profit: ~30-40% (implied move > breakeven move)
Recommendation:
✅ Consider if you expect >10% move (larger than implied)
❌ Avoid if expect normal ~5% earnings move (IV crush will hurt)
Alternative: Buy further OTM strikes to reduce cost
- $175/$185 strangle cost $4.00 (need >$8 move, but cheaper)Short Iron Condor:
Setup (AAPL @ $180, earnings in 7 days):
- Sell $170/$175 put spread for $2.00
- Sell $185/$190 call spread for $2.00
- Net Credit: $4.00
Thesis: Expect stock to stay range-bound ($175-$185)
Profit Zone: $175 to $185
Max Profit: $400
Max Loss: $100
IV Crush Benefit: ✅
- Short high IV before earnings
- IV drops after earnings → profit on vega
- Even if stock moves slightly, IV drop helps
Greeks:
- Delta: ~0 (market neutral)
- Vega: -$40 (short volatility - good here!)
- Theta: +$20/day
Recommendation:
✅ Good if expect normal earnings reaction (<8% move)
✅ Benefit from IV crush regardless of direction
⚠️ Risk if stock gaps outside range (>10% move)
Exit Plan:
- Close next day if IV crushed (capture profit early)
- Use stop loss if one side tested (-2x credit)Step 9: Risk Management Guidance
Position Sizing:
Account Size: $50,000
Risk Tolerance: 2% per trade = $1,000 max risk
Iron Condor Example:
- Max loss per spread: $300
- Max contracts: $1,000 / $300 = 3 contracts
- Actual position: 3 iron condors
Bull Call Spread Example:
- Debit paid: $2.50 per spread
- Max contracts: $1,000 / $250 = 4 contracts
- Actual position: 4 spreadsPortfolio Greeks Management:
Portfolio Guidelines:
- Delta: -10 to +10 (mostly neutral)
- Theta: Positive preferred (seller advantage)
- Vega: Monitor if >$500 (IV risk)
Current Portfolio:
- Delta: +5 (slightly bullish)
- Theta: +$150/day (collecting $150 daily)
- Vega: -$300 (short volatility)
Interpretation:
✅ Neutral delta (safe)
✅ Positive theta (time working for you)
⚠️ Short vega: If IV spikes, lose $300 per 1% IV increase
→ Reduce short premium positions if VIX risingAdjustments and Exits:
Exit Rules by Strategy:
Covered Call:
- Profit: 50-75% of max profit
- Loss: Stock drops >5%, buy back call to preserve upside
- Time: 7-10 DTE, roll to avoid assignment
Spreads:
- Profit: 50% of max profit (close early, reduce tail risk)
- Loss: 2x debit paid (cut losses early)
- Time: 21 DTE, close or roll (avoid gamma risk)
Iron Condor:
- Profit: 50% of credit (close early common)
- Loss: One side tested, 2x credit lost
- Adjustment: Roll tested side out in time
Straddle/Strangle:
- Profit: Stock moved >breakeven, close immediately
- Loss: Theta eating position, stock not moving
- Time: Day after earnings (if earnings play)Output Format
Strategy Analysis Report Template:
# Options Strategy Analysis: [Strategy Name]
**Symbol:** [TICKER]
**Strategy:** [Strategy Type]
**Expiration:** [Date] ([DTE] days)
**Contracts:** [Number]
---
## Strategy Setup
### Leg Details
| Leg | Type | Strike | Price | Position | Quantity |
|-----|------|--------|-------|----------|----------|
| 1 | Call | $180 | $5.00 | Long | 1 |
| 2 | Call | $185 | $2.50 | Short | 1 |
**Net Debit/Credit:** $2.50 debit ($250 total for 1 spread)
---
## Profit/Loss Analysis
**Max Profit:** $250 (at $185+)
**Max Loss:** -$250 (at $180-)
**Breakeven:** $182.50
**Risk/Reward Ratio:** 1:1
**Probability Analysis:**
- Probability of Profit: ~55% (stock above $182.50)
- Expected Value: $25 (simplified)
---
## P/L Diagram
[ASCII art diagram here]
---
## Greeks Analysis
### Position Greeks (1 spread)
- **Delta:** +0.20 (gains $20 if stock +$1)
- **Gamma:** +0.03 (delta increases by 0.03 if stock +$1)
- **Theta:** -$5/day (loses $5 per day from time decay)
- **Vega:** +$8 (gains $8 if IV increases 1%)
### Interpretation
- **Directional Bias:** Slightly bullish (positive delta)
- **Time Decay:** Working against you (negative theta)
- **Volatility:** Benefits from IV increase (positive vega)
---
## Risk Assessment
### Maximum Risk
**Scenario:** Stock falls below $180
**Max Loss:** -$250 (100% of premium paid)
**% of Account:** 0.5% (if $50k account)
### Assignment Risk
**Early Assignment:** Low (calls have time value)
**At Expiration:** Manage positions if in-the-money
---
## Trade Management
### Entry
✅ Enter if: [Conditions]
- Stock price $178-$182
- IV below 30%
- >21 DTE
### Profit Taking
- **Target 1:** 50% profit ($125) - Close half
- **Target 2:** 75% profit ($187.50) - Close all
### Stop Loss
- **Trigger:** Stock falls below $177 (-$150 loss)
- **Action:** Close position immediately
### Adjustments
- If stock rallies to $184, consider rolling short call higher
- If stock drops to $179, add second spread at $175/$180
---
## Suitability
### When to Use This Strategy
✅ Moderately bullish on AAPL
✅ Expect upside to $185-$190
✅ Want defined risk
✅ 21-45 DTE timeframe
### When to Avoid
❌ Very bullish (buy stock or long call instead)
❌ High IV environment (wait for IV to drop)
❌ Earnings in <7 days (IV crush risk)
---
## Alternatives Comparison
| Strategy | Max Profit | Max Loss | Complexity | When Better |
|----------|-----------|----------|------------|-------------|
| Bull Call Spread | $250 | -$250 | Medium | Moderately bullish |
| Long Call | Unlimited | -$500 | Low | Very bullish |
| Covered Call | $850 | Unlimited | Medium | Own stock already |
| Bull Put Spread | $300 | -$200 | Medium | Want credit spread |
**Recommendation:** Bull call spread is good balance of risk/reward for moderate bullish thesis.
---
*Disclaimer: This is theoretical analysis using Black-Scholes pricing. Actual market prices may differ. Trade at your own risk. Options are complex instruments with significant loss potential.*File Naming Convention:
options_analysis_[TICKER]_[STRATEGY]_[DATE].mdExample: options_analysis_AAPL_BullCallSpread_2025-11-08.md
Key Principles
Theoretical Pricing Limitations
What Users Should Know: 1. Black-Scholes Assumptions:
- European-style options (can't exercise early)
- Constant volatility (IV changes in reality)
- No transaction costs
- Continuous trading
2. Real vs Theoretical:
- Bid-ask spread: Actual cost higher than theoretical
- American options: Can be exercised early (especially ITM puts)
- Liquidity: Wide markets on illiquid options
- Dividends: Ex-dividend dates affect pricing
3. Best Practices:
- Use as educational tool and comparative analysis
- Get real quotes from broker before trading
- Understand theoretical price ≈ mid-market price
- Account for commissions and slippage
Volatility Guidance
Historical vs Implied Volatility:
Historical Volatility (HV): What happened
- Calculated from past price movements
- Objective, based on data
- Available for free (FMP API)
Implied Volatility (IV): What market expects
- Derived from option prices
- Subjective, based on supply/demand
- Requires live options data (user provides)
Comparison:
- IV > HV: Options expensive (consider selling)
- IV < HV: Options cheap (consider buying)
- IV = HV: Fairly pricedIV Percentile:
User provides current IV, we calculate percentile:
# Fetch 1-year HV data
historical_hvs = calculate_hv_series(prices_1yr, window=30)
# Calculate IV percentile
iv_percentile = percentileofscore(historical_hvs, current_iv)
if iv_percentile > 75:
guidance = "High IV - consider selling premium (credit spreads, iron condors)"
elif iv_percentile < 25:
guidance = "Low IV - consider buying options (long calls/puts, debit spreads)"
else:
guidance = "Normal IV - any strategy appropriate"Integration with Other Skills
Earnings Calendar:
- Fetch earnings dates automatically
- Suggest earnings-specific strategies
- Calculate days to earnings (DTE critical for IV)
- Warn about IV crush risk
Technical Analyst:
- Use support/resistance for strike selection
- Trend analysis for directional strategies
- Breakout potential for straddle/strangle timing
US Stock Analysis:
- Fundamental analysis for longer-term strategies (LEAPS)
- Dividend yield for covered call/put analysis
- Earnings quality for earnings plays
Bubble Detector:
- High bubble risk → focus on protective puts
- Low risk → bullish strategies
- Critical risk → avoid long premium (theta hurts)
Portfolio Manager:
- Track options positions alongside stock positions
- Aggregate Greeks across portfolio
- Options as hedging tool for stock positions
Important Notes
- All analysis in English
- Educational focus: Strategies explained clearly
- Theoretical pricing: Black-Scholes approximation
- User IV input: Optional, defaults to HV
- No real-time data required: FMP Free tier sufficient
- Dependencies: Python 3.9+, numpy, scipy, requests
Common Use Cases
Use Case 1: Learn Strategy
User: "Explain a covered call"
Workflow:
1. Load strategy reference (references/strategies_guide.md)
2. Explain concept, risk/reward, when to use
3. Simulate example on AAPL
4. Show P/L diagram
5. Compare to alternativesUse Case 2: Analyze Specific Trade
User: "Analyze $180/$185 bull call spread on AAPL, 30 days"
Workflow:
1. Fetch AAPL price from FMP
2. Calculate HV or ask user for IV
3. Price both options (Black-Scholes)
4. Calculate Greeks
5. Simulate P/L
6. Generate analysis reportUse Case 3: Earnings Strategy
User: "Should I trade options before NVDA earnings?"
Workflow:
1. Fetch NVDA earnings date (Earnings Calendar)
2. Calculate days to earnings
3. Estimate IV percentile (if user provides IV)
4. Suggest straddle/strangle vs iron condor
5. Warn about IV crush
6. Simulate both strategiesUse Case 4: Portfolio Greeks Check
User: "What are my total portfolio Greeks?"
Workflow:
1. User provides current positions
2. Calculate Greeks for each position
3. Sum Greeks across portfolio
4. Assess overall exposure
5. Suggest adjustments if neededTroubleshooting
Problem: IV not available
- Solution: Use HV as proxy, note to user
- Ask user to provide IV from broker platform
Problem: Negative option price
- Solution: Check inputs (strike vs stock price)
- Deep ITM options may have numerical issues
Problem: Greeks seem wrong
- Solution: Verify inputs (T, sigma, r)
- Check if using annual vs daily values
Problem: Strategy too complex
- Solution: Break into legs, analyze separately
- Refer to references for strategy details
Resources
References:
references/black_scholes_methodology.md- Black-Scholes formulas, Greeks, and interpretationreferences/strategies_guide.md- All 17+ strategies explained (future)references/greeks_explained.md- Greeks deep dive (future)references/volatility_guide.md- HV vs IV, when to trade (future)
Scripts:
scripts/black_scholes.py- Pricing engine and Greeksscripts/strategy_analyzer.py- Strategy simulationscripts/earnings_strategy.py- Earnings-specific analysis
External Resources:
- Options Playbook: https://www.optionsplaybook.com/
- CBOE Education: https://www.cboe.com/education/
- Black-Scholes Calculator: Various online tools for verification
---
Version: 1.0 Last Updated: 2025-11-08 Dependencies: Python 3.9+, numpy, scipy, requests API: FMP API (Free tier sufficient)
Options Strategy Advisor
Educational options trading tool providing theoretical pricing, strategy analysis, and risk management guidance using Black-Scholes model.
Overview
Options Strategy Advisor helps traders understand and analyze options strategies without requiring expensive real-time options data. It uses theoretical pricing models (Black-Scholes) combined with free stock market data (FMP API) to simulate strategies and calculate Greeks.
Key Features:
- ✅ Black-Scholes pricing engine
- ✅ All Greeks calculation (Delta, Gamma, Theta, Vega, Rho)
- ✅ 17+ options strategies supported
- ✅ P/L simulation and visualization
- ✅ Earnings strategy integration
- ✅ Historical volatility calculation
- ✅ Risk management guidance
Why This Approach?
No expensive data subscriptions needed:
- Real-time options data: $99-$500/month (Polygon.io, Intrinio)
- FMP API Free tier: $0/month (250 requests/day)
Educational focus:
- Learn how strategies work
- Understand Greeks and risk metrics
- Compare strategies side-by-side
Practical application:
- Theoretical prices ≈ market mid-prices
- User can input actual IV from broker
- Good for strategy planning and education
Supported Strategies
Income Strategies
1. Covered Call - Generate income from stock holdings 2. Cash-Secured Put - Get paid to buy stock 3. Poor Man's Covered Call - Capital-efficient covered call
Protection Strategies
4. Protective Put - Insurance for stock positions 5. Collar - Limited risk/reward protection
Directional Strategies
6. Bull Call Spread - Limited risk bullish play 7. Bull Put Spread - Credit spread for bullish view 8. Bear Call Spread - Credit spread for bearish view 9. Bear Put Spread - Limited risk bearish play
Volatility Strategies
10. Long Straddle - Profit from big moves 11. Long Strangle - Cheaper straddle, bigger move needed 12. Short Straddle - Profit from no movement (high risk) 13. Short Strangle - Wider range straddle
Range-Bound Strategies
14. Iron Condor - Profit from range-bound trading 15. Iron Butterfly - Tight range profit
Advanced Strategies
16. Calendar Spread - Time decay play 17. Diagonal Spread - Directional + time decay
Installation
Prerequisites
- Python 3.8+
- FMP API key (free tier sufficient)
Install Dependencies
pip install numpy scipy requests pandasGet FMP API Key
1. Visit https://financialmodelingprep.com/developer/docs 2. Sign up for free account 3. Copy API key 4. Set environment variable:
export FMP_API_KEY="your_key_here"Quick Start
Test Black-Scholes Pricer
python scripts/black_scholes.pyExample Output:
BLACK-SCHOLES OPTIONS PRICER - EXAMPLE
======================================================================
Input Parameters:
Stock Price: $180.00
Strike Price: $185.00
Days to Expiration: 30
Volatility: 25.0%
Risk-Free Rate: 5.30%
Dividend Yield: 1.0%
======================================================================
CALL OPTION
======================================================================
Price: $2.45
Intrinsic Value: $0.00
Time Value: $2.45
Greeks:
Delta: 0.3654 ($36.54 per $1 move)
Gamma: 0.0234 (delta changes by 0.0234)
Theta: -$0.18/day (loses $0.18 per day)
Vega: $0.25 per 1% IV (gains $0.25 if IV +1%)
Rho: $0.12 per 1% rate (gains $0.12 if rate +1%)Use in Your Code
from scripts.black_scholes import OptionPricer
# Initialize pricer
pricer = OptionPricer(
S=180, # Stock price
K=185, # Strike price
T=30/365, # Time to expiration (years)
r=0.053, # Risk-free rate (5.3%)
sigma=0.25, # Volatility (25%)
q=0.01 # Dividend yield (1%)
)
# Get call option price
call_price = pricer.call_price()
print(f"Call Price: ${call_price:.2f}")
# Get all Greeks for call
call_greeks = pricer.get_all_greeks('call')
print(f"Delta: {call_greeks['delta']:.4f}")
print(f"Gamma: {call_greeks['gamma']:.4f}")
print(f"Theta: ${call_greeks['theta']:.2f}/day")
print(f"Vega: ${call_greeks['vega']:.2f} per 1%")Calculate Historical Volatility
from scripts.black_scholes import (
calculate_historical_volatility,
fetch_historical_prices_for_hv
)
# Fetch prices from FMP
api_key = "your_key"
prices = fetch_historical_prices_for_hv("AAPL", api_key, days=90)
# Calculate 30-day HV
hv = calculate_historical_volatility(prices, window=30)
print(f"30-Day HV: {hv*100:.2f}%")Understanding the Metrics
Option Price Components
Intrinsic Value:
- Call: max(0, Stock Price - Strike Price)
- Put: max(0, Strike Price - Stock Price)
Time Value:
- Option Price - Intrinsic Value
- Decays to $0 at expiration
The Greeks
Delta (Δ) - Directional Exposure
Range: 0 to 1 (calls), -1 to 0 (puts)
Meaning: Change in option price per $1 stock move
Example: Δ = 0.50
→ If stock +$1, option +$0.50
→ If stock -$1, option -$0.50Gamma (Γ) - Delta Acceleration
Meaning: Change in delta per $1 stock move
Peak: ATM options
Low: Deep ITM or OTM
Example: Γ = 0.05, Δ currently = 0.50
→ If stock +$1, delta becomes 0.55Theta (Θ) - Time Decay
Meaning: Change in option price per day
Sign: Usually negative (options lose value over time)
Peak: Last 30 days before expiration
Example: Θ = -$0.15/day
→ Tomorrow, option loses $0.15 if nothing else changesVega (ν) - Volatility Sensitivity
Meaning: Change in option price per 1% IV change
Sign: Always positive (options gain value when vol increases)
Example: ν = $0.25 per 1%
→ If IV increases from 25% to 26%, option +$0.25Rho (ρ) - Interest Rate Sensitivity
Meaning: Change in option price per 1% rate change
Sign: Positive for calls, negative for puts
Impact: Usually small unless long-dated options
Example: ρ = $0.10 per 1%
→ If interest rate increases 1%, option +$0.10Volatility: HV vs IV
Historical Volatility (HV):
- Calculated from past price movements
- Objective, based on actual data
- Available free (from price data)
Implied Volatility (IV):
- Derived from option market prices
- Subjective, based on supply/demand
- Requires real-time options data (or user input)
Comparison:
IV > HV: Options expensive → Consider selling premium
IV < HV: Options cheap → Consider buying options
IV = HV: Fairly priced → Any strategy worksCommon Workflows
1. Analyze a Strategy
from scripts.black_scholes import OptionPricer
# Stock: AAPL @ $180
# Strategy: Bull Call Spread $180/$185 (30 DTE)
# Price long call ($180 strike)
long_call = OptionPricer(S=180, K=180, T=30/365, r=0.053, sigma=0.25)
long_price = long_call.call_price()
long_delta = long_call.call_delta()
# Price short call ($185 strike)
short_call = OptionPricer(S=180, K=185, T=30/365, r=0.053, sigma=0.25)
short_price = short_call.call_price()
short_delta = short_call.call_delta()
# Strategy metrics
net_debit = long_price - short_price
max_profit = (185 - 180) - net_debit
max_loss = -net_debit
position_delta = long_delta - short_delta
print(f"Bull Call Spread $180/$185")
print(f"Net Debit: ${net_debit:.2f}")
print(f"Max Profit: ${max_profit:.2f} (at $185+)")
print(f"Max Loss: ${max_loss:.2f} (at $180-)")
print(f"Position Delta: {position_delta:.4f}")2. Compare IV to HV
# Get HV
prices = fetch_historical_prices_for_hv("AAPL", api_key, days=90)
hv = calculate_historical_volatility(prices, window=30)
# User provides IV (from broker platform)
iv = 0.28 # 28% from ThinkorSwim
print(f"Historical Volatility: {hv*100:.2f}%")
print(f"Implied Volatility: {iv*100:.1f}%")
if iv > hv * 1.1:
print("→ Options expensive (IV > HV) - Consider selling premium")
elif iv < hv * 0.9:
print("→ Options cheap (IV < HV) - Consider buying options")
else:
print("→ Fairly priced")3. Earnings Strategy
Check if earnings coming up (use Earnings Calendar skill):
# If earnings in 7 days:
# - IV typically elevated (30-50% higher)
# - Consider straddle/strangle (profit from big move)
# - Or sell iron condor (profit from IV crush)
# Example: Long Straddle
straddle_cost = call_price + put_price
breakeven_up = stock_price + straddle_cost
breakeven_down = stock_price - straddle_cost
print(f"Straddle Cost: ${straddle_cost:.2f}")
print(f"Breakevens: ${breakeven_down:.2f} / ${breakeven_up:.2f}")
print(f"Need {abs(breakeven_up - stock_price)/stock_price*100:.1f}% move to profit")Limitations & Best Practices
Theoretical vs Market Prices
Black-Scholes Assumptions:
- European options (can't exercise early)
- Constant volatility (changes in reality)
- No transaction costs
- Continuous trading
Real World Differences:
- American options (most stocks) can exercise early
- Bid-ask spread: Actual cost higher than theoretical mid
- Commissions and slippage
- Liquidity: Wide markets on illiquid options
Best Practices
1. Use for Education & Planning:
- Learn how strategies work
- Compare different approaches
- Understand risk/reward before trading
2. Verify Before Trading:
- Get real quotes from your broker
- Check bid-ask spread
- Confirm option liquidity (open interest, volume)
3. Input Actual IV:
- Theoretical price assumes constant volatility
- Use current market IV for accuracy
- Check IV percentile (high/low relative to history)
4. Account for Dividends:
- Ex-dividend dates affect option prices
- Calls lose value, puts gain value on ex-div date
- Script supports dividend yield input
5. Monitor Greeks:
- Delta: Overall directional exposure
- Theta: Daily time decay (seller advantage)
- Vega: Volatility risk (watch during earnings)
- Gamma: Risk of delta changing (avoid near expiration)
Integration with Other Skills
Earnings Calendar:
- Fetch earnings dates
- Identify IV crush opportunities
- Time earnings strategies
Technical Analyst:
- Use support/resistance for strike selection
- Trend analysis for directional strategies
- Breakout potential for straddle timing
US Stock Analysis:
- Fundamental analysis for LEAPS
- Dividend yield for covered call/put
- Earnings quality for earnings plays
Bubble Detector:
- High risk → protective puts
- Low risk → bullish strategies
- Critical risk → avoid long premium
Portfolio Manager:
- Track options with stock positions
- Aggregate Greeks across portfolio
- Options as hedging tool
API Usage & Costs
Free Tier Sufficient:
- Stock prices: 1 request per symbol
- Historical prices (HV): 1 request per symbol
- Dividend data: 1 request per symbol
Example Analysis Cost:
Covered Call on AAPL:
- Current price: 1 request
- HV calculation: 1 request (90 days data)
- Dividend yield: 1 request
Total: 3 requests
Daily budget: 250 requests / day
→ Can analyze ~80 strategies per dayTroubleshooting
Negative Option Price
Cause: Invalid inputs (strike vs stock price) Solution: Check that inputs make sense
Greeks Seem Wrong
Cause: Using wrong units (annual vs daily) Solution: Verify T is in years, theta is per day
HV Very Different from IV
Normal: IV reflects future expectations, HV is past Action: Use IV from broker for more accuracy
Option Price Too High/Low
Cause: Volatility input incorrect Solution: Verify sigma is annual (e.g., 0.25 for 25%)
Resources
Documentation
SKILL.md- Complete workflow and strategiesreferences/strategies_guide.md- All strategies explained (TBD)references/greeks_explained.md- Greeks deep dive (TBD)
External Resources
- Options Playbook: https://www.optionsplaybook.com/
- CBOE Education: https://www.cboe.com/education/
- Black-Scholes Calculator: https://www.option-price.com/
Get Real IV
- ThinkorSwim (TD Ameritrade): Free
- TastyTrade: Free
- Barchart: https://www.barchart.com/options
- CBOE: http://www.cboe.com/delayedquote/
Future Enhancements
Planned:
- Strategy simulation script (complete P/L analysis)
- P/L diagram generator (ASCII art)
- Earnings strategy advisor (integrated with Earnings Calendar)
- Complete strategy reference guides
Contributions Welcome:
- Additional strategies
- Improved volatility models
- Better visualization tools
License
Educational use. Trade at your own risk. Options involve significant risk and are not suitable for all investors.
---
Version: 1.0 Last Updated: 2025-11-08 Dependencies: Python 3.8+, numpy, scipy, requests API: FMP API (Free tier sufficient) Model: Black-Scholes (European options pricing)
Black-Scholes Methodology Reference
Overview
The Black-Scholes model is the foundation for theoretical options pricing in this skill. This document provides the mathematical framework and interpretation guidance.
Black-Scholes Formula
Call Option Price
C = S × e^(-qT) × N(d₁) - K × e^(-rT) × N(d₂)Put Option Price
P = K × e^(-rT) × N(-d₂) - S × e^(-qT) × N(-d₁)Parameters
| Symbol | Description | Typical Range |
|---|---|---|
| S | Current stock price | Market price |
| K | Strike price | User selected |
| T | Time to expiration (years) | 0.01 - 2.0 |
| r | Risk-free rate | 0.03 - 0.06 |
| σ | Volatility | 0.10 - 1.00 |
| q | Dividend yield | 0.00 - 0.05 |
d₁ and d₂ Calculations
d₁ = [ln(S/K) + (r - q + σ²/2) × T] / (σ × √T)
d₂ = d₁ - σ × √TThe Greeks
Delta (Δ)
- Definition: Price change per $1 move in underlying
- Call: 0 to +1 (positive, bullish)
- Put: -1 to 0 (negative, bearish)
- ATM options: ~0.50 (call) or ~-0.50 (put)
Gamma (Γ)
- Definition: Rate of change of delta
- Always positive for long options
- Highest at ATM, decreases as option moves ITM/OTM
- Increases as expiration approaches
Theta (Θ)
- Definition: Daily time decay ($/day)
- Negative for long options (lose value daily)
- Positive for short options (collect decay)
- Accelerates in final 30 days
Vega (ν)
- Definition: Price change per 1% IV move
- Always positive for long options
- Highest at ATM
- Critical for earnings plays
Rho (ρ)
- Definition: Price change per 1% rate change
- Positive for calls, negative for puts
- Small impact in short-dated options
Historical vs Implied Volatility
| Metric | Description | Source |
|---|---|---|
| HV (Historical) | Past price movement | Calculated from prices |
| IV (Implied) | Market expectation | Derived from option prices |
IV-HV Relationship
- IV > HV: Options expensive → consider selling premium
- IV < HV: Options cheap → consider buying
- IV ≈ HV: Fairly priced
IV Percentile Interpretation
| Percentile | Interpretation | Strategy Bias |
|---|---|---|
| >80% | Very high IV | Sell premium |
| 50-80% | Elevated IV | Neutral/sell |
| 20-50% | Normal IV | Direction-based |
| <20% | Low IV | Buy premium |
Model Limitations
Assumptions (Not Always True)
1. Constant volatility - IV changes constantly 2. European exercise - American options can exercise early 3. No transaction costs - Commissions and slippage exist 4. Continuous trading - Gaps and halts occur 5. Log-normal distribution - Fat tails exist in practice
When Model Underperforms
- Deep ITM American puts (early exercise value)
- Near expiration with high gamma
- Earnings/events (IV regime change)
- Low liquidity options (wide bid-ask)
Practical Adjustments
American Options
- Calls: Black-Scholes accurate if no dividends
- Puts: May undervalue ITM puts near expiration
- Solution: Note "European pricing approximation"
Dividend Handling
- Include dividend yield (q) in calculations
- For discrete dividends: subtract PV from stock price
- Ex-dividend dates affect early exercise decisions
Strategy Selection Framework
By Market Outlook
| Outlook | Delta Target | Strategies |
|---|---|---|
| Bullish | +0.3 to +0.7 | Long call, bull spread, covered call |
| Bearish | -0.3 to -0.7 | Long put, bear spread, protective put |
| Neutral | -0.1 to +0.1 | Iron condor, straddle, butterfly |
By Volatility View
| IV View | Vega Target | Strategies |
|---|---|---|
| IV rising | Positive | Long straddle, long strangle |
| IV falling | Negative | Iron condor, short straddle |
| IV neutral | Near zero | Spreads, collars |
Position Sizing Guidelines
Risk-Based Sizing
Max Contracts = (Account Risk $) / (Max Loss per Contract)Portfolio Greeks Limits
| Greek | Conservative | Moderate | Aggressive |
|---|---|---|---|
| Delta | ±10 | ±25 | ±50 |
| Theta | +$50/day | +$150/day | +$300/day |
| Vega | ±$200 | ±$500 | ±$1000 |
References
- Black, F. & Scholes, M. (1973). "The Pricing of Options and Corporate Liabilities"
- Hull, J.C. "Options, Futures, and Other Derivatives"
- CBOE Education: https://www.cboe.com/education/
#!/usr/bin/env python3
"""
Black-Scholes Options Pricing Engine
Calculates theoretical option prices and Greeks using Black-Scholes model.
Features:
- European call and put pricing
- All Greeks (Delta, Gamma, Theta, Vega, Rho)
- Historical volatility calculation from price data
- Dividend adjustment support
Usage:
from black_scholes import OptionPricer
pricer = OptionPricer(
S=180, # Stock price
K=185, # Strike price
T=30/365, # Time to expiration (years)
r=0.053, # Risk-free rate
sigma=0.25, # Volatility
q=0.01 # Dividend yield
)
call_price = pricer.call_price()
delta = pricer.call_delta()
Author: Claude Trading Skills
Version: 1.0
"""
import numpy as np
import requests
from scipy.stats import norm
class OptionPricer:
"""Black-Scholes option pricer with Greeks calculation"""
def __init__(self, S, K, T, r, sigma, q=0):
"""
Initialize pricer with option parameters
Parameters:
-----------
S : float
Current stock price
K : float
Strike price
T : float
Time to expiration in years (e.g., 30/365 for 30 days)
r : float
Risk-free interest rate (annual, e.g., 0.053 for 5.3%)
sigma : float
Volatility (annual, e.g., 0.25 for 25%)
q : float, optional
Continuous dividend yield (annual, default 0)
"""
self.S = S
self.K = K
self.T = T
self.r = r
self.sigma = sigma
self.q = q
# Validate inputs
if S <= 0:
raise ValueError("Stock price must be positive")
if K <= 0:
raise ValueError("Strike price must be positive")
if T <= 0:
raise ValueError("Time to expiration must be positive")
if sigma <= 0:
raise ValueError("Volatility must be positive")
def _d1(self):
"""Calculate d1 parameter"""
numerator = np.log(self.S / self.K) + (self.r - self.q + 0.5 * self.sigma**2) * self.T
denominator = self.sigma * np.sqrt(self.T)
return numerator / denominator
def _d2(self):
"""Calculate d2 parameter"""
return self._d1() - self.sigma * np.sqrt(self.T)
# =========================================================================
# Option Pricing
# =========================================================================
def call_price(self):
"""Calculate European call option price"""
d1 = self._d1()
d2 = self._d2()
price = self.S * np.exp(-self.q * self.T) * norm.cdf(d1) - self.K * np.exp(
-self.r * self.T
) * norm.cdf(d2)
return max(0, price) # Price cannot be negative
def put_price(self):
"""Calculate European put option price"""
d1 = self._d1()
d2 = self._d2()
price = self.K * np.exp(-self.r * self.T) * norm.cdf(-d2) - self.S * np.exp(
-self.q * self.T
) * norm.cdf(-d1)
return max(0, price)
# =========================================================================
# Greeks - First Order
# =========================================================================
def call_delta(self):
"""
Calculate call delta
Delta: Change in option price per $1 change in stock price
Range: 0 to 1 for calls
"""
d1 = self._d1()
return np.exp(-self.q * self.T) * norm.cdf(d1)
def put_delta(self):
"""
Calculate put delta
Delta: Change in option price per $1 change in stock price
Range: -1 to 0 for puts
"""
d1 = self._d1()
return np.exp(-self.q * self.T) * (norm.cdf(d1) - 1)
def vega(self):
"""
Calculate vega (same for calls and puts)
Vega: Change in option price per 1% change in volatility
Always positive (options gain value when volatility increases)
"""
d1 = self._d1()
vega = self.S * np.exp(-self.q * self.T) * norm.pdf(d1) * np.sqrt(self.T)
return vega / 100 # Per 1% change in volatility
def call_theta(self):
"""
Calculate call theta
Theta: Change in option price per day (time decay)
Usually negative (options lose value as time passes)
"""
d1 = self._d1()
d2 = self._d2()
term1 = (
-self.S * norm.pdf(d1) * self.sigma * np.exp(-self.q * self.T) / (2 * np.sqrt(self.T))
)
term2 = -self.r * self.K * np.exp(-self.r * self.T) * norm.cdf(d2)
term3 = self.q * self.S * norm.cdf(d1) * np.exp(-self.q * self.T)
theta_annual = term1 + term2 + term3
return theta_annual / 365 # Convert to per-day
def put_theta(self):
"""Calculate put theta"""
d1 = self._d1()
d2 = self._d2()
term1 = (
-self.S * norm.pdf(d1) * self.sigma * np.exp(-self.q * self.T) / (2 * np.sqrt(self.T))
)
term2 = self.r * self.K * np.exp(-self.r * self.T) * norm.cdf(-d2)
term3 = -self.q * self.S * norm.cdf(-d1) * np.exp(-self.q * self.T)
theta_annual = term1 + term2 + term3
return theta_annual / 365
def call_rho(self):
"""
Calculate call rho
Rho: Change in option price per 1% change in interest rate
Positive for calls (calls gain value when rates increase)
"""
d2 = self._d2()
rho = self.K * self.T * np.exp(-self.r * self.T) * norm.cdf(d2)
return rho / 100 # Per 1% change
def put_rho(self):
"""
Calculate put rho
Rho: Change in option price per 1% change in interest rate
Negative for puts (puts lose value when rates increase)
"""
d2 = self._d2()
rho = -self.K * self.T * np.exp(-self.r * self.T) * norm.cdf(-d2)
return rho / 100
# =========================================================================
# Greeks - Second Order
# =========================================================================
def gamma(self):
"""
Calculate gamma (same for calls and puts)
Gamma: Change in delta per $1 change in stock price
Shows how fast delta changes
Highest for ATM options, lower for OTM and ITM
"""
d1 = self._d1()
gamma = (np.exp(-self.q * self.T) * norm.pdf(d1)) / (self.S * self.sigma * np.sqrt(self.T))
return gamma
# =========================================================================
# Utility Methods
# =========================================================================
def intrinsic_value(self, option_type="call"):
"""Calculate intrinsic value"""
if option_type.lower() == "call":
return max(0, self.S - self.K)
else: # put
return max(0, self.K - self.S)
def time_value(self, option_type="call"):
"""Calculate time value (extrinsic value)"""
if option_type.lower() == "call":
price = self.call_price()
else:
price = self.put_price()
intrinsic = self.intrinsic_value(option_type)
return price - intrinsic
def moneyness(self):
"""
Determine moneyness
Returns: 'ITM', 'ATM', or 'OTM'
"""
ratio = self.S / self.K
if abs(ratio - 1.0) < 0.02: # Within 2%
return "ATM"
elif ratio > 1.0:
return "ITM (Call) / OTM (Put)"
else:
return "OTM (Call) / ITM (Put)"
def get_all_greeks(self, option_type="call"):
"""
Get all Greeks for an option
Returns: dict with all Greeks
"""
if option_type.lower() == "call":
return {
"price": self.call_price(),
"delta": self.call_delta(),
"gamma": self.gamma(),
"theta": self.call_theta(),
"vega": self.vega(),
"rho": self.call_rho(),
"intrinsic_value": self.intrinsic_value("call"),
"time_value": self.time_value("call"),
}
else:
return {
"price": self.put_price(),
"delta": self.put_delta(),
"gamma": self.gamma(),
"theta": self.put_theta(),
"vega": self.vega(),
"rho": self.put_rho(),
"intrinsic_value": self.intrinsic_value("put"),
"time_value": self.time_value("put"),
}
# =============================================================================
# Historical Volatility Calculator
# =============================================================================
def calculate_historical_volatility(prices, window=30):
"""
Calculate historical volatility from price data
Parameters:
-----------
prices : array-like
Historical prices (daily)
window : int
Lookback window in days (default 30)
Returns:
--------
float
Annualized historical volatility
"""
if len(prices) < window + 1:
raise ValueError(f"Need at least {window + 1} price points")
# Calculate log returns
prices = np.array(prices)
log_returns = np.log(prices[1:] / prices[:-1])
# Use most recent 'window' returns
recent_returns = log_returns[-window:]
# Annualized volatility (252 trading days)
volatility = np.std(recent_returns) * np.sqrt(252)
return volatility
def fetch_historical_prices_for_hv(symbol, api_key, days=90):
"""
Fetch historical prices from FMP API for HV calculation
Parameters:
-----------
symbol : str
Stock ticker
api_key : str
FMP API key
days : int
Number of days to fetch
Returns:
--------
list
List of adjusted close prices
"""
# Try stable endpoint first, fall back to v3
endpoints = [
("https://financialmodelingprep.com/stable/historical-price-eod/full", True),
("https://financialmodelingprep.com/api/v3/historical-price-full", False),
]
for base_url, is_stable in endpoints:
try:
if is_stable:
url = base_url
resp = requests.get(
url, headers={"apikey": api_key}, params={"symbol": symbol}, timeout=30
)
else:
url = f"{base_url}/{symbol}"
resp = requests.get(url, headers={"apikey": api_key}, timeout=30)
if resp.status_code != 200:
continue
data = resp.json()
historical = None
if isinstance(data, dict) and "historical" in data:
historical = data["historical"]
elif isinstance(data, dict) and "historicalStockList" in data:
for entry in data["historicalStockList"]:
if entry.get("symbol", "").replace("-", ".") == symbol.replace("-", "."):
historical = entry.get("historical", [])
break
if historical:
historical = historical[:days]
historical = historical[::-1] # Reverse to chronological order
# stable shape compat: EOD endpoint exposes `close`, not `adjClose`
return [item.get("adjClose") or item["close"] for item in historical]
except Exception: # nosec B112 - intentional fallback to next FMP endpoint
continue
print(f"Error fetching prices for {symbol}: all endpoints failed")
return None
# =============================================================================
# FMP API Integration
# =============================================================================
def get_current_stock_price(symbol, api_key):
"""Fetch current stock price from FMP API (stable, with v3 fallback)."""
# stable: /quote?symbol=SYM ; v3 fallback (legacy keys): /quote/SYM
endpoints = [
("https://financialmodelingprep.com/stable/quote", True),
("https://financialmodelingprep.com/api/v3/quote", False),
]
for base_url, is_stable in endpoints:
try:
if is_stable:
response = requests.get(
base_url, headers={"apikey": api_key}, params={"symbol": symbol}, timeout=30
)
else:
response = requests.get(
f"{base_url}/{symbol}", headers={"apikey": api_key}, timeout=30
)
if response.status_code != 200:
continue
data = response.json()
if data and len(data) > 0 and data[0].get("price") is not None:
return data[0]["price"]
except Exception: # nosec B112 - intentional fallback to next FMP endpoint
continue
print(f"Error fetching current price for {symbol}: all endpoints failed")
return None
def get_dividend_yield(symbol, api_key):
"""Fetch dividend yield from FMP API (stable, with v3 fallback)."""
# stable: /profile?symbol=SYM ; v3 fallback (legacy keys): /profile/SYM
endpoints = [
("https://financialmodelingprep.com/stable/profile", True),
("https://financialmodelingprep.com/api/v3/profile", False),
]
for base_url, is_stable in endpoints:
try:
if is_stable:
response = requests.get(
base_url, headers={"apikey": api_key}, params={"symbol": symbol}, timeout=30
)
else:
response = requests.get(
f"{base_url}/{symbol}", headers={"apikey": api_key}, timeout=30
)
if response.status_code != 200:
continue
data = response.json()
if data and len(data) > 0:
# /stable/profile renamed lastDiv -> lastDividend; accept either.
last_div = data[0].get("lastDividend")
if last_div is None:
last_div = data[0].get("lastDiv", 0)
last_div = last_div or 0
price = data[0].get("price", 1) or 1
return (last_div / price) if price > 0 else 0
except Exception: # nosec B112 - intentional fallback to next FMP endpoint
continue
return 0
# =============================================================================
# Example Usage
# =============================================================================
if __name__ == "__main__":
print("\n" + "=" * 70)
print("BLACK-SCHOLES OPTIONS PRICER - EXAMPLE")
print("=" * 70)
# Example parameters
stock_price = 180.00
strike_price = 185.00
days_to_expiration = 30
time_to_expiration = days_to_expiration / 365
risk_free_rate = 0.053 # 5.3%
volatility = 0.25 # 25%
dividend_yield = 0.01 # 1%
print("\nInput Parameters:")
print(f" Stock Price: ${stock_price:.2f}")
print(f" Strike Price: ${strike_price:.2f}")
print(f" Days to Expiration: {days_to_expiration}")
print(f" Volatility: {volatility * 100:.1f}%")
print(f" Risk-Free Rate: {risk_free_rate * 100:.2f}%")
print(f" Dividend Yield: {dividend_yield * 100:.1f}%")
# Create pricer
pricer = OptionPricer(
S=stock_price,
K=strike_price,
T=time_to_expiration,
r=risk_free_rate,
sigma=volatility,
q=dividend_yield,
)
# Call option
print(f"\n{'=' * 70}")
print("CALL OPTION")
print("=" * 70)
call_greeks = pricer.get_all_greeks("call")
print(f"Price: ${call_greeks['price']:.2f}")
print(f"Intrinsic Value: ${call_greeks['intrinsic_value']:.2f}")
print(f"Time Value: ${call_greeks['time_value']:.2f}")
print("\nGreeks:")
print(f" Delta: {call_greeks['delta']:.4f} (${call_greeks['delta'] * 100:.2f} per $1 move)")
print(f" Gamma: {call_greeks['gamma']:.4f} (delta changes by {call_greeks['gamma']:.4f})")
print(
f" Theta: ${call_greeks['theta']:.2f}/day (loses ${abs(call_greeks['theta']):.2f} per day)"
)
print(
f" Vega: ${call_greeks['vega']:.2f} per 1% IV (gains ${call_greeks['vega']:.2f} if IV +1%)"
)
print(
f" Rho: ${call_greeks['rho']:.2f} per 1% rate (gains ${call_greeks['rho']:.2f} if rate +1%)"
)
# Put option
print(f"\n{'=' * 70}")
print("PUT OPTION")
print("=" * 70)
put_greeks = pricer.get_all_greeks("put")
print(f"Price: ${put_greeks['price']:.2f}")
print(f"Intrinsic Value: ${put_greeks['intrinsic_value']:.2f}")
print(f"Time Value: ${put_greeks['time_value']:.2f}")
print("\nGreeks:")
print(f" Delta: {put_greeks['delta']:.4f} (${put_greeks['delta'] * 100:.2f} per $1 move)")
print(f" Gamma: {put_greeks['gamma']:.4f} (delta changes by {put_greeks['gamma']:.4f})")
print(
f" Theta: ${put_greeks['theta']:.2f}/day (loses ${abs(put_greeks['theta']):.2f} per day)"
)
print(
f" Vega: ${put_greeks['vega']:.2f} per 1% IV (gains ${put_greeks['vega']:.2f} if IV +1%)"
)
print(
f" Rho: ${put_greeks['rho']:.2f} per 1% rate (loses ${abs(put_greeks['rho']):.2f} if rate +1%)"
)
# Moneyness
print(f"\n{'=' * 70}")
print(f"Moneyness: {pricer.moneyness()}")
print("=" * 70 + "\n")
# Historical Volatility Example
print("\nHistorical Volatility Example:")
print("-" * 70)
# Simulate price data
np.random.seed(42)
simulated_prices = [180 * np.exp(np.sum(np.random.randn(i) * 0.01)) for i in range(90)]
hv = calculate_historical_volatility(simulated_prices, window=30)
print(f"30-Day Historical Volatility: {hv * 100:.2f}%")
print(f"Implied Volatility (input): {volatility * 100:.1f}%")
if hv < volatility:
print("→ IV > HV: Options may be expensive (consider selling premium)")
elif hv > volatility:
print("→ IV < HV: Options may be cheap (consider buying)")
else:
print("→ IV ≈ HV: Options fairly priced")
print("\n" + "=" * 70 + "\n")
"""Shared fixtures for Options Strategy Advisor tests"""
import os
import sys
# Add scripts directory to path so modules can be imported
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
# Add tests directory to path so helpers can be imported
sys.path.insert(0, os.path.dirname(__file__))
"""Tests for Black-Scholes option pricing engine.
Covers:
- OptionPricer initialization and input validation
- Call/put pricing (known-value checks and put-call parity)
- Greeks: delta, gamma, theta, vega, rho
- Historical volatility calculation
- Utility methods: intrinsic value, time value, moneyness
"""
import numpy as np
import pytest
from black_scholes import OptionPricer, calculate_historical_volatility
# ── Fixtures ──────────────────────────────────────────────────────────
@pytest.fixture
def atm_pricer():
"""ATM call/put pricer with standard parameters."""
return OptionPricer(S=100, K=100, T=30 / 365, r=0.05, sigma=0.20)
@pytest.fixture
def itm_call_pricer():
"""ITM call pricer: S > K."""
return OptionPricer(S=110, K=100, T=30 / 365, r=0.05, sigma=0.25)
@pytest.fixture
def otm_call_pricer():
"""OTM call pricer: S < K."""
return OptionPricer(S=90, K=100, T=30 / 365, r=0.05, sigma=0.25)
@pytest.fixture
def dividend_pricer():
"""Pricer with continuous dividend yield."""
return OptionPricer(S=180, K=185, T=30 / 365, r=0.053, sigma=0.25, q=0.01)
# ── Input validation ──────────────────────────────────────────────────
class TestInputValidation:
def test_negative_stock_price(self):
with pytest.raises(ValueError, match="Stock price must be positive"):
OptionPricer(S=-10, K=100, T=0.1, r=0.05, sigma=0.2)
def test_zero_strike(self):
with pytest.raises(ValueError, match="Strike price must be positive"):
OptionPricer(S=100, K=0, T=0.1, r=0.05, sigma=0.2)
def test_negative_time(self):
with pytest.raises(ValueError, match="Time to expiration must be positive"):
OptionPricer(S=100, K=100, T=-1, r=0.05, sigma=0.2)
def test_zero_volatility(self):
with pytest.raises(ValueError, match="Volatility must be positive"):
OptionPricer(S=100, K=100, T=0.1, r=0.05, sigma=0)
# ── Option pricing ────────────────────────────────────────────────────
class TestOptionPricing:
def test_call_price_positive(self, atm_pricer):
assert atm_pricer.call_price() > 0
def test_put_price_positive(self, atm_pricer):
assert atm_pricer.put_price() > 0
def test_itm_call_has_intrinsic(self, itm_call_pricer):
"""ITM call should be worth at least intrinsic value."""
assert itm_call_pricer.call_price() >= itm_call_pricer.S - itm_call_pricer.K
def test_otm_call_less_than_stock(self, otm_call_pricer):
"""OTM call should be worth less than the stock."""
assert otm_call_pricer.call_price() < otm_call_pricer.S
def test_put_call_parity(self, atm_pricer):
"""Put-call parity: C - P = S*e^(-qT) - K*e^(-rT)."""
p = atm_pricer
call = p.call_price()
put = p.put_price()
expected_diff = p.S * np.exp(-p.q * p.T) - p.K * np.exp(-p.r * p.T)
assert abs((call - put) - expected_diff) < 0.01
def test_put_call_parity_with_dividend(self, dividend_pricer):
"""Put-call parity should also hold with dividends."""
p = dividend_pricer
call = p.call_price()
put = p.put_price()
expected_diff = p.S * np.exp(-p.q * p.T) - p.K * np.exp(-p.r * p.T)
assert abs((call - put) - expected_diff) < 0.01
def test_longer_expiry_more_expensive(self):
"""Option with longer time should be more expensive (all else equal)."""
short = OptionPricer(S=100, K=100, T=15 / 365, r=0.05, sigma=0.20)
long = OptionPricer(S=100, K=100, T=60 / 365, r=0.05, sigma=0.20)
assert long.call_price() > short.call_price()
assert long.put_price() > short.put_price()
def test_higher_vol_more_expensive(self):
"""Higher volatility should increase option price."""
low_vol = OptionPricer(S=100, K=100, T=30 / 365, r=0.05, sigma=0.10)
high_vol = OptionPricer(S=100, K=100, T=30 / 365, r=0.05, sigma=0.40)
assert high_vol.call_price() > low_vol.call_price()
# ── Greeks ────────────────────────────────────────────────────────────
class TestGreeks:
def test_call_delta_range(self, atm_pricer):
"""Call delta should be between 0 and 1."""
delta = atm_pricer.call_delta()
assert 0 < delta < 1
def test_put_delta_range(self, atm_pricer):
"""Put delta should be between -1 and 0."""
delta = atm_pricer.put_delta()
assert -1 < delta < 0
def test_atm_call_delta_near_05(self, atm_pricer):
"""ATM call delta should be close to 0.5."""
assert abs(atm_pricer.call_delta() - 0.5) < 0.1
def test_delta_put_call_relationship(self, atm_pricer):
"""call_delta - put_delta should equal e^(-qT)."""
diff = atm_pricer.call_delta() - atm_pricer.put_delta()
expected = np.exp(-atm_pricer.q * atm_pricer.T)
assert abs(diff - expected) < 0.01
def test_gamma_positive(self, atm_pricer):
"""Gamma is always positive."""
assert atm_pricer.gamma() > 0
def test_atm_gamma_highest(self):
"""ATM gamma should be higher than OTM or ITM gamma."""
atm = OptionPricer(S=100, K=100, T=30 / 365, r=0.05, sigma=0.20)
itm = OptionPricer(S=120, K=100, T=30 / 365, r=0.05, sigma=0.20)
otm = OptionPricer(S=80, K=100, T=30 / 365, r=0.05, sigma=0.20)
assert atm.gamma() > itm.gamma()
assert atm.gamma() > otm.gamma()
def test_vega_positive(self, atm_pricer):
"""Vega is always positive (options gain value with higher vol)."""
assert atm_pricer.vega() > 0
def test_call_theta_negative(self, atm_pricer):
"""Call theta is typically negative (time decay)."""
assert atm_pricer.call_theta() < 0
def test_put_theta_negative(self, atm_pricer):
"""Put theta is typically negative."""
assert atm_pricer.put_theta() < 0
def test_call_rho_positive(self, atm_pricer):
"""Call rho is positive (calls gain value with higher rates)."""
assert atm_pricer.call_rho() > 0
def test_put_rho_negative(self, atm_pricer):
"""Put rho is negative (puts lose value with higher rates)."""
assert atm_pricer.put_rho() < 0
# ── Utility methods ───────────────────────────────────────────────────
class TestUtilityMethods:
def test_intrinsic_value_itm_call(self, itm_call_pricer):
assert itm_call_pricer.intrinsic_value("call") == 10.0
def test_intrinsic_value_otm_call(self, otm_call_pricer):
assert otm_call_pricer.intrinsic_value("call") == 0.0
def test_intrinsic_value_itm_put(self, otm_call_pricer):
"""OTM call is ITM put."""
assert otm_call_pricer.intrinsic_value("put") == 10.0
def test_time_value_positive(self, atm_pricer):
"""Time value should be positive for ATM options."""
assert atm_pricer.time_value("call") > 0
assert atm_pricer.time_value("put") > 0
def test_moneyness_atm(self, atm_pricer):
assert atm_pricer.moneyness() == "ATM"
def test_moneyness_itm(self, itm_call_pricer):
assert "ITM" in itm_call_pricer.moneyness()
def test_moneyness_otm(self, otm_call_pricer):
assert "OTM" in otm_call_pricer.moneyness()
def test_get_all_greeks_call(self, atm_pricer):
greeks = atm_pricer.get_all_greeks("call")
assert "price" in greeks
assert "delta" in greeks
assert "gamma" in greeks
assert "theta" in greeks
assert "vega" in greeks
assert "rho" in greeks
def test_get_all_greeks_put(self, atm_pricer):
greeks = atm_pricer.get_all_greeks("put")
assert greeks["delta"] < 0 # put delta negative
# ── Historical volatility ─────────────────────────────────────────────
class TestHistoricalVolatility:
def test_constant_prices_zero_vol(self):
"""Constant prices should give zero volatility."""
prices = [100.0] * 40
hv = calculate_historical_volatility(prices, window=30)
assert hv == 0.0
def test_insufficient_data(self):
"""Should raise when not enough data for the window."""
prices = [100.0] * 10
with pytest.raises(ValueError, match="Need at least"):
calculate_historical_volatility(prices, window=30)
def test_reasonable_volatility_range(self):
"""Simulated data should produce a reasonable annualized vol."""
np.random.seed(42)
prices = [100.0]
for _ in range(60):
prices.append(prices[-1] * (1 + np.random.randn() * 0.01))
hv = calculate_historical_volatility(prices, window=30)
# Should be in a reasonable range for ~1% daily moves
assert 0.05 < hv < 0.50
def test_higher_moves_higher_vol(self):
"""Larger daily moves should produce higher volatility."""
np.random.seed(42)
small_moves = [100.0]
large_moves = [100.0]
for _ in range(60):
r = np.random.randn()
small_moves.append(small_moves[-1] * (1 + r * 0.005))
large_moves.append(large_moves[-1] * (1 + r * 0.03))
hv_small = calculate_historical_volatility(small_moves, window=30)
hv_large = calculate_historical_volatility(large_moves, window=30)
assert hv_large > hv_small
"""FMP /stable migration: live quote + dividend-yield helpers.
get_current_stock_price and get_dividend_yield used v3 path-style endpoints
(/quote/SYM, /profile/SYM) that 403 for keys issued after 2025-08-31. They now
call the /stable query-style endpoints first, with a v3 fallback. /stable also
renamed the dividend field lastDiv -> lastDividend, which get_dividend_yield
must read or it silently returns 0.
"""
import os
import sys
from unittest.mock import MagicMock, patch
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from black_scholes import get_current_stock_price, get_dividend_yield
def _resp(status_code, json_payload):
resp = MagicMock()
resp.status_code = status_code
resp.json.return_value = json_payload
return resp
class TestCurrentStockPrice:
@patch("black_scholes.requests")
def test_uses_stable_quote_first(self, mock_requests):
mock_requests.get.return_value = _resp(200, [{"symbol": "AAPL", "price": 150.0}])
price = get_current_stock_price("AAPL", "key")
assert price == 150.0
call = mock_requests.get.call_args
assert call[0][0].endswith("/stable/quote")
assert call[1]["params"] == {"symbol": "AAPL"}
@patch("black_scholes.requests")
def test_falls_back_to_v3(self, mock_requests):
def fake_get(url, headers=None, params=None, timeout=None):
if url.endswith("/stable/quote"):
return _resp(403, {}) # legacy/stable failure -> fallback
return _resp(200, [{"symbol": "AAPL", "price": 150.0}])
mock_requests.get.side_effect = fake_get
price = get_current_stock_price("AAPL", "key")
assert price == 150.0
urls = [c[0][0] for c in mock_requests.get.call_args_list]
assert any(u.endswith("/api/v3/quote/AAPL") for u in urls)
class TestDividendYield:
@patch("black_scholes.requests")
def test_reads_stable_lastDividend_field(self, mock_requests):
# /stable/profile uses lastDividend (no lastDiv). Yield = 2.0 / 100.
mock_requests.get.return_value = _resp(
200, [{"symbol": "AAPL", "lastDividend": 2.0, "price": 100.0}]
)
assert get_dividend_yield("AAPL", "key") == 0.02
call = mock_requests.get.call_args
assert call[0][0].endswith("/stable/profile")
assert call[1]["params"] == {"symbol": "AAPL"}
@patch("black_scholes.requests")
def test_v3_lastDiv_still_supported(self, mock_requests):
def fake_get(url, headers=None, params=None, timeout=None):
if url.endswith("/stable/profile"):
return _resp(403, {})
return _resp(200, [{"symbol": "AAPL", "lastDiv": 4.0, "price": 100.0}])
mock_requests.get.side_effect = fake_get
assert get_dividend_yield("AAPL", "key") == 0.04
@patch("black_scholes.requests")
def test_zero_when_no_data(self, mock_requests):
mock_requests.get.return_value = _resp(200, [])
assert get_dividend_yield("AAPL", "key") == 0
Related skills
How it compares
Use Options Strategy Advisor for theoretical education and free-data modeling; use broker-integrated tools when live chain data and execution matter.
FAQ
What does options-strategy-advisor do?
Options trading strategy analysis and simulation tool. Provides theoretical pricing using Black-Scholes model, Greeks calculation, strategy P/L simulation, and risk management guidance. Use when user requests options str
When should I use options-strategy-advisor?
Options trading strategy analysis and simulation tool. Provides theoretical pricing using Black-Scholes model, Greeks calculation, strategy P/L simulation, and risk management guidance. Use when user requests options str
What are common prerequisites?
--- name: options-strategy-advisor description: Options trading strategy analysis and simulation tool.
Is Options Strategy Advisor safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.