Skip to content

Commit

Permalink
fix(spot-price): updates spot price math
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexangelj committed Jan 12, 2022
1 parent 9873343 commit 3ed29f5
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 28 deletions.
135 changes: 109 additions & 26 deletions src/ReplicationMath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,20 @@ 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
* Forward trading function to calculate the risky reserve given a stable reserve which has a 0 invariant
*
* @remarks
* 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
*
* @beta
*/
export function getStableGivenRiskyApproximation(
reserveRisky: number,
Expand All @@ -39,10 +45,14 @@ export function getStableGivenRiskyApproximation(
}

/**
* @notice Using approximations in the forward function `getStableGivenRisky` will cause this inverse function
* 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`
*
* @remarks
* Uses a bisection to find the `reserveRisky` which sets the invariant to 0 given a `reserveStable`
*
* @beta
*/
export function getRiskyGivenStableApproximation(
reserveStable: number,
Expand All @@ -65,12 +75,18 @@ export function getRiskyGivenStableApproximation(
}

/**
* Computes the invariant of the CFMM using the forward trading function with the same solidity
* approximations for the CDF function.
*
* @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(...)
*
* @beta
*/
export function getInvariantApproximation(
reserveRisky: number,
Expand All @@ -86,13 +102,17 @@ export function getInvariantApproximation(
// ===== Precise math =====

/**
* @notice Forward trading function to calculate the risky reserve given a stable reserve which has a 0 invariant
* 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 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
*
* @beta
*/
export function getStableGivenRisky(
reserveRisky: number,
Expand All @@ -112,13 +132,21 @@ export function getStableGivenRisky(
}

/**
* @notice Inverse trading function to calculate the stable reserve given a risky reserve which has a 0 invariant
* Inverse trading function to calculate the stable reserve given a risky reserve which has a 0 invariant
*
* @remarks
* In the smart contracts, an approximation is used in the forward direction. Therefore, this
* is not the same result as the inverse of the approximated forward direction.
*
* @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
* @param invariantLast Previous invariant with the same `tau` input as the parameter `tau`
*
* @returns = 1 - Φ(Φ^-1((reserveStable - invariant) / K) + σ*sqrt(T - t))
*
* @beta
*/
export function getRiskyGivenStable(
reserveStable: number,
Expand All @@ -138,12 +166,17 @@ export function getRiskyGivenStable(
}

/**
* Computes the invariant of the CFMM.
*
* @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 - getStableGivenRisky(...)
*
* @beta
*/
export function calcInvariant(
reserveRisky: number,
Expand All @@ -156,36 +189,67 @@ 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 as a decimal percentage
* @param tau Time until expiry in years
* @returns getStableGivenRisky(...) * pdf(ppf(1 - risky))^-1
* Computes the reported price, also named marginal price, of the CFMM given the risky reserves.
*
* @remarks
* Source: https://github.com/primitivefinance/rmm-research/blob/93c0627d128b5253ea95e885c49151f7d089164a/whitepaper.pdf
*
* @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
*
* @returns Ke^( Φ^-1(1-x) * σ * sqrt(T) ) * e^ ( -1 / 2 * σ^2 * T)
*
* @beta
*/
export function getSpotPrice(reserveRisky: number, strike: number, sigma: number, tau: number): number {
return getStableGivenRisky(reserveRisky, strike, sigma, tau) * quantilePrime(1 - reserveRisky)
return (
strike *
Math.exp(inverse_std_n_cdf(1 - reserveRisky) * sigma * Math.sqrt(tau)) *
Math.exp((-1 / 2) * Math.pow(sigma, 2) * tau)
)
}

/**
* @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
* Uses the approximations in the solidity contract to compute the reported price of the CFMM.
*
*
* @remarks
* Source: https://github.com/primitivefinance/rmm-research/blob/93c0627d128b5253ea95e885c49151f7d089164a/whitepaper.pdf
*
* @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
*
* @returns Ke^( Φ^-1(1-x) * σ * sqrt(T) ) * e^ ( -1 / 2 * σ^2 * T)
*
* @beta
*/
export function getSpotPriceApproximation(reserveRisky: number, strike: number, sigma: number, tau: number): number {
return getStableGivenRiskyApproximation(reserveRisky, strike, sigma, tau) * quantilePrime(1 - reserveRisky)
return (
strike *
Math.exp(getInverseCDFSolidity(1 - reserveRisky) * sigma * Math.sqrt(tau)) *
Math.exp((-1 / 2) * Math.pow(sigma, 2) * tau)
)
}

/**
* @notice See https://arxiv.org/pdf/2012.08040.pdf
* See https://arxiv.org/pdf/2012.08040.pdf
*
* @remarks
* Source: https://github.com/primitivefinance/rmm-research/blob/93c0627d128b5253ea95e885c49151f7d089164a/whitepaper.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.
*
* @beta
*/
export function getMarginalPriceSwapRiskyIn(
amountIn: number,
Expand All @@ -207,13 +271,20 @@ export function getMarginalPriceSwapRiskyIn(
}

/**
* @notice See https://arxiv.org/pdf/2012.08040.pdf
* See https://arxiv.org/pdf/2012.08040.pdf
*
* @remarks
* Source: https://github.com/primitivefinance/rmm-research/blob/93c0627d128b5253ea95e885c49151f7d089164a/whitepaper.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.
*
* @beta
*/
export function getMarginalPriceSwapStableIn(
amountIn: number,
Expand All @@ -236,14 +307,20 @@ export function getMarginalPriceSwapStableIn(
}

/**
* @notice See https://arxiv.org/pdf/2012.08040.pdf
* @dev Uses same approximations used in solidity contracts
* See https://arxiv.org/pdf/2012.08040.pdf
*
* @remarks
* Uses same approximations used in solidity contracts
*
* @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.
*
* @beta
*/
export function getMarginalPriceSwapRiskyInApproximation(
amountIn: number,
Expand All @@ -265,14 +342,20 @@ export function getMarginalPriceSwapRiskyInApproximation(
}

/**
* @notice See https://arxiv.org/pdf/2012.08040.pdf
* @dev Uses same approximations used in solidity contracts
* See https://arxiv.org/pdf/2012.08040.pdf
*
* @remarks
* Uses same approximations used in solidity contracts
*
* @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.
*
* @beta
*/
export function getMarginalPriceSwapStableInApproximation(
amountIn: number,
Expand All @@ -295,7 +378,7 @@ export function getMarginalPriceSwapStableInApproximation(
}

/**
* @param x A number
* @param x - A number
* @returns is x greater than or equal to 0?
*/
export function nonNegative(x: number): boolean {
Expand Down
4 changes: 2 additions & 2 deletions test/replicationMath.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ describe('Replication math', () => {
})

it('should be nan if 1 - reserveRisky is 0 or less', () => {
expect(math.getSpotPrice(1, 1, 1, 1)).toBe(NaN)
expect(math.getSpotPrice(1, 1, 1, 1)).toBe(0)
})

it('should be nan if reserveRisky is greater than 1', () => {
expect(math.getSpotPrice(3, 2, 1, 1)).toBe(NaN)
expect(math.getSpotPrice(3, 2, 1, 1)).toBe(0)
})
})

Expand Down

0 comments on commit 3ed29f5

Please sign in to comment.