Calculate min and max rates in buy quote

This commit is contained in:
fragosti 2018-09-19 15:58:30 +02:00
parent 93f7e33f6a
commit 60e2dfdbda
3 changed files with 70 additions and 33 deletions

View File

@ -1,5 +1,6 @@
import { marketUtils } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import { constants } from '../constants';
import { AssetBuyerError, AssetBuyerOrdersAndFillableAmounts, BuyQuote } from '../types';
@ -18,41 +19,64 @@ export const buyQuoteCalculator = {
remainingFillableFeeAmounts,
} = ordersAndFillableAmounts;
const slippageBufferAmount = assetBuyAmount.mul(slippagePercentage).round();
const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(
orders,
assetBuyAmount,
{
remainingFillableMakerAssetAmounts,
slippageBufferAmount,
},
);
const {
resultOrders,
remainingFillAmount,
ordersRemainingFillableMakerAssetAmounts,
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, assetBuyAmount, {
remainingFillableMakerAssetAmounts,
slippageBufferAmount,
});
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
throw new Error(AssetBuyerError.InsufficientAssetLiquidity);
}
// TODO: optimization
// update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to
// finding order that cover all fees, this will help with estimating ETH and minimizing gas usage
const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
resultOrders,
feeOrders,
{
remainingFillableMakerAssetAmounts,
remainingFillableFeeAmounts,
},
);
const {
resultFeeOrders,
remainingFeeAmount,
feeOrdersRemainingFillableMakerAssetAmounts,
} = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(resultOrders, feeOrders, {
remainingFillableMakerAssetAmounts,
remainingFillableFeeAmounts,
});
if (remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
throw new Error(AssetBuyerError.InsufficientZrxLiquidity);
}
const assetData = orders[0].makerAssetData;
// TODO: critical
// calculate minRate and maxRate by calculating min and max eth usage and then dividing into
// assetBuyAmount to get assetData / WETH, needs to take into account feePercentage as well
// minEthAmount = (sum(takerAssetAmount[i]) until sum(makerAssetAmount[i]) >= assetBuyAmount ) * (1 + feePercentage)
// maxEthAmount = (sum(takerAssetAmount[i]) until i == orders.length) * (1 + feePercentage)
const allOrders = _.concat(resultOrders, resultFeeOrders);
const allRemainingAmounts = _.concat(
ordersRemainingFillableMakerAssetAmounts,
feeOrdersRemainingFillableMakerAssetAmounts,
);
let minEthAmount = constants.ZERO_AMOUNT;
let maxEthAmount = constants.ZERO_AMOUNT;
let cumulativeMakerAmount = constants.ZERO_AMOUNT;
_.forEach(allOrders, (order, index) => {
const remainingFillableMakerAssetAmount = allRemainingAmounts[index];
const orderRate = order.takerAssetAmount.div(order.makerAssetAmount);
const claimableTakerAssetAmount = orderRate.mul(remainingFillableMakerAssetAmount);
// taker asset is always assumed to be WETH
maxEthAmount = maxEthAmount.plus(claimableTakerAssetAmount);
if (cumulativeMakerAmount.lessThan(assetBuyAmount)) {
minEthAmount = minEthAmount.plus(claimableTakerAssetAmount);
}
cumulativeMakerAmount = cumulativeMakerAmount.plus(remainingFillableMakerAssetAmount);
});
const feeAdjustedMinRate = minEthAmount.mul(feePercentage + 1).div(assetBuyAmount);
const feeAdjustedMaxRate = minEthAmount.mul(feePercentage + 1).div(assetBuyAmount);
return {
assetData,
orders: resultOrders,
feeOrders: resultFeeOrders,
minRate: constants.ZERO_AMOUNT,
maxRate: constants.ZERO_AMOUNT,
minRate: feeAdjustedMinRate,
maxRate: feeAdjustedMaxRate,
assetBuyAmount,
feePercentage,
};

View File

@ -51,17 +51,23 @@ export const marketUtils = {
// iterate through the orders input from left to right until we have enough makerAsset to fill totalFillAmount
const result = _.reduce(
orders,
({ resultOrders, remainingFillAmount }, order, index) => {
({ resultOrders, remainingFillAmount, ordersRemainingFillableMakerAssetAmounts }, order, index) => {
if (remainingFillAmount.lessThanOrEqualTo(constants.ZERO_AMOUNT)) {
return { resultOrders, remainingFillAmount: constants.ZERO_AMOUNT };
return {
resultOrders,
remainingFillAmount: constants.ZERO_AMOUNT,
ordersRemainingFillableMakerAssetAmounts,
};
} else {
const makerAssetAmountAvailable = remainingFillableMakerAssetAmounts[index];
const shouldIncludeOrder = makerAssetAmountAvailable.gt(constants.ZERO_AMOUNT);
// if there is no makerAssetAmountAvailable do not append order to resultOrders
// if we have exceeded the total amount we want to fill set remainingFillAmount to 0
return {
resultOrders: makerAssetAmountAvailable.gt(constants.ZERO_AMOUNT)
? _.concat(resultOrders, order)
: resultOrders,
resultOrders: shouldIncludeOrder ? _.concat(resultOrders, order) : resultOrders,
ordersRemainingFillableMakerAssetAmounts: shouldIncludeOrder
? _.concat(ordersRemainingFillableMakerAssetAmounts, makerAssetAmountAvailable)
: ordersRemainingFillableMakerAssetAmounts,
remainingFillAmount: BigNumber.max(
constants.ZERO_AMOUNT,
remainingFillAmount.minus(makerAssetAmountAvailable),
@ -69,7 +75,11 @@ export const marketUtils = {
};
}
},
{ resultOrders: [] as T[], remainingFillAmount: totalFillAmount },
{
resultOrders: [] as T[],
remainingFillAmount: totalFillAmount,
ordersRemainingFillableMakerAssetAmounts: [] as BigNumber[],
},
);
return result;
},
@ -133,17 +143,18 @@ export const marketUtils = {
},
constants.ZERO_AMOUNT,
);
const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(
feeOrders,
totalFeeAmount,
{
remainingFillableMakerAssetAmounts: remainingFillableFeeAmounts,
slippageBufferAmount,
},
);
const {
resultOrders,
remainingFillAmount,
ordersRemainingFillableMakerAssetAmounts,
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(feeOrders, totalFeeAmount, {
remainingFillableMakerAssetAmounts: remainingFillableFeeAmounts,
slippageBufferAmount,
});
return {
resultFeeOrders: resultOrders,
remainingFeeAmount: remainingFillAmount,
feeOrdersRemainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
};
// TODO: add more orders here to cover rounding
// https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarding-contract-specification.md#over-buying-zrx

View File

@ -72,10 +72,12 @@ export interface FindFeeOrdersThatCoverFeesForTargetOrdersOpts {
export interface FeeOrdersAndRemainingFeeAmount<T> {
resultFeeOrders: T[];
feeOrdersRemainingFillableMakerAssetAmounts: BigNumber[];
remainingFeeAmount: BigNumber;
}
export interface OrdersAndRemainingFillAmount<T> {
resultOrders: T[];
ordersRemainingFillableMakerAssetAmounts: BigNumber[];
remainingFillAmount: BigNumber;
}