UNPKG

falbit-utils

Version:
352 lines (348 loc) 10 kB
// src/utils/amm.ts function calculateAmountOut(rIn, rOut, amountIn, fee = 0) { if (rIn <= 0 || rOut <= 0 || amountIn <= 0) { throw new Error("Reserves and amountIn must be greater than 0"); } const numerator = amountIn * rOut; const denominator = rIn + amountIn; return numerator / denominator * (1 - fee); ; } function calculateAmountIn(rIn, rOut, amountOut, fee = 0) { if (rIn <= 0 || rOut <= 0 || amountOut <= 0) { throw new Error("Reserves and amountOut must be greater than 0"); } const numerator = rIn * amountOut; const denominator = rOut - amountOut; return numerator / denominator * (1 + fee); } // src/utils/math.ts var truncate = (num, d) => Math.floor(num * 10 ** d) / 10 ** d; // src/utils/orderbook.ts var buyBase = (asks, base, commission = 0, precisions = 2) => { let tmpBase = base; let quote = 0; for (let i = 0; i < asks.length && base > 0; i++) { let { size, price } = asks[i]; let tradeVol = Math.min(size, base); base -= tradeVol; quote += tradeVol * price; } return { base: tmpBase - base, quote: truncate(quote * (1 + commission), precisions) }; }; var buyQuote = (asks, quote, commission = 0, precisions = 2) => { let tmpQuote = quote; let base = 0; for (let i = 0; i < asks.length && quote > 0; i++) { let { size, price } = asks[i]; let tradeVol = Math.min(quote / price, size); quote -= tradeVol * price; base += tradeVol; } return { base: truncate(base * (1 - commission), precisions), quote: tmpQuote - quote }; }; var sellBase = (bids, base, commission = 0, precisions = 2) => { let tmpBase = base; let quote = 0; for (let i = 0; i < bids.length && base > 0; i++) { let { size, price } = bids[i]; let tradeVol = Math.min(base, size); base -= tradeVol; quote += tradeVol * price; } return { base: tmpBase - base, quote: truncate(quote * (1 - commission), precisions) }; }; var sellQuote = (bids, quote, commission = 0, precisions = 2) => { let tmpQuote = quote; let base = 0; for (let i = 0; i < bids.length && quote > 0; i++) { let { size, price } = bids[i]; let tradeVol = Math.min(quote / price, size); quote -= tradeVol * price; base += tradeVol; } return { base: truncate(base * (1 + commission), precisions), quote: tmpQuote - quote }; }; // src/arbitrage-engine.ts var ArbitrageEngine = class { constructor() { this.DEFAULT_PRECISION = 8; this.orderbooks = {}; this.reserves = {}; } // Public functions updateOrderBooks(exchange, pair, orderbooks) { const reverseOrderBooks = this._getReverseOrderbooks(exchange, pair); if (reverseOrderBooks) throw new Error(`updateOrderBooks: reverse orderbooks already exists`); this._setOrderBooks(exchange, pair, orderbooks); } updateReserves(exchange, pair, reserves) { const reverseReserves = this._getReverseReserves(exchange, pair); if (reverseReserves) throw new Error(`updateReserves: reverse reserves already exists`); this._setReserves(exchange, pair, reserves); } getAmountsOut({ route, exchanges, amountIn, fees, precisions, orderTypes }) { const routeLen = route.length; if (exchanges.length !== routeLen) throw new Error(`getAmountsOut: exchanges length not match route length`); if (fees && fees.length !== routeLen) throw new Error(`getAmountsOut: fees length not match route length`); if (precisions && precisions.length !== routeLen) throw new Error(`getAmountsOut: precisions length not match route length`); if (orderTypes && orderTypes.length !== routeLen) throw new Error(`getAmountsOut: orderTypes length not match route length`); const amounts = [amountIn]; for (let i = 1; i < routeLen; i++) { const pair = `${route[i - 1]}_${route[i]}`; const exchange = exchanges[i]; const fee = fees ? fees[i] : 0; const precision = precisions ? precisions[i] : this.DEFAULT_PRECISION; const orderType = orderTypes ? orderTypes[i] : "orderbook"; const result = this.getAmountOut({ exchange, pair, amountIn: amounts[i - 1], fee, precision, orderType }); amounts.push(result.amountOut); } return amounts; } getAmountOut({ exchange, pair, amountIn, fee, precision, orderType }) { return orderType === "amm" ? this._getAmountOutAMM({ exchange, pair, amountIn, fee, precision }) : this._getAmountOutOrderBook({ exchange, pair, amountIn, fee, precision }); } getAmountIn({ exchange, pair, amountOut, fee, precision, orderType }) { return orderType === "amm" ? this._getAmountInAMM({ exchange, pair, amountOut, fee, precision }) : this._getAmountInOrderBook({ exchange, pair, amountOut, fee, precision }); } // Getters getOrderBooks(exchange, pair) { if (!this.orderbooks[exchange]) return null; if (!this.orderbooks[exchange][pair]) return null; return this.orderbooks[exchange][pair]; } getReserves(exchange, pair) { if (!this.reserves[exchange]) return null; if (!this.reserves[exchange][pair]) return null; return this.reserves[exchange][pair]; } // Private functions // BTC_USDT -> How much USDT gotten when selling BTC -> amountIn of BTC -> amountOut of USDT // USDT_BTC -> How much BTC gotten when buying with USDT -> amountIn of USDT -> amountOut of BTC _getAmountOutOrderBook({ exchange, pair, amountIn, fee, precision }) { if (!fee) fee = 0; if (!precision) precision = this.DEFAULT_PRECISION; const reverseOrderBooks = this._getReverseOrderbooks(exchange, pair); const orderBooks = this.getOrderBooks(exchange, pair); if (!reverseOrderBooks && !orderBooks) throw new Error(`_getAmountOutOrderBook: no orderbooks found`); if (reverseOrderBooks) { const { base, quote } = buyQuote(reverseOrderBooks.asks, amountIn, fee, precision); return { amountIn: quote, amountOut: base }; } else if (orderBooks) { const { base, quote } = sellBase(orderBooks.bids, amountIn, fee, precision); return { amountIn: base, amountOut: quote }; } return { amountIn: 0, amountOut: 0 }; } // BTC_USDT -> How much BTC sold to get the specific amount of USDT -> amountOut of USDT -> amountIn of BTC // USDT_BTC -> How much USDT paid to get the specific amount of BTC -> amountOut of BTC -> amountIn of USDT _getAmountInOrderBook({ exchange, pair, amountOut, fee, precision }) { if (!fee) fee = 0; if (!precision) precision = this.DEFAULT_PRECISION; const reverseOrderBooks = this._getReverseOrderbooks(exchange, pair); const orderBooks = this.getOrderBooks(exchange, pair); if (!reverseOrderBooks && !orderBooks) throw new Error(`_getAmountInOrderBook: no orderbooks found`); if (reverseOrderBooks) { const { base, quote } = buyBase(reverseOrderBooks.asks, amountOut, fee, precision); return { amountIn: quote, amountOut: base }; } else if (orderBooks) { const { base, quote } = sellQuote(orderBooks.bids, amountOut, fee, precision); return { amountIn: base, amountOut: quote }; } return { amountIn: 0, amountOut: 0 }; } _getAmountOutAMM({ exchange, pair, amountIn, fee, precision }) { if (!fee) fee = 0; const reverseReserves = this._getReverseReserves(exchange, pair); const reserves = this.getReserves(exchange, pair); if (!reverseReserves && !reserves) throw new Error(`_getAmountOutAMM: no reserves found`); if (reverseReserves) { const result = calculateAmountOut( reverseReserves.r1, reverseReserves.r0, amountIn, fee ); return { amountIn, amountOut: result }; } else if (reserves) { const result = calculateAmountOut(reserves.r0, reserves.r1, amountIn, fee); return { amountIn, amountOut: result }; } return { amountIn: 0, amountOut: 0 }; } _getAmountInAMM({ exchange, pair, amountOut, fee, precision }) { if (!fee) fee = 0; if (!precision) precision = this.DEFAULT_PRECISION; const [token0, token1] = pair.split("_"); const reverseReserves = this._getReverseReserves(exchange, pair); const reserves = this.getReserves(exchange, pair); if (!reverseReserves && !reserves) throw new Error(`_getAmountInAMM: no reserves found`); if (reverseReserves) { const result = calculateAmountIn( reverseReserves.r1, reverseReserves.r0, amountOut, fee ); return { amountIn: result, amountOut }; } else if (reserves) { const result = calculateAmountIn(reserves.r0, reserves.r1, amountOut, fee); return { amountIn: result, amountOut }; } return { amountIn: 0, amountOut: 0 }; } _getReverseOrderbooks(exchange, pair) { const [token0, token1] = pair.split("_"); return this.getOrderBooks(exchange, `${token1}_${token0}`); } _getReverseReserves(exchange, pair) { const [token0, token1] = pair.split("_"); return this.getReserves(exchange, `${token1}_${token0}`); } _setOrderBooks(exchange, pair, orderbooks) { if (!this.orderbooks[exchange]) { this.orderbooks[exchange] = {}; } this.orderbooks[exchange][pair] = orderbooks; } _setReserves(exchange, pair, reserves) { if (!this.reserves[exchange]) { this.reserves[exchange] = {}; } this.reserves[exchange][pair] = reserves; } }; export { ArbitrageEngine, buyBase, buyQuote, calculateAmountIn, calculateAmountOut, sellBase, sellQuote, truncate };