Skip to content

Commit

Permalink
Merge pull request #15 from primitivefinance/feat/premium-approx
Browse files Browse the repository at this point in the history
feat(bs-approx): uses cdf approximation for bs formula
  • Loading branch information
Alexangelj authored Oct 16, 2021
2 parents 9796474 + 0067014 commit 276c532
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 12 deletions.
41 changes: 38 additions & 3 deletions src/BlackScholes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getCDFSolidity } from '.'
import { std_n_cdf } from './CumulativeNormalDistribution'

/**
Expand All @@ -10,7 +11,7 @@ export function moneyness(strike: number, spot: number): number {
}

/**
* @notice Calculates a common expression
* @notice Calculates a common equation
* @param sigma Volatility as a float
* @param tau Time until expiry, in years, as a float
* @returns volatilty * sqrt(tau)
Expand Down Expand Up @@ -60,6 +61,23 @@ export function callDelta(strike: number, sigma: number, tau: number, spot: numb
return delta
}

/**
* @notice D1 = (Log(spot / strike) + sigma^2 / 2 * tau) / (sigma * sqrt(tau))
* D2 = D1 - sigma * sqrt(tau)
* @returns D1 and D2, auxiliary variables of the black-scholes formula
*/
export function getD1AndD2(
strike: number,
sigma: number,
tau: number,
spot: number,
rate: number = 0
): { d1: number; d2: number } {
const d1 = calculateD1(strike, sigma, tau, spot, rate)
const d2 = d1 - getProportionalVol(sigma, tau)
return { d1, d2 }
}

/**
* @param strike Strike price of option, as a float
* @param sigma Implied volatility of option, as a float
Expand All @@ -69,7 +87,24 @@ export function callDelta(strike: number, sigma: number, tau: number, spot: numb
* @returns Black-Scholes price of call option with parameters
*/
export function callPremium(strike: number, sigma: number, tau: number, spot: number, rate: number = 0): number {
const d1 = calculateD1(strike, sigma, tau, spot, rate)
const d2 = d1 - getProportionalVol(sigma, tau)
const { d1, d2 } = getD1AndD2(strike, sigma, tau, spot, rate ?? rate)
return Math.max(0, std_n_cdf(d1) * spot - std_n_cdf(d2) * strike * Math.exp(-tau * rate))
}

/**
* @notice Uses solidity CDF approximation
* @dev Use to determine the theoretical theta which can accrue to a position
* @returns Premium of a call option using the solidity CDF approximation formula
*/
export function callPremiumApproximation(
strike: number,
sigma: number,
tau: number,
spot: number,
rate: number = 0
): number {
const { d1, d2 } = getD1AndD2(strike, sigma, tau, spot, rate ?? rate)
const cdfD1 = getCDFSolidity(d1)
const cdfD2 = getCDFSolidity(d2)
return Math.max(0, cdfD1 * spot - cdfD2 * strike * Math.exp(-tau * rate))
}
49 changes: 40 additions & 9 deletions test/blackScholes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,23 @@ describe('Black Scholes', () => {
})

describe('calculateD1', () => {
it('return 0 if tau is 0', () => {
const tau = 0
expect(math.calculateD1(1, 1, tau, 1)).toEqual(0)
it('return 0.5 if tau is 1', () => {
const tau = 1
expect(math.calculateD1(1, 1, tau, 1)).toEqual(0.5)
})
})

describe('calculateD2', () => {
it('return 0 if tau is 0', () => {
const tau = 0
expect(math.calculateD2(1, 1, tau, 1)).toEqual(0)
it('return -0.5 if tau is 1', () => {
const tau = 1
expect(math.calculateD2(1, 1, tau, 1)).toEqual(-0.5)
})
})

describe('callDelta', () => {
it('return 0.5 if tau is 0', () => {
const tau = 0
expect(math.callDelta(1, 1, tau, 1)).toBeCloseTo(0.5)
it('return 0.6914624612740131036377 if tau is 1', () => {
const tau = 1
expect(math.callDelta(1, 1, tau, 1)).toBeCloseTo(0.6914624612740131036377)
})
})

Expand All @@ -50,5 +50,36 @@ describe('Black Scholes', () => {
const tau = 0
expect(math.callPremium(1, 1, tau, 1)).toEqual(0)
})

it('return 0.3829249225480262072754 if tau is 1', () => {
const tau = 1
expect(math.callPremium(1, 1, tau, 1)).toBeCloseTo(0.3829249225480262072754)
})
})

describe('approximations', () => {
it('call premium using solidity CDF approximation for 0 tau', () => {
const tau = 0
expect(math.callPremiumApproximation(1, 1, tau, 1)).toBeCloseTo(math.callPremium(1, 1, tau, 1), 6)
})

it('call premium using solidity CDF approximation for 1 year tau', () => {
const tau = 1
expect(math.callPremiumApproximation(1, 1, tau, 1)).toBeCloseTo(math.callPremium(1, 1, tau, 1), 6)
})

it('premium for ITM', () => {
const tau = 1
const spot = 2
const strike = 1
expect(math.callPremiumApproximation(strike, 1, tau, spot)).toBeCloseTo(math.callPremium(strike, 1, tau, spot), 6)
})

it('premium for OTM', () => {
const tau = 1
const spot = 1
const strike = 2
expect(math.callPremiumApproximation(strike, 1, tau, spot)).toBeCloseTo(math.callPremium(strike, 1, tau, spot), 6)
})
})
})

0 comments on commit 276c532

Please sign in to comment.