1. What “no arbitrage” really means

Informal working definition:

You should not be able to construct a zero-cost portfolio today that gives a non-negative payoff in all states and a strictly positive payoff in at least one state.

If you can build such a portfolio from quoted instruments, either:

  • some price is wrong, or
  • there are constraints (funding, short-selling, collateral, credit) that block the trade.

For interviews, you assume an ideal frictionless market and use no-arbitrage as a consistency condition to relate prices of:

  • spot / forward / discount factors
  • calls / puts with same strike & maturity
  • options across strikes and maturities

2. Put–call parity

2.1 Standard equity-style parity

European call and put on the same underlying, with:

  • current spot price: S0S_0
  • strike: KK
  • maturity: TT
  • risk-free rate (continuously compounded): rr
  • zero dividends

Put–call parity:

C0−P0=S0−Ke−rT C_0 - P_0 = S_0 - K e^{-rT}

Equivalently:

C0=P0+S0−Ke−rT C_0 = P_0 + S_0 - K e^{-rT} P0=C0−S0+Ke−rT P_0 = C_0 - S_0 + K e^{-rT}

Replication idea:

Consider two portfolios:

  1. Portfolio A: long call +C+C and long discount bond that pays KK at TT. Current cost: C0+Ke−rTC_0 + K e^{-rT}.

  2. Portfolio B: long put +P+P and long stock +S+S. Current cost: P0+S0P_0 + S_0.

Check their payoff at maturity:

  • If ST>KS_T > K:
    • A: call is in the money ⇒ST−K\Rightarrow S_T - K, plus KK from bond ⇒ STS_T.
    • B: put expires worthless, stock is worth STS_T ⇒ STS_T.
  • If ST≤KS_T \le K:
    • A: call worthless, bond pays KK ⇒ KK.
    • B: stock worth STS_T, put worth K−STK - S_T ⇒ ST+(K−ST)=KS_T + (K - S_T) = K.

So payoffs are identical in all states. No-arbitrage implies their initial prices must be equal, which gives the formula above.


2.2 Forward-style parity (useful in commodities)

Equity-style spot + bond is often inconvenient in power & commodities where:

  • forwards/futures are the primary liquid object,
  • “spot” is ill-defined or only a settlement index.

Define:

  • Forward price for delivery at TT: F0,TF_{0,T}
  • Discount factor: D(0,T)D(0,T) (e.g. e−rTe^{-rT})

For options on forwards (common in power/gas):

C0−P0=D(0,T)⋅(F0,T−K) C_0 - P_0 = D(0,T) \cdot (F_{0,T} - K)

This is the same parity, just expressed in terms of the forward instead of spot.

Interview line:

“In commodities, I tend to write put–call parity as C−P=D(0,T)(F−K)C - P = D(0,T)(F - K). It’s more natural because forwards are the primary quotes and carry/seasonality are already baked into FF.”


3. Arbitrage bounds for call and put prices

No-arbitrage also gives inequalities (bounds) for option prices.

Assume non-dividend underlying, non-negative interest rate r≥0r \ge 0.

3.1 Call option bounds

Call premium C0C_0 must satisfy:

max⁡(0,S0−Ke−rT)≤C0≤S0 \max(0, S_0 - K e^{-rT}) \;\le\; C_0 \;\le\; S_0

  • Upper bound C0≤S0C_0 \le S_0: A call is never worth more than the stock itself; with the stock you can do at least as well as the call in all states. If C0>S0C_0 > S_0, short call, buy stock → non-negative payoff and upfront profit.

  • Lower bound C0≥max⁡(0,S0−Ke−rT)C_0 \ge \max(0, S_0 - K e^{-rT}): Start from parity C0=P0+S0−Ke−rTC_0 = P_0 + S_0 - K e^{-rT}. Since P0≥0P_0 \ge 0,

C0≥S0−Ke−rT. C_0 \ge S_0 - K e^{-rT}.

Also, a call can never be negative ⇒ C0≥0C_0 \ge 0. Combine them: C0≥max⁡(0,S0−Ke−rT)C_0 \ge \max(0, S_0 - K e^{-rT}).

3.2 Put option bounds

Similarly, put premium P0P_0 must satisfy:

max⁡(0,Ke−rT−S0)≤P0≤Ke−rT \max(0, K e^{-rT} - S_0) \;\le\; P_0 \;\le\; K e^{-rT}

  • Upper bound P0≤Ke−rTP_0 \le K e^{-rT}: The put can never pay more than KK, so its present value can’t exceed the PV of KK.

  • Lower bound from parity and C0≥0C_0 \ge 0:

P0=C0−S0+Ke−rT≥−S0+Ke−rT P_0 = C_0 - S_0 + K e^{-rT} \ge -S_0 + K e^{-rT}

and also P0≥0P_0 \ge 0. Combine them.


4. Monotonicity properties

These are dominance arguments: if one payoff is always ≥ another payoff, it must be pricier.

4.1 Monotonicity in strike

For two strikes K1<K2K_1 < K_2, same maturity TT:

  • Calls: C(K1)≥C(K2)C(K_1) \ge C(K_2)
  • Puts: P(K1)≤P(K2)P(K_1) \le P(K_2)

Pathwise for calls:

(ST−K1)+≥(ST−K2)+for all ST (S_T - K_1)^+ \ge (S_T - K_2)^+ \quad \text{for all } S_T

So call with lower strike dominates higher-strike call ⇒ must be more expensive.

For puts:

(K1−ST)+≤(K2−ST)+for all ST (K_1 - S_T)^+ \le (K_2 - S_T)^+ \quad \text{for all } S_T

So higher-strike put dominates ⇒ must be more expensive.

If your call surface increases with strike, something is wrong: either quotes are off, or you are misreading them.


4.2 Monotonicity in underlying price

If spot increases, calls should be worth more and puts less.

Formally, for fixed K,TK, T:

  • C(S)C(S) is increasing in SS,
  • P(S)P(S) is decreasing in SS.

Pathwise dominance:

  • If you add ΔS>0\Delta S > 0 to the underlying level in every state, the call payoff never decreases (and typically increases); the put payoff never increases.

In models, this is reflected by:

  • call delta ∂C/∂S∈(0,1)\partial C / \partial S \in (0, 1)
  • put delta ∂P/∂S∈(−1,0)\partial P / \partial S \in (-1, 0)

4.3 Monotonicity in maturity (caveat: rates, dividends)

Under constant non-negative rates and no dividends:

  • C(K,T)C(K, T) is non-decreasing in TT: more time ⇒ more optionality.
  • P(K,T)P(K, T) is non-increasing in TT: with more time, the PV of strike shrinks and chance of ending ITM is nuanced; but the no-arb result (in frictionless, non-negative rate environment) gives non-increasing behaviour.

In real markets:

  • Term structure of rates and dividends (or cost-of-carry, convenience yield) can bend this a bit.
  • But gross violations (“1-year call cheaper than 1-month call at same strike in a stable market”) are a red flag.

5. How this shows up in power & commodities

Key differences vs equity:

  • You often think in terms of forwards and discount factors: C−P=D(0,T)(F−K)C - P = D(0,T)(F - K).

  • “Spot” may be an index over a delivery period rather than a snapshot price.

But the logic is identical:

  • Options on forwards/futures still satisfy put–call parity.
  • Calls should still be monotone decreasing in strike.
  • Puts monotone increasing in strike.
  • Option on more volatile / longer horizons should not be worth less than the same option on a short horizon (modulo carry effects).

In practice you use these as:

  1. Data sanity checks. After scraping quotes, check:

    • If any call price < max(0, S - K e^{-rT}) or > S.
    • If call(K1) < call(K2) with K1 < K2.
    • If put–call parity is badly off.

    Big violations → bad data or mis-mapped contracts (wrong strike unit, wrong underlying).

  2. Unit tests for pricing engines.

    • Numerically computed call and put must satisfy parity to within tolerance.
    • Surfaces must respect monotonicity and positivity constraints.
  3. Interpolation / calibration discipline.

    • When you interpolate option prices or implied vols, you must preserve no-arbitrage: otherwise you build arbitrage into your own model.

6. Example arbitrage from put–call parity

Suppose market quotes:

  • S0=100S_0 = 100
  • K=100K = 100
  • T=1T = 1 year, r=5%r = 5\% ⇒ Ke−rT≈95.12K e^{-rT} \approx 95.12

True parity:

C0−P0=100−95.12=4.88 C_0 - P_0 = 100 - 95.12 = 4.88

Assume market gives:

  • C0mkt=12C_0^\text{mkt} = 12
  • P0mkt=6P_0^\text{mkt} = 6

Then:

C0mkt−P0mkt=6(too high vs 4.88) C_0^\text{mkt} - P_0^\text{mkt} = 6 \quad (\text{too high vs } 4.88)

Arbitrage (one possible construction):

  • Short one call −C-C
  • Long one put +P+P
  • Long PV(K) in bond +Ke−rT+K e^{-rT}
  • Short one share −S-S

Initial cash flow:

CF0=−C0mkt+P0mkt+Ke−rT−S0=−(12)+6+95.12−100=−10.88<0 \text{CF}_0 = -C_0^\text{mkt} + P_0^\text{mkt} + K e^{-rT} - S_0 = -(12) + 6 + 95.12 - 100 = -10.88 < 0

This sign convention means: you receive +10.88 upfront (because you are net short the expensive combination).

At maturity, you can check state-by-state that payoff is identically zero. So you lock in risk-free profit today. In practice, markets adjust quotes or the mispriced leg disappears.


7. Interview soundbites

A few sentences you can use:

  • On parity: “Put–call parity is just replication: long call + bond must equal long put + underlying. For options on forwards I write it as C−P=D(0,T)(F−K)C - P = D(0,T)(F - K). I use it as a hard constraint to QA both market data and our pricing outputs.”

  • On bounds: “From parity I get bounds like $ (0, S - K e^{-rT}) C S$. Any violation is either a data issue or a mis-specified contract; we flag and clean that before feeding models.”

  • On monotonicity: “Prices must be monotone in strike and underlying by simple payoff dominance. I like to treat those as unit tests for our surfaces and finite-difference solvers: if monotonicity is broken, I don’t trust Greeks or hedges.”


Good, this is the right pause to take. Let’s park the math and talk like a trader explaining to a junior.


1. Cast of characters

Think of every option as a little contract between two people:

  • Underlying: the thing whose price moves (stock, power forward, gas, etc.).
  • Strike KK: the “deal price” written in the contract.
  • Expiry TT: the last day the contract matters.
  • Buyer (long option):
    • Pays premium today.
    • Has a right, not an obligation.
    • Worst case: loses the premium.
  • Seller / writer (short option):
    • Receives premium today.
    • Has an obligation if the buyer exercises.
    • Best case: keeps the premium; worst case: can lose a lot.

The option price / premium is just:

“How much the buyer pays today to shift some risk to the seller.”


2. Call option – “right to buy later”

Plain English:

  • A call is the right to buy the underlying at strike KK on/at expiry.
  • The buyer of a call likes up moves in the underlying.
  • The seller of a call is giving away upside in exchange for the premium.

Payoff at expiry (call buyer):

  • If final price ST≤KS_T \le K: right is useless → payoff = 0.
  • If ST>KS_T > K: exercise → payoff = ST−KS_T - K.

Graph in words:

  • Draw horizontal axis = final price STS_T.
  • Vertical axis = payoff.
  • Below KK: flat at zero.
  • Above KK: straight line with slope +1 starting from zero at ST=KS_T = K.

So:

  • Long call payoff:
    • Bottom is floored at zero.
    • Upside is unlimited.
  • Long call profit = payoff − premium.
    • Shifts the whole graph down by the premium.

How a pro describes it:

“A long call is a cheap way to be long convexity: you pay a fixed premium, your downside is capped, your upside is open-ended.”

Seller side:

  • Short call payoff = −(long call payoff).
  • Short call profit = premium − payoff.
    • You earn premium if market is quiet or down.
    • You bleed if market rallies through the strike.

“Short call = I’ve sold upside. I collect premium; I’m short convexity and short gamma.”


3. Put option – “right to sell later”

Plain English:

  • A put is the right to sell the underlying at strike KK on/at expiry.
  • The buyer of a put likes down moves (it’s protection against drops).
  • The seller of a put is insuring someone against a drop.

Payoff at expiry (put buyer):

  • If ST≥KS_T \ge K: you wouldn’t sell at K, so payoff = 0.
  • If ST<KS_T < K: you exercise → payoff = K−STK - S_T.

Graph:

  • Horizontal axis = STS_T.
  • Vertical axis = payoff.
  • Above KK: flat at zero.
  • Below KK: straight line going up as price goes down (slope −1).

Again:

  • Long put payoff:
    • Upside is limited (max is K if S_T→0).
    • Downside is floored at zero.
  • Long put profit = payoff − premium.

Typical practitioner line:

“A long put is downside insurance: I pay a premium so I know my minimum selling price.”

Seller:

  • Short put payoff = −(long put payoff).
  • Profit = premium − payoff.

“Short put = I’m selling downside insurance. I earn premium most of the time, but I eat the crash.”


4. Buyer vs seller: who wants what?

Call buyer

  • View: “I think it might go up a lot, but I don’t want big downside.”
  • Pays premium now.
  • Happy if market moves a lot up.
  • Sad if market stays below K (option expires worthless).

Call seller

  • View: “I think it won’t rally too much; I’m happy to give up some upside for income.”
  • Receives premium now.
  • Happy if market stays below K.
  • Suffering if market rallies hard.

Put buyer

  • View: hedger: “I already hold the asset; I want a floor.” speculator: “I want to bet on a selloff with limited risk.”
  • Pays premium now.
  • Happy if market falls.
  • Sad if market stays above K.

Put seller

  • View: “I’m happy to buy if it drops; pay me to place a limit order.”
  • Receives premium now.
  • Happy if market stays high or drifts.
  • Suffering if market collapses.

5. What does the option price “mean”?

The premium today is roughly:

“Discounted expected payoff under a risk-neutral world + risk/technical adjustments.”

Intuitively, it:

  • Increases with:
    • Time to expiry (more time for things to happen),
    • Volatility (bigger possible moves),
    • Interest rates (for calls via carry),
    • Moneyness (ITM has intrinsic value).
  • Decreases if:
    • Deep out of the money,
    • Almost at expiry with nothing happening.

Practitioner sentence:

“High premium = the market expects big moves or is scared of the tails.”


6. One simple picture (mentally)

If you visualise only two graphs, you’re 80% there:

  1. Long call P&L at expiry (vs STS_T):
    • Flat negative (−premium) until STS_T reaches about K+premiumK + \text{premium},
    • Then an upward line with slope +1.
  2. Long put P&L at expiry:
    • Flat negative (−premium) above about K−premiumK - \text{premium},
    • Then line with slope −1 as price falls.

Everything else (delta, gamma, parity) is just different ways of talking about those two shapes.


7. How to sound relaxed when talking about options

You can summarise like this in an interview:

  • “A call is a right to buy; a put is a right to sell. The buyer pays a premium and gets convex exposure; the seller receives the premium and is short convexity. At expiry, a long call pays (ST−K)+(S_T - K)^+, a long put pays (K−ST)+(K - S_T)^+. The option price is what the market charges today to transfer that upside or downside risk.”

If you want, next I can give you one tiny code cell that plots the four basic payoff shapes (long/short call/put) so you can see them and memorise the picture.


Python Simulation

The following Python snippet is a small, self-contained example for this topic. It is not a production pricer. It is a didactic toy that isolates the core mechanism we just discussed, so you can see the inputs, the transformation, and the resulting output in one place. Treat it as a sanity check and a template: the structure mirrors how we would implement the same idea in a real library, but with simplified assumptions and minimal plumbing.


import numpy as np
from math import log, sqrt, exp, erf

# --- 1. Black–Scholes helper for intuition (no dividends, European options) ---

def norm_cdf(x: float) -> float:
    """Standard normal CDF via erf (no external dependencies)."""
    return 0.5 * (1.0 + erf(x / sqrt(2.0)))

def bs_call_put(S, K, r, sigma, T):
    """
    Returns (call, put) prices under Black–Scholes.
    This is only to illustrate parity and monotonicity numerically.
    """
    if T <= 0 or sigma <= 0:
        # Immediate expiry: pure intrinsic value
        call = max(S - K, 0.0)
        put = max(K - S, 0.0)
        return call, put

    d1 = (log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * sqrt(T))
    d2 = d1 - sigma * sqrt(T)

    Nd1 = norm_cdf(d1)
    Nd2 = norm_cdf(d2)
    Nd1_m = norm_cdf(-d1)
    Nd2_m = norm_cdf(-d2)

    df = exp(-r * T)
    call = S * Nd1 - K * df * Nd2
    put = K * df * Nd2_m - S * Nd1_m
    return call, put

S0 = 100.0
K  = 100.0
r  = 0.05
T  = 1.0
sigma = 0.3

C, P = bs_call_put(S0, K, r, sigma, T)
df = exp(-r * T)

print(f"S0={S0}, K={K}, r={r}, T={T}, sigma={sigma}")
print(f"Call (C): {C:.4f}")
print(f"Put  (P): {P:.4f}")

# --- 2. Check put–call parity numerically ---

lhs = C - P
rhs = S0 - K * df
print("\nPut–call parity check:")
print(f"  C - P = {lhs:.6f}")
print(f"  S0 - K*df = {rhs:.6f}")
print(f"  Difference = {lhs - rhs:.6e}")


# --- 3. Check no-arbitrage bounds for this option ---

lower_C = max(0.0, S0 - K * df)
upper_C = S0
lower_P = max(0.0, K * df - S0)
upper_P = K * df

print("\nCall bounds:")
print(f"  Lower bound: {lower_C:.4f}")
print(f"  Actual C:    {C:.4f}")
print(f"  Upper bound: {upper_C:.4f}")

print("\nPut bounds:")
print(f"  Lower bound: {lower_P:.4f}")
print(f"  Actual P:    {P:.4f}")
print(f"  Upper bound: {upper_P:.4f}")


# --- 4. Monotonicity in strike: C(K) should decrease, P(K) should increase ---

Ks = np.linspace(60, 140, 9)
calls = []
puts = []
for K_i in Ks:
    C_i, P_i = bs_call_put(S0, K_i, r, sigma, T)
    calls.append(C_i)
    puts.append(P_i)

calls = np.array(calls)
puts = np.array(puts)

print("\nStrike grid:", Ks)
print("Calls C(K): ", np.round(calls, 4))
print("Puts  P(K): ", np.round(puts, 4))

print("\nDifferences C(K_{i+1}) - C(K_i) (should be <= 0):")
print(np.round(np.diff(calls), 4))

print("Differences P(K_{i+1}) - P(K_i) (should be >= 0):")
print(np.round(np.diff(puts), 4))


# --- 5. Example: introduce a fake market violation of parity and compute arbitrage payoff ---

C_mkt = C + 2.0   # artificially expensive call
P_mkt = P         # fair put (for illustration)

lhs_mkt = C_mkt - P_mkt
rhs_true = S0 - K * df
print("\nFAKE MARKET:")
print(f"  C_mkt - P_mkt = {lhs_mkt:.4f} vs theoretical {rhs_true:.4f}")

# Standard arbitrage combo when C - P is TOO HIGH:
# Short call, long put, long bond PV(K), short stock.
# We'll show initial CF and payoff at maturity as a function of S_T.

def arbitrage_payoff(ST):
    # At t=0: cash in = C_mkt (short call), cash out = P_mkt (long put),
    #         + pay PV(K) for the bond, + receive S0 from short stock.
    # We just look at *payoff at T*; initial cash we compute separately.
    # At T: payoff from each leg:
    call_payoff = -max(ST - K, 0.0)      # short call
    put_payoff  = max(K - ST, 0.0)       # long put
    bond_payoff = K                      # long bond
    stock_payoff = -ST                   # short stock
    return call_payoff + put_payoff + bond_payoff + stock_payoff

cf0 = -C_mkt + P_mkt + K * df - S0  # this is negative if we RECEIVE money (our sign convention)
print(f"\nInitial CF (our P&L at t=0, positive = we receive): {-cf0:.4f}")

ST_grid = np.linspace(40, 160, 7)
payoffs_T = [arbitrage_payoff(ST) for ST in ST_grid]
print("Arbitrage payoff at T for different S_T (should be ~0 everywhere):")
for ST, pf in zip(ST_grid, payoffs_T):
    print(f"  S_T={ST:6.1f} -> payoff at T = {pf:6.4f}")