falbit-utils
Version:
TypeScript utils for Falcon Arbitrage Strategy
352 lines (348 loc) • 10 kB
JavaScript
// 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
};