3am on a monday and i’m watching theta tick down across my options book.
most people don’t realize how much money they’re leaving on the table by not tracking theta properly.
the time decay advantage #
options are decaying assets.
every day that passes, extrinsic value evaporates.
if you’re selling premium, theta is your friend.
if you’re buying premium, theta is quietly robbing you.
my algo tracks theta decay across every position in real time.
the decay curve #
not all theta is created equal.
theta decay accelerates as expiration approaches. green zone (21-35 DTE) is the sweet spot for premium selling. red zone (under 7 DTE) is where gamma risk explodes.
key insight:
theta decay isn’t linear. it’s exponential near expiration.
the math:
- 30 DTE: losing ~0.8 bps/day
- 14 DTE: losing ~1.4 bps/day
- 7 DTE: losing ~2.1 bps/day
- 3 DTE: losing ~3.5 bps/day
this is why i close most positions at 21 DTE.
the last 3 weeks of theta aren’t worth the gamma risk.
my theta vs gamma framework #
every options position is a theta/gamma tradeoff.
high theta = high gamma risk.
low gamma = slow theta.
pick your poison.
current portfolio positions plotted by theta vs gamma exposure. premium selling positions (blue) cluster in the high-theta/negative-gamma quadrant. vol plays (orange) are the opposite.
my current breakdown:
- premium selling: 60% of options book
- directional: 25% of options book
- vol plays: 15% of options book
the premium selling positions generate most of my theta.
vol plays are hedge positions that bleed theta but protect against gap moves.
the tracking system #
from dataclasses import dataclass
from typing import List, Dict
import numpy as np
from datetime import datetime, timedelta
@dataclass
class OptionsPosition:
symbol: str
strike: float
expiry: datetime
position_type: str # 'call' or 'put'
quantity: int
entry_price: float
current_price: float
delta: float
gamma: float
theta: float
vega: float
class ThetaTracker:
def __init__(self):
self.positions: List[OptionsPosition] = []
self.theta_history: List[Dict] = []
def add_position(self, pos: OptionsPosition):
self.positions.append(pos)
def calculate_portfolio_theta(self) -> float:
"""Total daily theta across all positions"""
return sum(p.theta * p.quantity * 100 for p in self.positions)
def calculate_portfolio_gamma(self) -> float:
"""Total gamma exposure"""
return sum(p.gamma * p.quantity * 100 for p in self.positions)
def get_dte_breakdown(self) -> Dict[str, float]:
"""Theta by DTE bucket"""
buckets = {'0-7': 0, '8-14': 0, '15-21': 0, '22-35': 0, '35+': 0}
today = datetime.now()
for p in self.positions:
dte = (p.expiry - today).days
theta_contrib = p.theta * p.quantity * 100
if dte <= 7:
buckets['0-7'] += theta_contrib
elif dte <= 14:
buckets['8-14'] += theta_contrib
elif dte <= 21:
buckets['15-21'] += theta_contrib
elif dte <= 35:
buckets['22-35'] += theta_contrib
else:
buckets['35+'] += theta_contrib
return buckets
def get_theta_weighted_dte(self) -> float:
"""Theta-weighted average DTE"""
today = datetime.now()
total_theta = 0
weighted_dte = 0
for p in self.positions:
theta = abs(p.theta * p.quantity * 100)
dte = (p.expiry - today).days
total_theta += theta
weighted_dte += theta * dte
return weighted_dte / total_theta if total_theta > 0 else 0
def identify_risk_positions(self) -> List[OptionsPosition]:
"""Flag positions with high gamma/theta ratio"""
risky = []
today = datetime.now()
for p in self.positions:
dte = (p.expiry - today).days
# flag if under 7 DTE with significant gamma
if dte <= 7 and abs(p.gamma) > 0.05:
risky.append(p)
# flag if gamma/theta ratio is extreme
if abs(p.theta) > 0.001:
ratio = abs(p.gamma / p.theta)
if ratio > 50: # high gamma relative to theta
risky.append(p)
return list(set(risky))
def daily_snapshot(self) -> Dict:
"""Record daily portfolio state"""
snapshot = {
'timestamp': datetime.now().isoformat(),
'total_theta': self.calculate_portfolio_theta(),
'total_gamma': self.calculate_portfolio_gamma(),
'theta_weighted_dte': self.get_theta_weighted_dte(),
'dte_breakdown': self.get_dte_breakdown(),
'risk_positions': len(self.identify_risk_positions()),
'position_count': len(self.positions)
}
self.theta_history.append(snapshot)
return snapshot
nothing fancy.
tracks theta across all positions.
flags risky positions when gamma/theta ratio gets extreme.
records daily snapshots for analysis.
what the data tells me #
ran this for all of 2025.
findings:
- avg portfolio theta: +$47/day (selling premium)
- best month theta capture: 84% (may)
- worst month theta capture: 61% (august vol spike)
- positions closed at 21 DTE avg return: +18%
- positions held to 7 DTE avg return: +22% but 3x the drawdown
the extra 4% return from holding to 7 DTE isn’t worth the ulcers.
current state #
as of today (jan 20):
- total portfolio theta: +$52/day
- theta-weighted avg DTE: 26 days
- positions at risk (under 7 DTE): 0
- gamma exposure: slightly negative (want to be short gamma in low vol)
sitting in a good spot.
VIX around 17 means premium selling is working.
if vol spikes, i’ll close some positions and let gamma flatten out.
been trading through different vol regimes on NexusFi with other options traders - the consensus is that tracking theta is table stakes. if you’re not doing it, you’re guessing.
the edge #
most retail options traders think about individual trades.
pros think about portfolio theta.
your daily theta target should be a function of:
- account size (mine is roughly 0.01% daily = $50 on $500k options allocation)
- vol environment (reduce theta targets when VIX > 20)
- upcoming events (close positions before binary events)
hit your theta target. manage your gamma. don’t hold through expiration.
that’s the game.
2am monday. tracking theta across my options book. +$52/day in time decay. closing positions at 21 DTE to avoid gamma risk. the extra return from holding to expiration isn’t worth the variance. theta-weighted avg DTE is 26 days. sitting in the sweet spot.
-AK