Skip to content

About

Monte Carlo pricing of autocalls, reverse convertibles, shark notes and credit-linked notes, with autocall probabilities, coupon distributions and greeks

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Structured products pricer

Monte Carlo pricing of the payoffs that make up most of a retail structured products book: Phoenix and Athena autocalls, reverse convertibles, shark notes and credit-linked notes. Everything is priced from simulated paths, with the analytics a salesperson needs in his head on top: when the note is likely to be called, how much coupon it should actually pay, and how the capital behaves in the tail.

I wrote this after two internships on structured products desks (Natixis CIB, Kepler Cheuvreux). On the desk I priced these payoffs on the bank's own tools, which give you a number and not a reason; this is my own implementation of the same structures, built to see what sits behind the number.

What it prices

Product Payoff
Autocallable Phoenix: conditional coupon with memory, early redemption at par, capital barrier at maturity. Set the coupon to zero and add a redemption_premium to get an Athena.
ReverseConvertible Unconditional coupon, capital indexed to the underlying below the barrier. Long a bond, short a down-and-in put.
SharkNote Capital guaranteed, participation in the upside that knocks out if the barrier is touched, fixed rebate if it is.
CreditLinkedNote Coupon note on a reference entity, default times from a constant-intensity model, recovery paid on the default date.

Example output

python examples/compare_products.py, spot 100, vol 22%, rate 3%, dividend yield 2.5%, 200k antithetic paths:

Product                     Price      SE   Coupon    Life
----------------------------------------------------------
Phoenix autocall 5Y        99.239   0.035     7.60    1.38
Athena autocall 5Y         94.335   0.049     0.00    2.63
Reverse convertible 1Y    100.982   0.022     9.00    1.00
Shark note 3Y              93.421   0.006     2.22    3.00
Credit-linked note 4Y      93.469   0.040    11.23    4.00

Price and coupon are in points of nominal, life is the expected time to redemption in years.

python examples/price_phoenix.py breaks one note down in full. On the 5Y Phoenix above, struck at 100 with a 70% coupon barrier, a 100% autocall barrier and a 60% capital barrier:

  • 48.2% of paths are called on the very first quarterly observation, and 84.2% are called at some point before maturity
  • expected life is 1.38 years against a 5 year legal maturity
  • average coupon collected is 7.60 points, but the median is 3.50: the mean is pulled up by the 16% of paths that survive and keep paying
  • 8.6% of paths end with a capital loss, and when that happens the average redemption is 44.9 points

That last pair of numbers is the whole point of the product. The note looks like a 7% coupon and behaves like one most of the time, but the downside is concentrated and severe.

Autocall redemption profile

Autocall value against the underlying

The value profile is concave and flattens out just above par: once the spot is comfortably above the autocall barrier the note is going to be called at 100, so there is nothing left to gain. That is the same thing as saying the investor is long delta, short gamma and short vega, which is what the engine reports:

delta    0.2000    per 1% move in the spot
gamma   -0.0266
vega    -0.4501    per volatility point
rho     -0.0049    per basis point

Design notes

Barriers are pinned to the strike level, not to the simulated spot. This sounds obvious but it is easy to get wrong: if you compute performance as path / path[0], the payoff becomes homogeneous of degree zero in the spot, every barrier follows the underlying under a bump, and the note shows a delta of exactly zero. Products therefore carry an initial_level, and price() refuses to run until it is set. strike_at_inception(product, market) sets it to today's spot; to price a live note, set it to the level actually fixed at inception and move market.spot to where the underlying trades now.

Maturity has three outcomes, not two. Above the final trigger the note redeems like an early call, accrued premium included. Between the trigger and the capital barrier the investor gets their money back and nothing more. Below it, capital is indexed to the underlying. Collapsing the first two cases, and paying an Athena's accrued premium all the way down to the protection barrier, is worth about five points on a five year note: 15% of paths finish between the two barriers. final_trigger defaults to the autocall barrier, which is what a standard term sheet says, and can be lowered explicitly for the structures that do set a separate final coupon barrier.

Coupon and redemption legs are kept separate in the Cashflows record, so the income and the capital risk can be measured independently without trying to reverse-engineer one out of the other.

Antithetic sampling is on by default. Since that makes paths come in dependent pairs, the standard error is computed on the pair averages rather than on the individual paths, which would understate it.

Greeks reuse the random numbers across the bumped and unbumped runs. Without that, the Monte Carlo noise swamps the difference and gamma is unusable.

Validation

The tests check the engine against cases where the answer is known:

  • E[S_T] = S_0 e^{(r-q)T}, so the simulated forward is a martingale
  • a reverse convertible with its barrier at 100% is exactly a zero-coupon bond plus its coupon minus a vanilla put, which Black-Scholes prices in closed form; the Monte Carlo agrees within four standard errors
  • a credit-linked note with zero recovery is the survival-weighted sum of its cashflows, again matched within four standard errors
  • an autocall with its barrier at zero is called on the first date with probability one and must price to (100 + coupon) * exp(-r * t_1)
  • no Athena path redeems strictly between par and par plus one premium, which is the test that pins the maturity split
  • a Phoenix is unchanged by final_trigger, since it has no premium to pay
  • memory pays two coupons at once on a hand-built path that misses the barrier and then meets it
  • capital on a shark note is never below par, on any path
  • antithetic sampling reduces the standard error
  • raising the coupon barrier lowers the price
  • delta is positive, gamma and vega negative
  • pricing an unstruck product raises rather than silently returning a zero delta
python -m pytest tests/ -q
16 passed

Limitations

The model is flat Black-Scholes: one volatility, one rate, a continuous dividend yield. That is enough to get the structure of the payoff right and to reason about the risk, but it is not what a desk quotes on. In particular a real autocall is very sensitive to the volatility skew, because the capital barrier sits far out of the money, and flat vol will systematically underestimate how expensive that downside is. Discrete dividends matter too on single stocks. A local volatility or Heston path generator would slot in behind gbm_paths without touching the payoffs.

Barrier monitoring is discrete, on the simulation grid. For the shark note, whose barrier is continuous in the term sheet, pass a daily grid (n_steps=252*T) or the note will price too high.

Running it

pip install -r requirements.txt
python examples/price_phoenix.py
python examples/compare_products.py
python -m pytest tests/ -q

Licence

MIT.

About

Monte Carlo pricing of autocalls, reverse convertibles, shark notes and credit-linked notes, with autocall probabilities, coupon distributions and greeks

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages