Created
February 15, 2021 17:17
-
-
Save jeffywu/0bc3cb934adf14511ba2fa04b675fd74 to your computer and use it in GitHub Desktop.
Notional Market Calculation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {BigNumber} from 'ethers/utils'; | |
import {Decimal} from 'decimal.js'; | |
import {CashMarket} from './typechain/CashMarket'; | |
import {WeiPerEther} from 'ethers/constants'; | |
import {getNowSeconds, toBigNumber} from './utils'; | |
import {CashGroup} from './cashGroup'; | |
import {GraphClient, MarketQueryResult} from './graphClient'; | |
import {ApolloQueryResult} from '@apollo/client'; | |
/** Mimic ABDK rounding settings */ | |
Decimal.set({ | |
precision: 18, | |
rounding: Decimal.ROUND_CEIL, | |
}); | |
const MAX64 = new Decimal(9.223372036854775807); | |
/** | |
* Used internally for annualizing rates | |
* @ignore | |
*/ | |
export const SECONDS_IN_YEAR = 31536000; | |
/** Describes the market for a maturity for calculating rates */ | |
export interface MarketData { | |
/** Total current cash available in the market */ | |
totalCurrentCash: BigNumber; | |
/** Total fCash available in the market */ | |
totalfCash: BigNumber; | |
/** Total liquidity tokens in the market */ | |
totalLiquidity: BigNumber; | |
/** Current rate anchor for the market */ | |
rateAnchor: ExchangeRate; | |
/** Rate scalar for the market */ | |
rateScalar: number; | |
/** The last implied rate that the market traded at */ | |
lastImpliedRate: ImpliedPeriodRate; | |
} | |
/** Global contract parameters for calculating rates */ | |
export interface RateParams { | |
/** Decimal places of precision for the rate (defaults to 1e9) */ | |
ratePrecision: number; | |
/** Liquidity fee paid as a part of the exchange rate */ | |
liquidityFee: number; | |
/** Decimal places of precision for balances (defaults to 1e18) */ | |
balancePrecision: BigNumber; | |
liquidityHaircut: BigNumber; | |
globalRateAnchor: number; | |
globalRateScalar: number; | |
maxTradeSize: BigNumber; | |
} | |
/** Type alias for rates that are annualized */ | |
type ImpliedAnnualRate = number; | |
/** Type alias for rates that are period-ized */ | |
type ImpliedPeriodRate = number; | |
/** Spot exchange rate */ | |
type ExchangeRate = number; | |
/** | |
* Provides methods for calculating various rates for a market. These are replications of the rate calculation methods on the Fixed Rate Pool | |
* contract itself for use in user interfaces where lower latency calculations are important. The ultimate source of truth for rate calculations | |
* will always be the smart contract. | |
* | |
* The three relevant types of rates are: | |
* - Exchange Rate: the amount of current cash to be exchanged for a unit of fCash (i.e. `current * exchangeRate = future`) | |
* - Implied Period Rate: the interest rate implied by the exchange rate over the length of the period that the fCash will mature. (i.e. `(exchangeRate - 1) * (maturityLength / timeToMaturity)`) | |
* - Implied Annual Rate: the implied period rate on an annualized basis | |
*/ | |
export class Market { | |
/** | |
* Formats rates as a string with a given precision. | |
* | |
* @category Formatting [Static] | |
* @param rate rate to format | |
* @param ratePrecision the rate Precision (default: 1e9) | |
* @param precision amount of decimals to return (default: 4) | |
* @return formatted rate string | |
*/ | |
public static formatImpliedRate(rate: number, ratePrecision = 1e9, precision = 4) { | |
if (rate === undefined || rate === 0) return 'No Rate'; | |
return ((rate / ratePrecision) * 100).toFixed(precision) + '%'; | |
} | |
/** | |
* Formats rates as a string with a given precision. | |
* | |
* @category Formatting | |
* @param rate rate to format | |
* @param precision the number of decimals places to supply (default: 3) | |
* @return formatted rate string | |
* @return the rate formatted as a string | |
*/ | |
public formatImpliedRate(rate: number, precision = 3): string { | |
return Market.formatImpliedRate(rate, this.rateParams.ratePrecision, precision); | |
} | |
/** | |
* Formats rates as a string with a given precision. | |
* | |
* @category Formatting [Static] | |
* @param rate rate to format | |
* @param ratePrecision the rate Precision (default: 1e9) | |
* @param precision amount of decimals to return (default: 4) | |
* @return formatted rate string | |
*/ | |
public static formatExchangeRate(rate: number, ratePrecision = 1e9, precision = 4) { | |
if (rate === undefined || rate === 0) return 'No Rate'; | |
return (((rate - ratePrecision) / ratePrecision) * 100).toFixed(precision) + '%'; | |
} | |
/** | |
* Formats rates as a string with a given precision. | |
* | |
* @category Formatting | |
* @param rate rate to format | |
* @param precision the number of decimals places to supply (default: 4) | |
* @return formatted rate string | |
* @return the rate formatted as a string | |
*/ | |
public formatExchangeRate(rate: number, precision = 4): string { | |
return Market.formatExchangeRate(rate, this.rateParams.ratePrecision, precision); | |
} | |
/** | |
* Converts an implied period rate to an implied annual rate | |
* | |
* @category Formatting [Static] | |
* @param impliedPeriodRate the implied period rate to annualize | |
* @param maturityLength the size of the period of the simple rate | |
* @param avgBlockTimeMs average block time for the network | |
* @param ratePrecision rate Precision (default: 1e9) | |
* @return implied annualized rate | |
*/ | |
public static periodToAnnualRate(impliedPeriodRate: ImpliedPeriodRate, maturityLength: number): ImpliedAnnualRate { | |
if (impliedPeriodRate === undefined) return 0; | |
if (maturityLength == 0) return 0; | |
const multiplier = SECONDS_IN_YEAR / maturityLength; | |
return Math.trunc(impliedPeriodRate * multiplier); | |
} | |
/** | |
* Converts an implied period rate to an implied annual rate | |
* | |
* @category Formatting | |
* @param impliedPeriodRate | |
* @return implied annualized rate | |
*/ | |
public periodToAnnualRate(impliedPeriodRate?: ImpliedPeriodRate): ImpliedAnnualRate { | |
if (impliedPeriodRate === undefined) return 0; | |
if (this.cashGroup.maturityLength == 0) return 0; | |
return Market.periodToAnnualRate(impliedPeriodRate, this.cashGroup.maturityLength); | |
} | |
/** | |
* Convert an implied annual rate to an implied period rate. | |
* | |
* @category Formatting [Static] | |
* @param annualizedRate the annualized rate to convert to an implied period rate | |
* @param maturityLength period length of the market | |
* @param avgBlockTimeMs average block time for the network | |
* @param ratePrecision rate Precision (default: 1e9) | |
* @return implied annual rate | |
*/ | |
public static annualToPeriodRate(annualizedRate: ImpliedAnnualRate, maturityLength: number): ImpliedPeriodRate { | |
if (annualizedRate === undefined) return 0; | |
if (maturityLength == 0) return 0; | |
const multiplier = SECONDS_IN_YEAR / maturityLength; | |
return Math.trunc(annualizedRate / multiplier); | |
} | |
/** | |
* Convert an implied annual rate to an implied period rate. | |
* | |
* @category Formatting | |
* @param annualizedRate the annualized rate to convert to an implied period rate | |
* @return implied annual rate | |
*/ | |
public annualToPeriodRate(annualizedRate: ImpliedAnnualRate): number { | |
return Market.annualToPeriodRate(annualizedRate, this.cashGroup.maturityLength); | |
} | |
/** | |
* Converts an exchange rate to an implied period rate | |
* | |
* @category Formatting [Static] | |
* @param exchangeRate | |
* @param blockTime block time the exchange occurs on | |
* @param maturityLength period length of the market | |
* @param maturity block height when the market will mature | |
* @param ratePrecision (default: 1e9) | |
* @return implied period rate | |
*/ | |
public static exchangeToPeriodRate( | |
exchangeRate: ExchangeRate, | |
blockTime: number, | |
maturityLength: number, | |
maturity: number, | |
ratePrecision = 1e9, | |
): ImpliedPeriodRate { | |
const timeToMaturity = maturity - blockTime; | |
if (timeToMaturity < 0) { | |
throw new RangeError('timeToMaturity < 0, cannot convert an exchange rate in a matured period.'); | |
} | |
return Math.trunc(((exchangeRate - ratePrecision) * maturityLength) / timeToMaturity); | |
} | |
/** | |
* Converts an exchange rate to an implied period rate | |
* | |
* @category Formatting | |
* @param exchangeRate exchange rate | |
* @param blockTime block time the exchange occurs on | |
* @return implied period rate | |
*/ | |
public exchangeToPeriodRate(exchangeRate: ExchangeRate, blockTime: number): ImpliedPeriodRate { | |
return Market.exchangeToPeriodRate(exchangeRate, blockTime, this.cashGroup.maturityLength, this.maturity); | |
} | |
/** | |
* Converts a period rate to an exchange rate. | |
* | |
* @param impliedPeriodRate | |
* @param blockTime | |
* @param maturityLength | |
* @param maturity | |
* @param ratePrecision | |
*/ | |
public static periodToExchangeRate( | |
impliedPeriodRate: ImpliedPeriodRate, | |
blockTime: number, | |
maturityLength: number, | |
maturity: number, | |
ratePrecision = 1e9, | |
): ExchangeRate { | |
const timeToMaturity = maturity - blockTime; | |
if (timeToMaturity < 0) { | |
throw new RangeError('timeToMaturity < 0, cannot convert an exchange rate in a matured period.'); | |
} | |
return Math.trunc((impliedPeriodRate * timeToMaturity) / maturityLength) + ratePrecision; | |
} | |
/** | |
* Converts a period rate to an exchange rate. | |
* | |
* @param impliedPeriodRate | |
* @param blockTime | |
*/ | |
public periodToExchangeRate(impliedPeriodRate: ImpliedPeriodRate, blockTime: number): ExchangeRate { | |
return Market.periodToExchangeRate(impliedPeriodRate, blockTime, this.cashGroup.maturityLength, this.maturity); | |
} | |
public fCashFromExchangeRate(exchangeRate: ExchangeRate, cash: BigNumber): BigNumber { | |
return Market.fCashFromExchangeRate(exchangeRate, cash, this.rateParams.ratePrecision); | |
} | |
public static fCashFromExchangeRate(exchangeRate: ExchangeRate, cash: BigNumber, ratePrecision = 1e9): BigNumber { | |
return cash.mul(exchangeRate).div(ratePrecision); | |
} | |
public cashFromExchangeRate(exchangeRate: ExchangeRate, fCash: BigNumber): BigNumber { | |
return Market.cashFromExchangeRate(exchangeRate, fCash, this.rateParams.ratePrecision); | |
} | |
public static cashFromExchangeRate(exchangeRate: ExchangeRate, fCash: BigNumber, ratePrecision = 1e9): BigNumber { | |
return fCash.mul(ratePrecision).div(exchangeRate); | |
} | |
/** | |
* Returns the implied rate for the loan over the period | |
* | |
* @category Formatting [Static] | |
* @param fCashAmount | |
* @param cashAmount | |
* @param maturityLength period length of the market | |
* @param blockTime block time the exchange occurs on | |
* @param ratePrecision (default: 1e9) | |
* @return implied period rate | |
*/ | |
public static impliedPeriodRate( | |
fCashAmount: BigNumber, | |
cashAmount: BigNumber, | |
maturityLength: number, | |
maturity: number, | |
blockTime: number, | |
ratePrecision = 1e9, | |
): ImpliedPeriodRate { | |
const timeToMaturity = maturity - blockTime; | |
return fCashAmount | |
.mul(ratePrecision) | |
.div(cashAmount) | |
.sub(ratePrecision) | |
.mul(maturityLength) | |
.div(timeToMaturity) | |
.toNumber(); | |
} | |
/** | |
* Returns the implied rate for the loan over the period | |
* | |
* @param fCashAmount | |
* @param cashAmount | |
* @param blockTime block time the exchange occurs on | |
* @return implied period rate | |
*/ | |
public impliedPeriodRate(fCashAmount: BigNumber, cashAmount: BigNumber, blockTime: number): ImpliedPeriodRate { | |
return Market.impliedPeriodRate( | |
fCashAmount, | |
cashAmount, | |
this.cashGroup.maturityLength, | |
this.maturity, | |
blockTime, | |
this.rateParams.ratePrecision, | |
); | |
} | |
/** | |
* Returns the spot exchange rate between two amounts. | |
* | |
* @category Formatting [Static] | |
* @param fCashAmount | |
* @param cashAmount | |
* @param ratePrecision | |
* @return exchange rate been fCash and current amount | |
*/ | |
public static exchangeRate(fCashAmount: BigNumber, cashAmount: BigNumber, ratePrecision: number): ExchangeRate { | |
return fCashAmount.mul(ratePrecision).div(cashAmount).toNumber(); | |
} | |
/** | |
* Returns the spot exchange rate between two amounts. | |
* | |
* @category Formatting | |
* @param fCashAmount | |
* @param cashAmount | |
* @return exchange rate been fCash and current amount | |
*/ | |
public exchangeRate(fCashAmount: BigNumber, cashAmount: BigNumber): ExchangeRate { | |
return Market.exchangeRate(fCashAmount, cashAmount, this.rateParams.ratePrecision); | |
} | |
/** | |
* Returns the current market rate. Async because it fetches the current block time. | |
* | |
* @category Calculation | |
* @returns exchange rate at `blockTime` | |
*/ | |
public marketImpliedRateNow() { | |
const nowSeconds = getNowSeconds(); | |
return this.exchangeToPeriodRate(this.marketExchangeRate(nowSeconds), nowSeconds); | |
} | |
/** | |
* Returns the current market rate. Async because it fetches the current block time. | |
* | |
* @category Calculation | |
* @returns exchange rate at `blockTime` | |
*/ | |
public marketImpliedRate(blockTime: number) { | |
return this.exchangeToPeriodRate(this.marketExchangeRate(blockTime), blockTime); | |
} | |
/** | |
* Returns the current market rate. Async because it fetches the current block time. | |
* | |
* @category Calculation | |
* @returns exchange rate at `blockTime` | |
*/ | |
public marketExchangeRateNow() { | |
const nowSeconds = getNowSeconds(); | |
return this.marketExchangeRate(nowSeconds); | |
} | |
/** | |
* Returns the current market rate. | |
* | |
* @category Calculation | |
* @param blockTime block time where the exchange will occur | |
* @returns exchange rate at `blockTime` | |
*/ | |
public marketExchangeRate(blockTime: number) { | |
const anchor = this.calculateAnchor(blockTime); | |
return this.calculateExchangeRate( | |
new BigNumber(0), | |
this.market.totalCurrentCash, | |
this.market.totalfCash, | |
anchor, | |
blockTime, | |
); | |
} | |
/** | |
* Calculates the amount of current cash that can be borrowed after selling the specified amount of fCash. | |
* | |
* @category Calculation | |
* @param fCashAmount amount of fCash to sell | |
* @param blockTime block time where the exchange will occur | |
* @returns the amount of current cash this will purchase | |
*/ | |
public getfCashToCurrentCashInput(fCashAmount: BigNumber, blockTime: number) { | |
const anchor = this.calculateAnchor(blockTime); | |
const feeRate = this.calculateFeeRate(blockTime); | |
const tradeRate = | |
this.calculateExchangeRate(fCashAmount, this.market.totalCurrentCash, this.market.totalfCash, anchor, blockTime) + | |
feeRate; | |
return fCashAmount.mul(this.rateParams.ratePrecision).div(tradeRate); | |
} | |
/** | |
* Calculates the amount of fCash that must be sold in order to borrow the amount of current cash specified. | |
* | |
* @category Calculation | |
* @param cashAmount amount of current cash to purchase | |
* @param blockTime block time where the exchange will occur | |
* @returns the amount of fCash that must be sold | |
*/ | |
public getfCashToCurrentCashOutput(cashAmount: BigNumber, blockTime: number) { | |
return this.iterateRates(cashAmount, blockTime, true); | |
} | |
/** | |
* Calculates the amount of fCash that will be purchased if `cashAmount` is lent. | |
* | |
* @category Calculation | |
* @param cashAmount amount of current cash to sell | |
* @param blockTime block time where the exchange will occur | |
* @returns the amount of fCash this will purchase | |
*/ | |
public getCurrentCashTofCashInput(cashAmount: BigNumber, blockTime: number) { | |
return this.iterateRates(cashAmount, blockTime, false); | |
} | |
/** | |
* Calculates the amount of current cash that must be lent in order to purchase `fCashAmount` at maturity. | |
* | |
* @category Calculation | |
* @param fCashAmount amount of fCash to purchase | |
* @param blockTime block time where the exchange will occur | |
* @returns the amount of current cash that must be sold | |
*/ | |
public getCurrentCashTofCashOutput(fCashAmount: BigNumber, blockTime: number) { | |
const anchor = this.calculateAnchor(blockTime); | |
const feeRate = this.calculateFeeRate(blockTime); | |
const tradeRate = | |
this.calculateExchangeRate( | |
fCashAmount.mul(-1), | |
this.market.totalCurrentCash, | |
this.market.totalfCash, | |
anchor, | |
blockTime, | |
) - feeRate; | |
if (tradeRate < this.rateParams.ratePrecision) { | |
throw new Error('Cannot lend at negative interest rates'); | |
} | |
return fCashAmount.mul(this.rateParams.ratePrecision).div(tradeRate); | |
} | |
public getLiquidityTokenClaims(tokens: BigNumber, shouldHaircut = true) { | |
const nowSeconds = getNowSeconds(); | |
const cashClaim = this.market.totalCurrentCash.mul(tokens).div(this.market.totalLiquidity); | |
const fCashClaim = this.market.totalfCash.mul(tokens).div(this.market.totalLiquidity); | |
if (this.maturity <= nowSeconds || !shouldHaircut) { | |
return { | |
cashClaim, | |
fCashClaim, | |
}; | |
} else { | |
return { | |
cashClaim: cashClaim.mul(this.rateParams.liquidityHaircut).div(WeiPerEther), | |
fCashClaim: fCashClaim.mul(this.rateParams.liquidityHaircut).div(WeiPerEther), | |
}; | |
} | |
} | |
public getTokensMinted(cashAmount: BigNumber) { | |
if (this.market.totalLiquidity.isZero()) { | |
return cashAmount; | |
} | |
return this.market.totalLiquidity.mul(cashAmount).div(this.market.totalCurrentCash); | |
} | |
public getPoolShare(tokens: BigNumber, calculatePostAdd = false) { | |
if (this.market.totalLiquidity.isZero()) { | |
return '100%'; | |
} | |
if (calculatePostAdd) { | |
const divisor = this.market.totalLiquidity.add(tokens); | |
return (tokens.mul(new BigNumber(100000)).div(divisor).toNumber() / 1000).toFixed(3) + '%'; | |
} | |
return (tokens.mul(new BigNumber(100000)).div(this.market.totalLiquidity).toNumber() / 1000).toFixed(3) + '%'; | |
} | |
public getfCashForLiquidity(cashAmount: BigNumber) { | |
return this.market.totalfCash.mul(cashAmount).div(this.market.totalCurrentCash); | |
} | |
public getImpliedRatePostLiquidity(cashAmount: BigNumber, blockTime: number, fCash?: BigNumber) { | |
if (fCash == null) { | |
fCash = this.market.totalfCash.mul(cashAmount).div(this.market.totalCurrentCash); | |
} | |
const newTotalCollateral = this.market.totalCurrentCash.add(cashAmount); | |
const newTotalfCash = this.market.totalfCash.add(fCash); | |
const anchor = this.calculateAnchor(blockTime, newTotalCollateral, newTotalfCash); | |
return this.exchangeToPeriodRate( | |
this.calculateExchangeRate(new BigNumber(0), newTotalCollateral, newTotalfCash, anchor, blockTime), | |
blockTime, | |
); | |
} | |
public getPresentValue(fCashValue: BigNumber): BigNumber { | |
const exchangeRate = this.marketExchangeRateNow(); | |
return fCashValue.mul(this.rateParams.ratePrecision).div(exchangeRate); | |
} | |
/** | |
* @category Formatting | |
*/ | |
public toJSON() { | |
return { | |
totalCurrentCash: this.market.totalCurrentCash.toString(), | |
totalfCash: this.market.totalfCash.toString(), | |
totalLiquidity: this.market.totalLiquidity.toString(), | |
maturity: this.maturity, | |
}; | |
} | |
private timeToMaturity(blockTime: number) { | |
return this.maturity - blockTime; | |
} | |
private calculateAnchor( | |
blockTime: number, | |
totalCurrentCash = this.market.totalCurrentCash, | |
totalfCash = this.market.totalfCash, | |
) { | |
const newImpliedRate = this.exchangeToPeriodRate( | |
this.calculateExchangeRate(new BigNumber(0), totalCurrentCash, totalfCash, this.market.rateAnchor, blockTime), | |
blockTime, | |
); | |
let rateDifference = 0; | |
if (this.market.lastImpliedRate != 0) { | |
rateDifference = Math.trunc( | |
((newImpliedRate - this.market.lastImpliedRate) * this.timeToMaturity(blockTime)) / | |
this.cashGroup.maturityLength, | |
); | |
} | |
let rateAnchor: number; | |
if (this.market.rateAnchor == 0) { | |
rateAnchor = new BigNumber(this.rateParams.globalRateAnchor) | |
.sub(this.rateParams.ratePrecision) | |
.mul(this.timeToMaturity(blockTime)) | |
.div(SECONDS_IN_YEAR) | |
.add(this.rateParams.ratePrecision) | |
.toNumber(); | |
} else { | |
rateAnchor = this.market.rateAnchor; | |
} | |
return rateAnchor - rateDifference; | |
} | |
private calculateScalar(blockTime: number) { | |
let rateScalar = this.rateParams.globalRateScalar; | |
if (this.market.rateScalar != 0) { | |
rateScalar = this.market.rateScalar; | |
} | |
return Math.trunc((rateScalar * this.cashGroup.maturityLength) / this.timeToMaturity(blockTime)); | |
} | |
private calculateFeeRate(blockTime: number): number { | |
return Math.trunc((this.rateParams.liquidityFee * this.timeToMaturity(blockTime)) / this.cashGroup.maturityLength); | |
} | |
/** | |
* Returns the exchange rate given the total amount of fCash and cash. Uses | |
* mathjs internally. | |
* | |
* @param _totalCurrentCash | |
* @param _totalfCash | |
* @return a bignumber of the exchange rate | |
*/ | |
private calculateExchangeRate( | |
_fCashAmount: BigNumber, | |
_totalCurrentCash: BigNumber, | |
_totalfCash: BigNumber, | |
anchor: number, | |
blockTime: number, | |
): ExchangeRate { | |
const fCashAmount = new Decimal(_fCashAmount.toString()); | |
const totalCurrentCash = new Decimal(_totalCurrentCash.toString()); | |
const totalfCash = new Decimal(_totalfCash.toString()); | |
const one = new Decimal(1); | |
// p = (totalFC + FC) / (totalFC + totalC) | |
const proportion = totalfCash.add(fCashAmount).div(totalfCash.add(totalCurrentCash)); | |
if (proportion.lt(0) || proportion.gt(1)) { | |
throw new RangeError( | |
`Trade size of ${fCashAmount} out of bounds for market ${this.maturity}, proportion = ${proportion}`, | |
); | |
} | |
const scalar = this.calculateScalar(blockTime); | |
const ratio = proportion.div(one.sub(proportion)); | |
if (ratio.greaterThan(MAX64)) { | |
throw new RangeError(`Trade size of ${fCashAmount} out of bounds for market ${this.maturity}, ratio = ${ratio}`); | |
} | |
// exchangeRate = ln(p / (1 - p)) / rateScalar + rateAnchor | |
const logPortion = Decimal.ln(ratio).mul(this.rateParams.ratePrecision).round(); // We round here instead of trunc() because of the ABDK behavior | |
const exchangeRate = logPortion.div(scalar).trunc().add(anchor); | |
return parseInt(exchangeRate.toFixed(0)); | |
} | |
private iterateRates(cashAmount: BigNumber, blockTime: number, isBuy: boolean) { | |
const marketRate = this.marketExchangeRate(blockTime); | |
let fCashAmount = cashAmount.mul(marketRate).div(this.rateParams.ratePrecision); | |
let currentEstimate = isBuy | |
? this.getfCashToCurrentCashInput(fCashAmount, blockTime) | |
: this.getCurrentCashTofCashOutput(fCashAmount, blockTime); | |
let diff = cashAmount.sub(currentEstimate); | |
while (!diff.eq(0)) { | |
let fcDiff = diff.mul(marketRate).div(this.rateParams.ratePrecision); | |
if (fcDiff.eq(0)) { | |
// The minimum of the diff has to be 1 if this has not converged. | |
fcDiff = new BigNumber(1); | |
} | |
fCashAmount = fCashAmount.add(fcDiff); | |
currentEstimate = isBuy | |
? this.getfCashToCurrentCashInput(fCashAmount, blockTime) | |
: this.getCurrentCashTofCashOutput(fCashAmount, blockTime); | |
diff = cashAmount.sub(currentEstimate); | |
} | |
return fCashAmount; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment