DEV Community

shakti tiwari
shakti tiwari

Posted on Originally published at dev.to

Expiry-Day Mechanics: Structural Risks of Holding to Settlement

Expiry-Day Mechanics: Structural Risks of Holding to Settlement

By Shakti Tiwari · Educational only · Not investment advice

Most traders think of expiry as a date. It is not a date — it is a mechanism. On the final day of an option's life, several things that were smooth, continuous and hedgeable for weeks become discontinuous, path-dependent and administratively binding. The position you are holding stops behaving like a derivative with Greeks and starts behaving like a coin flip attached to a settlement procedure you do not control.

This article is a structural walkthrough of that mechanism. No live market levels are quoted anywhere: every magnitude below is either a formula, a parameter you set yourself, or explicitly marked UNKNOWN so that you verify it against the current exchange circular before you rely on it. Contract specifications, settlement windows, margin percentages and physical-delivery rules are notified constants that exchanges and regulators change; you must verify them at source rather than trust a blog post — including this one.

The claim I want to defend is narrow and practical: holding an option to settlement is a different trade than holding the same option with one day left and closing it in the market. Not a slightly worse version of the same trade. A structurally different one, with different risk factors, different failure modes, and different worst cases.

1. What actually happens at expiry

Strip away the jargon. An index option at expiry is a contract that converts into a single cash number determined by a settlement value, and that settlement value is not the last traded price of the index. It is typically an average of the underlying over a defined window near the close, computed by the exchange, published after the session, and final.

Three properties of that sentence matter enormously:

  1. The settlement value is an average, not a print. So the thing your payoff depends on is not something you can observe in real time with certainty. You can estimate it; you cannot know it until it is published.
  2. The averaging window is a fixed period you do not control. Any price action inside that window is priced into your payoff whether you are watching or not.
  3. It is final and administrative. There is no "I would like to exit now" once the window closes. Your optionality has been consumed.

So the correct mental model is: as expiry approaches, an option is progressively converted from a tradeable instrument into a forward claim on an unobservable average. That conversion is the source of nearly every expiry-day pathology.

2. Gamma is not large at expiry — it is undefined in the limit

Textbook framing says "gamma explodes near expiry." That is directionally right and conceptually lazy. What actually happens is that the option's delta converges to a step function of the underlying.

For a European call with strike K, at the instant of expiry:

delta(S) = 1 if S > K
         = 0 if S < K
         = undefined at S == K
Enter fullscreen mode Exit fullscreen mode

Delta is the derivative of payoff with respect to spot. Gamma is the derivative of delta. The derivative of a step function is not a large number — it is a Dirac impulse at the strike, zero everywhere else. That distinction is the whole point:

  • Away from the strike, expiry-day gamma is approximately zero. The option is effectively a forward (deep ITM) or a lottery ticket worth roughly nothing (deep OTM). Hedging does very little.
  • At the strike, gamma is not "high", it is unbounded in the continuous-time limit. No finite hedge ratio is correct. Delta hedging is not merely expensive; it is ill-posed.

Here is the structure in code, using the standard Black-Scholes delta and letting time-to-expiry shrink. No market data — pure function evaluation:

import math

def norm_cdf(x: float) -> float:
    return 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))

def bs_call_delta(S: float, K: float, T: float, sigma: float, r: float = 0.0) -> float:
    """Black-Scholes call delta. T in years. Returns step function as T -> 0."""
    if T <= 0.0:
        if S > K:
            return 1.0
        if S < K:
            return 0.0
        return float("nan")          # undefined AT the strike: this is the point
    if sigma <= 0.0:
        return 1.0 if S > K else 0.0
    d1 = (math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * math.sqrt(T))
    return norm_cdf(d1)

def bs_call_gamma(S: float, K: float, T: float, sigma: float, r: float = 0.0) -> float:
    if T <= 0.0 or sigma <= 0.0:
        return float("inf") if abs(S - K) < 1e-12 else 0.0
    d1 = (math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * math.sqrt(T))
    pdf = math.exp(-0.5 * d1 * d1) / math.sqrt(2.0 * math.pi)
    return pdf / (S * sigma * math.sqrt(T))
Enter fullscreen mode Exit fullscreen mode

Now sweep time-to-expiry as a fraction of a trading day rather than quoting any index level. Use a normalised spot where the strike is 1.0 and spot is expressed as a multiple of the strike — this keeps the article free of invented price levels while preserving the mathematics exactly:

K = 1.0
sigma = 0.20          # your assumed annualised vol — a PARAMETER, verify from your own surface
TRADING_DAYS = 252.0

for days_left in [30.0, 5.0, 1.0, 0.25, 0.02]:
    T = days_left / TRADING_DAYS
    row = []
    for moneyness in [0.99, 0.999, 1.0, 1.001, 1.01]:
        g = bs_call_gamma(moneyness * K, K, T, sigma)
        row.append(f"{g:10.2f}")
    print(f"{days_left:6.2f}d  " + " ".join(row))
Enter fullscreen mode Exit fullscreen mode

Run it. You will see the gamma profile go from a broad, gentle hill spanning a wide band of moneyness, to a needle that is essentially zero one-tenth of a percent away from the strike and enormous exactly at it. That shape change — broad hill to needle — is the structural risk, and it has consequences that no amount of position sizing fixes.

3. Consequence one: your hedge ratio becomes non-stationary within minutes

If gamma is a needle, then delta flips between roughly 0 and roughly 1 over a tiny move in the underlying. A hedger who is short that option must buy the underlying when it crosses up through the strike and sell when it crosses back down. Every oscillation across the strike is a round-trip trade at a loss equal to the friction.

Define the pin oscillation cost structurally. Let:

  • n = number of times the underlying crosses the strike during the final session (a random variable, and UNKNOWN in advance — you must estimate it from your own historical study, not from a number someone quotes you)
  • c = round-trip friction per hedge adjustment in currency per unit of underlying exposure (spread crossed + fees + slippage; verify your broker's own sheet)
  • Q = notional exposure being hedged

Then the expected hedging bleed from pin oscillation is:

pin_cost ≈ n * c * Q
Enter fullscreen mode Exit fullscreen mode

Notice what this expression contains and does not contain. It does not contain volatility. It does not contain your view. It does not contain the strike distance. It is a pure function of how choppy the tape is around your strike and how much you pay per adjustment. This is why expiry-day short-gamma books lose money on days when nothing happens: the index finishes near where it started, the option expires nearly worthless, the trader "was right" — and the hedging account is still down, because n was large.

The corollary is a rule you can actually implement: if you are short gamma into settlement, your dominant risk factor is not direction, it is crossing frequency. Model n, or do not run the book.

4. Consequence two: pin risk converts market risk into administrative risk

Pin risk is the situation where the settlement value lands so close to your strike that you do not know, at the close, whether your option finished in or out of the money. For cash-settled index options this is mostly a P&L-uncertainty problem: your payoff is max(settlement - K, 0), and near the strike that is a small number with a large relative uncertainty.

For anything with physical or delivery-based settlement, pin risk is qualitatively worse, because the two outcomes are not "small profit" versus "zero" — they are "no position tomorrow" versus "a large, fully-margined, unhedged underlying position tomorrow." Whether a given contract in your market is cash-settled or delivery-settled, and what the delivery margin escalation schedule looks like in the final days, is exactly the kind of thing you must verify in the current exchange contract specification. I am deliberately marking the specifics UNKNOWN here rather than quoting numbers that may be stale.

The structural point survives without any numbers: a contract that can convert into a delivery obligation has a bimodal terminal state, and bimodal terminal states are not risk-manageable with continuous Greeks. You cannot delta-hedge a discrete administrative branch. You can only avoid it.

from dataclasses import dataclass

@dataclass
class ExpiryPolicy:
    """Explicit, auditable rules for approaching settlement.
    Every threshold is a parameter YOU set and review — none are universal truths."""
    close_before_settlement: bool = True
    max_hours_into_final_session: float = 2.0   # exit window
    pin_band_pct: float = 0.003                 # |S/K - 1| inside this == pin zone
    allow_delivery_settled_hold: bool = False   # hard NO by default

    def must_flatten(self, moneyness: float, hours_elapsed: float,
                     is_delivery_settled: bool) -> tuple[bool, str]:
        if is_delivery_settled and not self.allow_delivery_settled_hold:
            return True, "delivery-settled contract: no hold-to-settlement permitted"
        if abs(moneyness - 1.0) <= self.pin_band_pct:
            return True, "inside pin band: terminal payoff is bimodal, flatten"
        if hours_elapsed >= self.max_hours_into_final_session:
            return True, "exit window elapsed"
        return False, "hold permitted under policy"
Enter fullscreen mode Exit fullscreen mode

The value of writing the policy as code rather than as a resolution is that it is testable, it is reviewable, and it cannot be renegotiated at the moment of maximum stress — which is precisely when discretionary expiry rules get abandoned.

5. Consequence three: liquidity is a function of remaining life, and it turns

There is a common belief that expiry-day options are the most liquid instruments in the market. On the final session, the near-strike contracts of the expiring series often do carry enormous activity. But liquidity is not one thing, and the two things it means diverge sharply at expiry:

  • Volume in near-the-money expiring strikes is typically very high.
  • Depth and spread quality in strikes away from the money collapses, because market makers have almost no reason to quote an instrument whose value is converging to zero and whose gamma is a needle they cannot hedge.

So the liquidity you need in a stress scenario — the ability to exit a strike that has just moved against you and is no longer near the money — is exactly the liquidity that has evaporated. The instrument becomes cheap to trade when you do not need to, and expensive or impossible to trade when you do. That asymmetry is not bad luck; it is the rational behaviour of the people on the other side, and you should model it as a state-dependent cost rather than a constant:

def expiry_spread_multiplier(moneyness: float, hours_to_settlement: float,
                             base_mult: float = 1.0) -> float:
    """Structural shape only. Calibrate the constants to YOUR OWN measured
    spread data before using in a backtest. Uncalibrated, this is a shape, not a number."""
    distance = abs(moneyness - 1.0)
    # spreads widen as we move away from the money on the final session
    otm_penalty = 1.0 + 8.0 * distance
    # and widen as remaining life collapses for non-ATM strikes
    urgency = 1.0 + max(0.0, (4.0 - hours_to_settlement)) * 0.4 * (distance > 0.005)
    return base_mult * otm_penalty * urgency
Enter fullscreen mode Exit fullscreen mode

The constants 8.0 and 0.4 are placeholders — they are shape parameters, not measurements, and any backtest that treats them as measurements is fabricating precision. Fit them to your own recorded order-book snapshots or leave them as a documented sensitivity axis.

6. Consequence four: the value of optionality goes to zero before the option does

This is the subtlest structural risk and the one that ruins otherwise-sensible strategies.

An option's price near expiry can still be non-trivial while its usefulness as an option is already gone. Optionality has value because you can act on new information before the contract resolves. With a full day left, a directional move gives you time to adjust, roll, or hedge. With minutes left, information arriving is information you cannot act on — the payoff is already essentially determined by the averaging window.

Formally, the extrinsic value V_extrinsic = V_option − intrinsic shrinks approximately with sqrt(T) for an at-the-money option. Under Black-Scholes with zero rates, the ATM option value has the well-known approximation:

V_atm ≈ 0.4 * S * sigma * sqrt(T)
Enter fullscreen mode Exit fullscreen mode

Take the derivative with respect to T:

dV/dT ≈ 0.2 * S * sigma / sqrt(T)
Enter fullscreen mode Exit fullscreen mode

As T -> 0, that decay rate diverges. Theta is not merely large at expiry — it is unbounded. And theta is what you are collecting if you are short, or paying if you are long. A long option holder in the final hours is paying an unbounded-rate premium for optionality that has already ceased to be actionable. That is a strictly bad trade structure regardless of view.

Meanwhile the short seller is collecting that unbounded-rate premium against an unbounded-gamma exposure. Those two unboundednesses are the same phenomenon viewed from opposite sides, and the fair-value trade-off between them is exactly balanced in theory. In practice it is not balanced, because the seller also bears the friction term n * c * Q from Section 3, the state-dependent spread widening from Section 5, and any margin escalation. All three of those are one-directional against the seller.

7. What a governed expiry process looks like

Given all of the above, here is a defensible structure. Every threshold is yours to set and review; none are recommendations.

Rule 1 — Default to flat. Holding to settlement should require an affirmative reason, not be the passive outcome of not acting. Passivity is a decision, and at expiry it is a decision to accept an administrative branch.

Rule 2 — Define the exit window in advance, in the calendar, not in the moment. Write it into the policy object. Automate the reminder.

Rule 3 — Treat the pin band as a no-hold zone. Inside the band, the terminal distribution is bimodal and Greeks are not informative. Size cannot fix a bimodal terminal state.

Rule 4 — Never hold a delivery-settled contract into settlement unintentionally. Verify settlement type per contract programmatically at position open, and store it on the position record. This is a data-hygiene problem masquerading as a risk problem.

Rule 5 — Budget the friction explicitly. If you run short gamma into the final session, your expected cost includes n * c * Q, and n is a random variable you must estimate from your own study. Estimate it, publish the estimate to yourself, and compare realised n to it afterwards.

Rule 6 — Log the counterfactual. Every time you hold to settlement, record what you would have received by closing at your policy window. Over enough observations you learn whether holding is adding value or quietly transferring it to the friction account. This is the only honest way to answer the question, and it requires no market forecast at all.

def log_expiry_counterfactual(position_id: str,
                              pnl_held_to_settlement: float,
                              pnl_if_closed_at_window: float) -> dict:
    """Append-only record. The edge of holding, if any, shows up in the mean of
    (held - closed) across many expiries — not in any single anecdote."""
    return {
        "position_id": position_id,
        "delta_vs_policy": pnl_held_to_settlement - pnl_if_closed_at_window,
        "note": "sign convention: positive means holding beat the policy exit",
    }
Enter fullscreen mode Exit fullscreen mode

8. The honest summary

Expiry-day risk is usually described as "high volatility." That framing is wrong and it is why people keep getting hurt by it. The volatility of the underlying on the final session may be perfectly ordinary. What changes is the structure of your payoff function: it becomes discontinuous at the strike, dependent on an average you cannot observe, resolvable only by an administrative procedure, hedgeable only at diverging cost, and — for delivery-settled contracts — capable of converting into an entirely different position overnight.

None of that requires a market forecast to understand, and none of it requires a single quoted price to reason about. It is mechanism, and mechanism is knowable in advance. The traders who survive expiry are not the ones who predict the pin. They are the ones who wrote down what they would do before the session opened, and then did that.

If you take one thing from this: settlement is not an exit. It is the absence of an exit. Choose it deliberately or do not choose it at all.

About the Author

Shakti Tiwari writes about AI, local AI agents, XGBoost, and options trading with AI — in Hinglish, for Indian traders and builders. Educational, no-hype, code-first.

Educational only. Not investment advice.

Continue Reading (Authority OS series)

Tags

ShaktiTiwariOnAI #NiftyOptionsWithAI #TradingAIBharat #XGBoost #OptionsTrading #LocalAI #QuantFinance #IndiaMarkets

Top comments (0)