From 7bce73551004bdd63cde46d7fa25223bdc1d30b8 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 10 Oct 2021 11:57:21 -0700 Subject: [PATCH 1/3] feat(solidity-approx): finishes sol approximations for inverse cdf --- src/CumulativeNormalDistribution.ts | 83 +++++++++++++++++++++++------ src/ReplicationMath.ts | 10 ++++ 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/src/CumulativeNormalDistribution.ts b/src/CumulativeNormalDistribution.ts index af89694..ca6a9de 100644 --- a/src/CumulativeNormalDistribution.ts +++ b/src/CumulativeNormalDistribution.ts @@ -5,7 +5,7 @@ import gaussian from 'gaussian' * source: https://github.com/errcw/gaussian/blob/master/lib/gaussian.js * @returns CDF of x */ -export const std_n_cdf = x => { +export function std_n_cdf(x) { return gaussian(0, 1).cdf(x) } @@ -14,7 +14,7 @@ export const std_n_cdf = x => { * source: https://github.com/errcw/gaussian/blob/master/lib/gaussian.js * @returns CDF of x */ -export const std_n_pdf = x => { +export function std_n_pdf(x) { return gaussian(0, 1).pdf(x) } @@ -23,7 +23,7 @@ export const std_n_pdf = x => { * source: https://github.com/errcw/gaussian/blob/master/lib/gaussian.js * @returns CDF^-1 of x */ -export const inverse_std_n_cdf = x => { +export function inverse_std_n_cdf(x) { return gaussian(0, 1).ppf(x) } @@ -31,7 +31,7 @@ export const inverse_std_n_cdf = x => { * uses source: https://github.com/errcw/gaussian/blob/master/lib/gaussian.js * @returns CDF(CDF(x)^-1)^-1 */ -export const quantilePrime = x => { +export function quantilePrime(x) { if (x > 1 || x < 0) return NaN return gaussian(0, 1).pdf(inverse_std_n_cdf(x)) ** -1 } @@ -41,7 +41,7 @@ export const quantilePrime = x => { * source: https://stackoverflow.com/questions/14846767/std-normal-cdf-normal-cdf-or-error-function * @returns Cumulative distribution function */ -const solidityCDF = (x, mean, variance) => { +function solidityCDF(x, mean, variance) { return 0.5 * (1 + solidityErf((x - mean) / Math.sqrt(2 * variance))) } @@ -50,7 +50,7 @@ const solidityCDF = (x, mean, variance) => { * source: https://stackoverflow.com/questions/14846767/std-normal-cdf-normal-cdf-or-error-function * @returns error function of x */ -const solidityErf = x => { +function solidityErf(x) { // save the sign of x var sign = x >= 0 ? 1 : -1 x = Math.abs(x) @@ -74,26 +74,77 @@ const solidityErf = x => { * source: https://stackoverflow.com/questions/14846767/std-normal-cdf-normal-cdf-or-error-function * @returns standard normal cumulative distribution function of x */ -export const solidityNormalCDF = x => { +export function getCDFSolidity(x) { return solidityCDF(x, 0, 1) } +export const HIGH_TAIL = 0.975 +export const LOW_TAIL = 0.025 + /** - * @notice Used in solidity smart contracts - * source: https://arxiv.org/pdf/1002.0567.pdf + * @notice Returns the inverse CDF, or quantile function of `p`. + * Source: https://arxiv.org/pdf/1002.0567.pdf + * Maximum error of central region is 1.16x10−4 + * @dev Used in solidity smart contracts * @returns standard normal invervse cumulative distribution (quantile) function of x */ -export const solidityInverseNormalCDF = x => { - const q = x - 0.5 +export function getInverseCDFSolidity(p) { + if (p >= 1 || p <= 0) return NaN + if (p <= HIGH_TAIL && p >= LOW_TAIL) { + return centralInverseCDFSolidity(p) + } else if (p < LOW_TAIL) { + return tailInverseCDFSolidity(p) + } else { + return -tailInverseCDFSolidity(1 - p) + } +} + +/** + * @dev Maximum error: 1.16x10−4 + * @param p Probability to find inverse cdf of + * @returns Inverse CDF around the central area of 0.025 <= p <= 0.975 + */ +export function centralInverseCDFSolidity(p) { + const q = p - 0.5 const r = Math.pow(q, 2) - const a0 = 0.151015506 - const a1 = -0.530357263 - const a2 = 1.365020123 - const b0 = 0.132089632 - const b1 = -0.760732499 + const a0 = 0.151015505647689 + const a1 = -0.5303572634357367 + const a2 = 1.365020122861334 + const b0 = 0.132089632343748 + const b1 = -0.7607324991323768 const numerator = a1 * r + a0 const denominator = Math.pow(r, 2) + b1 * r + b0 const input = a2 + numerator / denominator const result = q * input return result } + +/** + * @dev Maximum error: 2.458x10-5 + * @param p Probability to find inverse cdf of + * @returns Inverse CDF of the tail, defined for p < 0.0465, used with p < 0.025 + */ +export function tailInverseCDFSolidity(p) { + const r = Math.sqrt(Math.log(1 / Math.pow(p, 2))) + const c0 = 16.896201479841517652 + const c1 = -2.793522347562718412 + const c2 = -8.731478129786263127 + const c3 = -1.000182518730158122 + const c0_D = 16.682320830719986527 + const c1_D = 4.120411523939115059 + const c2_D = 0.029814187308200211 + const D0 = 7.173787663925508066 + const D1 = 8.759693508958633869 + + const numerator = c1_D * r + c0_D + const denominator = Math.pow(r, 2) + D1 * r + D0 + const quotient = numerator / denominator + const result = c3 * r + c2_D + quotient + + const secondary = (c3 * Math.pow(r, 3) + c2 * Math.pow(r, 2) + c1 * r + c0) / Math.pow(r, 2) + D1 * r + D0 + if (result !== secondary) { + console.log(`Result ${result} does not match secondary ${secondary}`) + return result + } + return result +} diff --git a/src/ReplicationMath.ts b/src/ReplicationMath.ts index 7d22598..6168585 100644 --- a/src/ReplicationMath.ts +++ b/src/ReplicationMath.ts @@ -1,6 +1,16 @@ import { inverse_std_n_cdf, std_n_cdf, std_n_pdf, quantilePrime } from './CumulativeNormalDistribution' import { getProportionalVol } from './BlackScholes' +// ===== Approximations as in the Solidity implementation ===== + +export function getStableGivenRiskyApproximation() {} + +export function getRiskyGivenStableApproximation() {} + +export function getInvariantApproximation() {} + +// ===== Precise math ===== + /** * @notice Core math trading function of the AMM to calculate the stable reserve using risky * @param reserveRisky Pool's reserve of risky tokens per unit of liquidity From 89c22b4f66631349a2724a0ab1bc10e2432f2fb5 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 10 Oct 2021 14:55:16 -0700 Subject: [PATCH 2/3] -feat(replication-approx): adds proper formulas using sol approximations --- src/CumulativeNormalDistribution.ts | 10 +- src/ReplicationMath.ts | 196 +++++++++++++++++++--- src/index.ts | 1 + src/utils.ts | 30 ++++ test/cumulativeNormalDistribution.test.ts | 38 ++++- test/replicationMath.test.ts | 43 +++++ 6 files changed, 277 insertions(+), 41 deletions(-) create mode 100644 src/utils.ts diff --git a/src/CumulativeNormalDistribution.ts b/src/CumulativeNormalDistribution.ts index ca6a9de..4a7f203 100644 --- a/src/CumulativeNormalDistribution.ts +++ b/src/CumulativeNormalDistribution.ts @@ -126,9 +126,9 @@ export function centralInverseCDFSolidity(p) { */ export function tailInverseCDFSolidity(p) { const r = Math.sqrt(Math.log(1 / Math.pow(p, 2))) - const c0 = 16.896201479841517652 + /* const c0 = 16.896201479841517652 const c1 = -2.793522347562718412 - const c2 = -8.731478129786263127 + const c2 = -8.731478129786263127 */ const c3 = -1.000182518730158122 const c0_D = 16.682320830719986527 const c1_D = 4.120411523939115059 @@ -140,11 +140,5 @@ export function tailInverseCDFSolidity(p) { const denominator = Math.pow(r, 2) + D1 * r + D0 const quotient = numerator / denominator const result = c3 * r + c2_D + quotient - - const secondary = (c3 * Math.pow(r, 3) + c2 * Math.pow(r, 2) + c1 * r + c0) / Math.pow(r, 2) + D1 * r + D0 - if (result !== secondary) { - console.log(`Result ${result} does not match secondary ${secondary}`) - return result - } return result } diff --git a/src/ReplicationMath.ts b/src/ReplicationMath.ts index 6168585..a6ae99e 100644 --- a/src/ReplicationMath.ts +++ b/src/ReplicationMath.ts @@ -1,24 +1,96 @@ -import { inverse_std_n_cdf, std_n_cdf, std_n_pdf, quantilePrime } from './CumulativeNormalDistribution' +import { + inverse_std_n_cdf, + std_n_cdf, + std_n_pdf, + quantilePrime, + getInverseCDFSolidity, + getCDFSolidity, +} from './CumulativeNormalDistribution' import { getProportionalVol } from './BlackScholes' +import { bisection, MAX_PRECISION } from './utils' // ===== Approximations as in the Solidity implementation ===== +/** + * @notice Forward trading function to calculate the risky reserve given a stable reserve which has a 0 invariant + * @dev Uses the same approximations as in the solidity contract + * @param reserveRisky Pool's reserve of risky tokens per unit of liquidity + * @param strike Price point that defines complete stable token composition of the pool + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years + * @param invariantLast Previous invariant with the same `tau` input as the parameter `tau` + * @returns = K * Φ(Φ^-1(1 - reserveRisky) - σ*sqrt(T - t)) + invariant + */ +export function getStableGivenRiskyApproximation( + reserveRisky: number, + strike: number, + sigma: number, + tau: number, + invariantLast: number = 0 +): number { + if (reserveRisky >= 1 || reserveRisky <= 0) return 0 + const K = strike + const vol = getProportionalVol(sigma, tau) + if (vol <= 0) return 0 + const inverseInput: number = 1 - reserveRisky + const phi: number = getInverseCDFSolidity(inverseInput) + const input = phi - vol + const reserveStable = K * getCDFSolidity(input) + invariantLast + return reserveStable +} -export function getStableGivenRiskyApproximation() {} +/** + * @notice Using approximations in the forward function `getStableGivenRisky` will cause this inverse function + * to not be exactly equal. Therefore, a numerical bisection method is used to find the exact amount based on the function which + * uses the approximations. + * @dev Uses a bisection to find the `reserveRisky` which sets the invariant to 0 given a `reserveStable` + */ +export function getRiskyGivenStableApproximation( + reserveStable: number, + strike: number, + sigma: number, + tau: number +): number { + const func = reserveRisky => getInvariantApproximation(reserveRisky, reserveStable, strike, sigma, tau) -export function getRiskyGivenStableApproximation() {} + const MAX_RISKY = 1 + let optimalDeltaOut: number + if (Math.sign(func(MAX_PRECISION)) != Math.sign(func(MAX_RISKY - MAX_PRECISION))) { + optimalDeltaOut = bisection(func, MAX_PRECISION, MAX_RISKY - MAX_PRECISION) + } else { + optimalDeltaOut = MAX_RISKY + } -export function getInvariantApproximation() {} + return optimalDeltaOut +} + +/** + * @param reserveRisky Pool's reserve of risky tokens per unit of liquidity + * @param reserveStable Pool's reserve of stable tokens per unit of liquidity + * @param strike Price point that defines complete stable token composition of the pool + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years + * @returns Invariant = Reserve stable - getStableGivenRiskyApproximation(...) + */ +export function getInvariantApproximation( + reserveRisky: number, + reserveStable: number, + strike: number, + sigma: number, + tau: number +): number { + return reserveStable - getStableGivenRiskyApproximation(reserveRisky, strike, sigma, tau) +} // ===== Precise math ===== /** - * @notice Core math trading function of the AMM to calculate the stable reserve using risky + * @notice Forward trading function to calculate the risky reserve given a stable reserve which has a 0 invariant * @param reserveRisky Pool's reserve of risky tokens per unit of liquidity * @param strike Price point that defines complete stable token composition of the pool - * @param sigma Implied volatility of the pool - * @param tau Time until expiry + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years * @param invariantLast Previous invariant with the same `tau` input as the parameter `tau` - * @returns Covered Call AMM black-scholes trading function + * @returns = K * Φ(Φ^-1(1 - reserveRisky) - σ*sqrt(T - t)) + invariant */ export function getStableGivenRisky( reserveRisky: number, @@ -38,13 +110,13 @@ export function getStableGivenRisky( } /** - * @notice Core math trading function of the AMM to calculate the risky reserve using stable + * @notice Inverse trading function to calculate the stable reserve given a risky reserve which has a 0 invariant * @param reserveStable Pool's reserve of stable tokens per unit of liquidity * @param strike Price point that defines complete stable token composition of the pool - * @param sigma Implied volatility of the pool - * @param tau Time until expiry + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years * @param invariantLast Previous invariant with the same `tau` input as the parameter `tau` - * @returns Covered Call AMM black-scholes inverse trading function + * @returns = 1 - Φ(Φ^-1((reserveStable - invariant) / K) + σ*sqrt(T - t)) */ export function getRiskyGivenStable( reserveStable: number, @@ -64,11 +136,11 @@ export function getRiskyGivenStable( } /** - * @param reserveRisky Pool's reserve of risky tokens - * @param reserveStable Pool's reserve of stable tokens + * @param reserveRisky Pool's reserve of risky tokens per unit of liquidity + * @param reserveStable Pool's reserve of stable tokens per unit of liquidity * @param strike Price point that defines complete stable token composition of the pool - * @param sigma Implied volatility of the pool - * @param tau Time until expiry + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years * @returns Invariant = Reserve stable - getStableGivenRisky(...) */ export function calcInvariant( @@ -84,31 +156,43 @@ export function calcInvariant( /** * @param reserveRisky Pool's reserve of risky tokens * @param strike Price point that defines complete stable token composition of the pool - * @param sigma Implied volatility of the pool - * @param tau Time until expiry + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years * @returns getStableGivenRisky(...) * pdf(ppf(1 - risky))^-1 */ export function getSpotPrice(reserveRisky: number, strike: number, sigma: number, tau: number): number { return getStableGivenRisky(reserveRisky, strike, sigma, tau) * quantilePrime(1 - reserveRisky) } +/** + * @param reserveRisky Pool's reserve of risky tokens + * @dev Uses the approximations in the solidity contract + * @param strike Price point that defines complete stable token composition of the pool + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years + * @returns getStableGivenRisky(...) * pdf(ppf(1 - risky))^-1 + */ +export function getSpotPriceApproximation(reserveRisky: number, strike: number, sigma: number, tau: number): number { + return getStableGivenRiskyApproximation(reserveRisky, strike, sigma, tau) * quantilePrime(1 - reserveRisky) +} + /** * @notice See https://arxiv.org/pdf/2012.08040.pdf * @param amountIn Amount of risky token to add to risky reserve * @param reserveRisky Pool's reserve of risky tokens * @param strike Price point that defines complete stable token composition of the pool - * @param sigma Implied volatility of the pool - * @param tau Time until expiry, in years + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years * @return Marginal price after a trade with size `amountIn` with the current reserves. */ -export const getMarginalPriceSwapRiskyIn = ( +export function getMarginalPriceSwapRiskyIn( amountIn: number, reserveRisky: number, strike: number, sigma: number, tau: number, fee: number -) => { +): number { if (!nonNegative(amountIn)) return 0 const gamma = 1 - fee const step0 = 1 - reserveRisky - gamma * amountIn @@ -125,11 +209,11 @@ export const getMarginalPriceSwapRiskyIn = ( * @param amountIn Amount of stable token to add to stable reserve * @param reserveStable Pool's reserve of stable tokens * @param strike Price point that defines complete stable token composition of the pool - * @param sigma Implied volatility of the pool - * @param tau Time until expiry, in years + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years * @return Marginal price after a trade with size `amountIn` with the current reserves. */ -export const getMarginalPriceSwapStableIn = ( +export function getMarginalPriceSwapStableIn( amountIn: number, invariant: number, reserveStable: number, @@ -137,7 +221,7 @@ export const getMarginalPriceSwapStableIn = ( sigma: number, tau: number, fee: number -) => { +): number { if (!nonNegative(amountIn)) return 0 const gamma = 1 - fee const step0 = (reserveStable + gamma * amountIn - invariant) / strike @@ -150,10 +234,68 @@ export const getMarginalPriceSwapStableIn = ( return 1 / step7 } +/** + * @notice See https://arxiv.org/pdf/2012.08040.pdf + * @param amountIn Amount of risky token to add to risky reserve + * @param reserveRisky Pool's reserve of risky tokens + * @param strike Price point that defines complete stable token composition of the pool + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years + * @return Marginal price after a trade with size `amountIn` with the current reserves. + */ +export function getMarginalPriceSwapRiskyInApproximation( + amountIn: number, + reserveRisky: number, + strike: number, + sigma: number, + tau: number, + fee: number +): number { + if (!nonNegative(amountIn)) return 0 + const gamma = 1 - fee + const step0 = 1 - reserveRisky - gamma * amountIn + const step1 = sigma * Math.sqrt(tau) + const step2 = quantilePrime(step0) + const step3 = gamma * strike + const step4 = getInverseCDFSolidity(step0) + const step5 = std_n_pdf(step4 - step1) + return step3 * step5 * step2 +} + +/** + * @notice See https://arxiv.org/pdf/2012.08040.pdf + * @param amountIn Amount of stable token to add to stable reserve + * @param reserveStable Pool's reserve of stable tokens + * @param strike Price point that defines complete stable token composition of the pool + * @param sigma Implied volatility of the pool as a decimal percentage + * @param tau Time until expiry in years + * @return Marginal price after a trade with size `amountIn` with the current reserves. + */ +export function getMarginalPriceSwapStableInApproximation( + amountIn: number, + invariant: number, + reserveStable: number, + strike: number, + sigma: number, + tau: number, + fee: number +): number { + if (!nonNegative(amountIn)) return 0 + const gamma = 1 - fee + const step0 = (reserveStable + gamma * amountIn - invariant) / strike + const step1 = sigma * Math.sqrt(tau) + const step3 = getInverseCDFSolidity(step0) + const step4 = std_n_pdf(step3 + step1) + const step5 = step0 * (1 / strike) + const step6 = quantilePrime(step5) + const step7 = gamma * step4 * step6 + return 1 / step7 +} + /** * @param x A number * @returns is x greater than or equal to 0? */ -export const nonNegative = (x: number): boolean => { +export function nonNegative(x: number): boolean { return x >= 0 } diff --git a/src/index.ts b/src/index.ts index dad1126..c57eb09 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ export * from './BlackScholes' export * from './CumulativeNormalDistribution' export * from './ReplicationMath' +export * from './utils' diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..8a9c363 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,30 @@ +export const EPSILON = 1e-3 +export const MAX_PRECISION = 1e-6 + +/** + * @notice source: https://www.geeksforgeeks.org/program-for-bisection-method/ + * This code is contributed by susmitakundugoaldanga. + * @param func Returns a value, run the bisection such that the return value is 0 + * @param a Left most point + * @param b Right most point + * @returns Root of function + */ +export function bisection(func, a, b) { + if (func(a) * func(b) >= 0) { + console.log('\n You have not assumed' + ' right a and b') + return + } + + let c = a + while (b - a >= EPSILON) { + // Find middle point + c = (a + b) / 2 + + // Check if middle point is root + if (func(c) == 0.0) break + // Decide the side to repeat the steps + else if (func(c) * func(a) < 0) b = c + else a = c + } + return c +} diff --git a/test/cumulativeNormalDistribution.test.ts b/test/cumulativeNormalDistribution.test.ts index d7ab39f..0668325 100644 --- a/test/cumulativeNormalDistribution.test.ts +++ b/test/cumulativeNormalDistribution.test.ts @@ -36,15 +36,41 @@ describe('Stats Math Library', () => { }) }) - describe('solidityNormalCDF', () => { - it('moneyness', () => { - expect(2).toEqual(2) + describe('solidity cdf', () => { + it('cdf of 0', () => { + expect(math.getCDFSolidity(0)).toBeCloseTo(0.5, maxError.cdf) + }) + + it('cdf of -1', () => { + expect(math.getCDFSolidity(-1)).toBeCloseTo(0.1586552539314570514148, maxError.cdf) + }) + + it('cdf of 1', () => { + expect(math.getCDFSolidity(1)).toBeCloseTo(0.8413447460685429485852, maxError.cdf) }) }) - describe('solidityInverseCDF', () => { - it('moneyness', () => { - expect(2).toEqual(2) + describe('solidity inverse cdf', () => { + it('inverseCDF of 0.5', () => { + expect(math.getInverseCDFSolidity(0.5)).toBeCloseTo(0, maxError.centralInverseCDF) + }) + + it('inverseCDF of 0.7', () => { + expect(math.getInverseCDFSolidity(0.7)).toBeCloseTo(0.5244005127080407840383, maxError.centralInverseCDF) + }) + + it('inverseCDF high tail', () => { + expect(math.getInverseCDFSolidity(0.98)).toBeCloseTo(2.053748910631823052937, maxError.tailInverseCDF) + }) + + it('inverseCDF low tail', () => { + expect(math.getInverseCDFSolidity(0.01)).toBeCloseTo(-2.32634787404084110089, maxError.tailInverseCDF) }) }) }) + +export const maxError = { + cdf: 3.15e-3, + centralInverseCDF: 1.16e-4, + tailInverseCDF: 2.458e-5, +} diff --git a/test/replicationMath.test.ts b/test/replicationMath.test.ts index d8683fa..ff0d7f3 100644 --- a/test/replicationMath.test.ts +++ b/test/replicationMath.test.ts @@ -1,4 +1,6 @@ import * as math from '../src/ReplicationMath' +import { EPSILON, MAX_PRECISION } from '../src/utils' +import { maxError } from './cumulativeNormalDistribution.test' describe('Replication math', () => { describe('getStableGivenRisky', () => { @@ -70,4 +72,45 @@ describe('Replication math', () => { expect(math.nonNegative(-1)).toEqual(false) }) }) + + // desmos used https://www.desmos.com/calculator/ztctiscqqe + describe('solidity approximations', () => { + const R1 = 0.308537538726 + const R2 = 3.08537538726 + const strike = 10 + const sigma = 1 + const tau = 1 + + it('getRiskyGivenStableApproximation using desmos as reference', () => { + expect(math.getRiskyGivenStableApproximation(R2, strike, sigma, tau)).toBeCloseTo(R1, maxError.cdf) + }) + + it('getStableGivenRiskyApproximation using desmos as reference', () => { + expect(math.getStableGivenRiskyApproximation(R1, strike, sigma, tau)).toBeCloseTo(R2, maxError.cdf) + }) + + it('getStableGivenRiskyApproximation at risky reserve = 1 - MAX_PRECISION', () => { + expect(math.getStableGivenRiskyApproximation(1 - MAX_PRECISION, strike, sigma, tau)).toBeCloseTo(0, maxError.cdf) + }) + + it('getStableGivenRiskyApproximation at risky reserve = MAX_PRECISION', () => { + expect(math.getStableGivenRiskyApproximation(MAX_PRECISION, strike, sigma, tau)).toBeCloseTo(strike, maxError.cdf) + }) + + it('getRiskyGivenStableApproximation at stable reserve = strike - EPSILON', () => { + expect(math.getRiskyGivenStableApproximation(strike - EPSILON, strike, sigma, tau)).toBeCloseTo(0, maxError.cdf) + }) + + it('getRiskyGivenStableApproximation at stable reserve = MAX_PRECISION', () => { + expect(math.getRiskyGivenStableApproximation(MAX_PRECISION, strike, sigma, tau)).toBeCloseTo(1, maxError.cdf) + }) + + it('getInvariantApproximation', () => { + expect(math.getInvariantApproximation(R1, R2, strike, sigma, tau)).toBeCloseTo(0, maxError.cdf) + }) + }) + + it('should return false for a negative value', () => { + expect(math.nonNegative(-1)).toEqual(false) + }) }) From d1ee63b6e3dfc00e5f0a3aed6ab4dc84aa3251d9 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 10 Oct 2021 15:05:52 -0700 Subject: [PATCH 3/3] fix(lint issues): fixes some typescript linting problems --- src/ReplicationMath.ts | 2 +- src/utils.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ReplicationMath.ts b/src/ReplicationMath.ts index a6ae99e..a4fd5ad 100644 --- a/src/ReplicationMath.ts +++ b/src/ReplicationMath.ts @@ -54,7 +54,7 @@ export function getRiskyGivenStableApproximation( const MAX_RISKY = 1 let optimalDeltaOut: number - if (Math.sign(func(MAX_PRECISION)) != Math.sign(func(MAX_RISKY - MAX_PRECISION))) { + if (Math.sign(func(MAX_PRECISION)) !== Math.sign(func(MAX_RISKY - MAX_PRECISION))) { optimalDeltaOut = bisection(func, MAX_PRECISION, MAX_RISKY - MAX_PRECISION) } else { optimalDeltaOut = MAX_RISKY diff --git a/src/utils.ts b/src/utils.ts index 8a9c363..fb6ceb9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -11,7 +11,7 @@ export const MAX_PRECISION = 1e-6 */ export function bisection(func, a, b) { if (func(a) * func(b) >= 0) { - console.log('\n You have not assumed' + ' right a and b') + console.log('\n You have not assumed right a and b') return } @@ -21,7 +21,7 @@ export function bisection(func, a, b) { c = (a + b) / 2 // Check if middle point is root - if (func(c) == 0.0) break + if (func(c) === 0.0) break // Decide the side to repeat the steps else if (func(c) * func(a) < 0) b = c else a = c