Put–Call Parity Checks for Volatility Surface Construction
Summary
The document discusses how put–call parity constrains call and put prices and informs implied-volatility surface construction. With a consistent forward price, discounting convention, and contemporaneous market inputs, parity links call and put prices at the same strike and expiry. A discrepancy between their implied volatilities may therefore indicate inconsistent inputs or, if the forward is genuinely fixed and prices are actionable, a parity trade. One response describes combining a put with the forward exposure to create a synthetic call and comparing it with the market call.
When the forward is unknown, paired call and put prices can help infer it; the resulting forward is then useful for moneyness coordinates. Dividend yield, dividend adjustments, and repo assumptions may also need calibration to reconcile quotes. The excerpt offers several practical explanations rather than a single definitive convention, and it does not quantify transaction costs, bid–ask spreads, or execution constraints. Surface construction should use aligned observations and consistent assumptions, since stale or mismatched quotes can create misleading parity gaps.
Key ideas
- Put–call parity links call and put prices at a shared strike and expiry through the forward and discounting assumptions.
- If the forward is unknown, paired option prices can be used to infer it.
- Dividend and repo assumptions may need adjustment when reconciling call and put implied volatilities.
- A parity discrepancy with a known forward can imply a synthetic-option arbitrage trade, subject to market frictions.
- Contemporaneous option quotes help avoid false parity signals from mismatched observations.
Tags
Full text
# How to apply put-call parity in volatility surface construction?
# How to apply put-call parity in volatility surface construction?
How to make the volatility surface free of put-call parity arbitrage? If I bootstrapped the implied vol from a call price and plugged it into the BS model to have a put price, what if it violates the put-call parity?
It looks like I should adjust the implied volatility to make the put-call parity hold. But is it ok for the difference between the market price and the model price using our implied volatility?
I wonder how this situation is handled in practice, and if it violates the parity, how to arbitrage? An example would be much appreciated.
## Answer by Arshdeep (score 0)
https://quant.stackexchange.com/a/76139
That's a great question. It seems on first glance a bit freakish that pricing models are automatically able to take care of such arbitrage possibilities.
There are 2 ways to understand this:
- Models aren't really original, they're equivalent to solving PDE's (Feynman Kac theorem). The content of the PDE is basically if you get the volatility of the hedging instrument correct (equal to its average realized vol), you will prevent arbitrage.
So if you price all instruments with the same estimate of volatility of the underlying asset, you have no chance of creating an arbitrage as all instruments are priced fairly.
P.S. Even if your vol estimate is wrong, but it is consistent across pricing calls and puts, you will prevent call put parity arbitrage (but there may be money to be made in gamma trading).
- You can also understand this as expectations are additive. So $E(Put)=E(K-C+S)$ simply because $Put=K-C+S$ (no wonder prices are expectations!). So as long as expectations are taken w.r.t the same density, the parity is automatically taken care of.
## Answer by T123 (score 0)
https://quant.stackexchange.com/a/77705
Put-Call parity is usefull for implied vola construction in the following way: You know from put-call parity that the vola of the call should equal the vola of the put. As you are trying to determine the vola surface, you need to adjust other parameters to make sure both volas are equal. There are two ways (or basically three) how you could accomplish that:
- Adjust the implied dividend yield manually. Increasing the dividend yield impacts the price of a put differently from that of a call option. Thus, you can finetune the implied volas.
- Adjust your tax factor div*(1-tax) to get your dividends right: Traders usually apply a tax factor to correct dividends if they are too much off those dividend estimates that you can see on Bloomberg or your own models. This seems ok as you don't get full dividends once its paid off.
- In cases where your tax factor is higher than 50% you need to find other smart ways, Tax factors above 100% are also problematic: Here you can use the repo-rate to adjust your option prices accordingly. Sorry, i'm at work and don't have too much time - if you find some typos, please be so kind and edit my post, thank you Thomas
## Answer by QuantCalc.net (score 0)
https://quant.stackexchange.com/a/85362
There are two primary scenarios to consider:
Unknown Forward Price:
Typically, the forward price of the underlying is not directly observable. When bootstrapping implied volatility from call options, the forward price ($F = S_0 e^{(r-q)T}$) depends on an unknown convenience yield ($q$). To ensure consistency, you must solve for $q$ such that the implied volatilities for both calls and puts are equalized.
Known Forward Price:
If the forward price is fixed, any discrepancy between call and put implied volatilities represents an arbitrage opportunity. For instance, if call implied volatility is higher, you can execute a "sell-call, buy-synthetic-call" strategy by utilizing the put-call parity relationship: $C_{syn} = P + e^{-rT}(F - K)$.
## Answer by Marwin Steiner (score 0)
https://quant.stackexchange.com/a/85442
You need contemporaneous put and call prices, ensuring you don't introduce lookahead bias, so you need to ensure you only forward fill.
Here's a sample Python implementation:
```
def calculate_implied_forward(
spot: pd.Series,
tte: pd.Series,
r: float,
strike: pd.Series,
call_mid: pd.Series,
put_mid: pd.Series
) -> pd.Series:
"""
Calculate the implied forward price using put-call parity.
Parameters
----------
spot : float
Current underlying spot price.
tte : float
Time to expiry in years.
r : float
Risk-free rate (annualized, continuously compounded).
strike : float
Option strike price (ATM strike recommended).
call_mid : float
Mid-price of the call option at the strike.
put_mid : float
Mid-price of the put option at the strike.
Returns
-------
implied_forward : float or np.nan
The implied forward price, or NaN if inputs are invalid.
"""
spot = spot.astype(float)
tte = tte.astype(float)
strike = strike.astype(float)
call_mid = call_mid.astype(float)
put_mid = put_mid.astype(float)
# Put-call parity: F = K + exp(r t) * (C - P)
implied_forward = strike + np.exp(r * tte) * (call_mid - put_mid)
# Replace invalids with NaN
implied_forward = implied_forward.where(
(spot > 0) & (tte > 0) & (strike > 0) & call_mid.notna() & put_mid.notna(),
np.nan
)
return implied_forward
```
You need the implied forward to compute moneyness, which you can then transform into, say, log moneyness as one of the axes on your surface.Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.