added testing to market utils for market sell
This commit is contained in:
@@ -73,6 +73,8 @@ export {
|
||||
TransferType,
|
||||
FindFeeOrdersThatCoverFeesForTargetOrdersOpts,
|
||||
FindOrdersThatCoverMakerAssetFillAmountOpts,
|
||||
FindOrdersThatCoverTakerAssetFillAmountOpts,
|
||||
FeeOrdersAndRemainingFeeAmount,
|
||||
OrdersAndRemainingFillAmount,
|
||||
OrdersAndRemainingTakerFillAmount,
|
||||
OrdersAndRemainingMakerFillAmount,
|
||||
} from './types';
|
||||
|
||||
@@ -5,12 +5,16 @@ import * as _ from 'lodash';
|
||||
|
||||
import { assert } from './assert';
|
||||
import { constants } from './constants';
|
||||
import {
|
||||
orderCalculationUtils,
|
||||
} from './order_calculation_utils';
|
||||
import {
|
||||
FeeOrdersAndRemainingFeeAmount,
|
||||
FindFeeOrdersThatCoverFeesForTargetOrdersOpts,
|
||||
FindOrdersThatCoverMakerAssetFillAmountOpts,
|
||||
FindOrdersThatCoverTakerAssetFillAmountOpts,
|
||||
OrdersAndRemainingFillAmount,
|
||||
OrdersAndRemainingMakerFillAmount,
|
||||
OrdersAndRemainingTakerFillAmount,
|
||||
} from './types';
|
||||
|
||||
export const marketUtils = {
|
||||
@@ -18,10 +22,61 @@ export const marketUtils = {
|
||||
orders: T[],
|
||||
takerAssetFillAmount: BigNumber,
|
||||
opts?: FindOrdersThatCoverTakerAssetFillAmountOpts,
|
||||
): OrdersAndRemainingFillAmount<T> {
|
||||
): OrdersAndRemainingTakerFillAmount<T> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
|
||||
|
||||
// try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from orders
|
||||
const remainingFillableTakerAssetAmounts = _.get(
|
||||
opts,
|
||||
'remainingFillableTakerAssetAmounts',
|
||||
_.map(orders, order => order.takerAssetAmount),
|
||||
) as BigNumber[];
|
||||
_.forEach(remainingFillableTakerAssetAmounts, (amount, index) =>
|
||||
assert.isValidBaseUnitAmount(`remainingFillableTakerAssetAmount[${index}]`, amount),
|
||||
);
|
||||
assert.assert(
|
||||
orders.length === remainingFillableTakerAssetAmounts.length,
|
||||
'Expected orders.length to equal opts.remainingFillableMakerAssetAmounts.length',
|
||||
);
|
||||
// try to get slippageBufferAmount from opts, if it's not there, default to 0
|
||||
const slippageBufferAmount = _.get(opts, 'slippageBufferAmount', constants.ZERO_AMOUNT) as BigNumber;
|
||||
assert.isValidBaseUnitAmount('opts.slippageBufferAmount', slippageBufferAmount);
|
||||
// calculate total amount of makerAsset needed to be filled
|
||||
const totalFillAmount = takerAssetFillAmount.plus(slippageBufferAmount);
|
||||
// iterate through the orders input from left to right until we have enough makerAsset to fill totalFillAmount
|
||||
const result = _.reduce(
|
||||
orders,
|
||||
({ resultOrders, remainingFillAmount, ordersRemainingFillableTakerAssetAmounts }, order, index) => {
|
||||
if (remainingFillAmount.isLessThanOrEqualTo(constants.ZERO_AMOUNT)) {
|
||||
return {
|
||||
resultOrders,
|
||||
remainingFillAmount: constants.ZERO_AMOUNT,
|
||||
ordersRemainingFillableTakerAssetAmounts,
|
||||
};
|
||||
} else {
|
||||
const takerAssetAmountAvailable = remainingFillableTakerAssetAmounts[index];
|
||||
const shouldIncludeOrder = takerAssetAmountAvailable.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: shouldIncludeOrder ? _.concat(resultOrders, order) : resultOrders,
|
||||
ordersRemainingFillableTakerAssetAmounts: shouldIncludeOrder
|
||||
? _.concat(ordersRemainingFillableTakerAssetAmounts, takerAssetAmountAvailable)
|
||||
: ordersRemainingFillableTakerAssetAmounts,
|
||||
remainingFillAmount: BigNumber.max(
|
||||
constants.ZERO_AMOUNT,
|
||||
remainingFillAmount.minus(takerAssetAmountAvailable),
|
||||
),
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
resultOrders: [] as T[],
|
||||
remainingFillAmount: totalFillAmount,
|
||||
ordersRemainingFillableTakerAssetAmounts: [] as BigNumber[],
|
||||
},
|
||||
);
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* Takes an array of orders and returns a subset of those orders that has enough makerAssetAmount
|
||||
@@ -37,7 +92,7 @@ export const marketUtils = {
|
||||
orders: T[],
|
||||
makerAssetFillAmount: BigNumber,
|
||||
opts?: FindOrdersThatCoverMakerAssetFillAmountOpts,
|
||||
): OrdersAndRemainingFillAmount<T> {
|
||||
): OrdersAndRemainingMakerFillAmount<T> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
assert.isValidBaseUnitAmount('makerAssetFillAmount', makerAssetFillAmount);
|
||||
// try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from orders
|
||||
|
||||
@@ -34,19 +34,18 @@ export interface CreateOrderOpts {
|
||||
*/
|
||||
export interface FindOrdersThatCoverMakerAssetFillAmountOpts {
|
||||
remainingFillableMakerAssetAmounts?: BigNumber[];
|
||||
remainingFillableTakerAssetAmounts?: BigNumber[];
|
||||
slippageBufferAmount?: BigNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* remainingFillableTakerAssetAmount: An array of BigNumbers corresponding to the `orders` parameter.
|
||||
* remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `orders` parameter.
|
||||
* You can use `OrderStateUtils` `@0x/order-utils` to perform blockchain lookups for these values.
|
||||
* Defaults to `makerAssetAmount` values from the orders param.
|
||||
* slippageBufferAmount: An additional amount of makerAsset to be covered by the result in case of trade collisions or partial fills.
|
||||
* Defaults to 0
|
||||
*/
|
||||
export interface FindOrdersThatCoverTakerAssetFillAmountOpts {
|
||||
remainingFillableMakerAssetAmounts?: BigNumber[];
|
||||
remainingFillableTakerAssetAmounts?: BigNumber[];
|
||||
slippageBufferAmount?: BigNumber;
|
||||
}
|
||||
|
||||
@@ -72,8 +71,14 @@ export interface FeeOrdersAndRemainingFeeAmount<T> {
|
||||
remainingFeeAmount: BigNumber;
|
||||
}
|
||||
|
||||
export interface OrdersAndRemainingFillAmount<T> {
|
||||
export interface OrdersAndRemainingMakerFillAmount<T> {
|
||||
resultOrders: T[];
|
||||
ordersRemainingFillableMakerAssetAmounts: BigNumber[];
|
||||
remainingFillAmount: BigNumber;
|
||||
}
|
||||
|
||||
export interface OrdersAndRemainingTakerFillAmount<T> {
|
||||
resultOrders: T[];
|
||||
ordersRemainingFillableTakerAssetAmounts: BigNumber[];
|
||||
remainingFillAmount: BigNumber;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,135 @@ const expect = chai.expect;
|
||||
|
||||
// tslint:disable: no-unused-expression
|
||||
describe('marketUtils', () => {
|
||||
describe('#findOrdersThatCoverTakerAssetFillAmount', () => {
|
||||
describe('no orders', () => {
|
||||
it('returns empty and unchanged remainingFillAmount', async () => {
|
||||
const fillAmount = new BigNumber(10);
|
||||
const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount(
|
||||
[],
|
||||
fillAmount,
|
||||
);
|
||||
expect(resultOrders).to.be.empty;
|
||||
expect(remainingFillAmount).to.be.bignumber.equal(fillAmount);
|
||||
});
|
||||
});
|
||||
describe('orders are completely fillable', () => {
|
||||
// generate three signed orders each with 10 units of makerAsset, 30 total
|
||||
const takerAssetAmount = new BigNumber(10);
|
||||
const inputOrders = testOrderFactory.generateTestSignedOrders(
|
||||
{
|
||||
takerAssetAmount,
|
||||
},
|
||||
3,
|
||||
);
|
||||
it('returns input orders and zero remainingFillAmount when input exactly matches requested fill amount', async () => {
|
||||
// try to fill 20 units of makerAsset
|
||||
// include 10 units of slippageBufferAmount
|
||||
const fillAmount = new BigNumber(20);
|
||||
const slippageBufferAmount = new BigNumber(10);
|
||||
const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount(
|
||||
inputOrders,
|
||||
fillAmount,
|
||||
{
|
||||
slippageBufferAmount,
|
||||
},
|
||||
);
|
||||
expect(resultOrders).to.be.deep.equal(inputOrders);
|
||||
expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('returns input orders and zero remainingFillAmount when input has more than requested fill amount', async () => {
|
||||
// try to fill 15 units of makerAsset
|
||||
// include 10 units of slippageBufferAmount
|
||||
const fillAmount = new BigNumber(15);
|
||||
const slippageBufferAmount = new BigNumber(10);
|
||||
const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount(
|
||||
inputOrders,
|
||||
fillAmount,
|
||||
{
|
||||
slippageBufferAmount,
|
||||
},
|
||||
);
|
||||
expect(resultOrders).to.be.deep.equal(inputOrders);
|
||||
expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('returns input orders and non-zero remainingFillAmount when input has less than requested fill amount', async () => {
|
||||
// try to fill 30 units of makerAsset
|
||||
// include 5 units of slippageBufferAmount
|
||||
const fillAmount = new BigNumber(30);
|
||||
const slippageBufferAmount = new BigNumber(5);
|
||||
const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount(
|
||||
inputOrders,
|
||||
fillAmount,
|
||||
{
|
||||
slippageBufferAmount,
|
||||
},
|
||||
);
|
||||
expect(resultOrders).to.be.deep.equal(inputOrders);
|
||||
expect(remainingFillAmount).to.be.bignumber.equal(new BigNumber(5));
|
||||
});
|
||||
it('returns first order and zero remainingFillAmount when requested fill amount is exactly covered by the first order', async () => {
|
||||
// try to fill 10 units of makerAsset
|
||||
const fillAmount = new BigNumber(10);
|
||||
const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount(
|
||||
inputOrders,
|
||||
fillAmount,
|
||||
);
|
||||
expect(resultOrders).to.be.deep.equal([inputOrders[0]]);
|
||||
expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('returns first two orders and zero remainingFillAmount when requested fill amount is over covered by the first two order', async () => {
|
||||
// try to fill 15 units of makerAsset
|
||||
const fillAmount = new BigNumber(15);
|
||||
const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount(
|
||||
inputOrders,
|
||||
fillAmount,
|
||||
);
|
||||
expect(resultOrders).to.be.deep.equal([inputOrders[0], inputOrders[1]]);
|
||||
expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
});
|
||||
describe('orders are partially fillable', () => {
|
||||
// generate three signed orders each with 10 units of makerAsset, 30 total
|
||||
const takerAssetAmount = new BigNumber(10);
|
||||
const inputOrders = testOrderFactory.generateTestSignedOrders(
|
||||
{
|
||||
takerAssetAmount,
|
||||
},
|
||||
3,
|
||||
);
|
||||
// generate remainingFillableMakerAssetAmounts that cover different partial fill scenarios
|
||||
// 1. order is completely filled already
|
||||
// 2. order is partially fillable
|
||||
// 3. order is completely fillable
|
||||
const remainingFillableTakerAssetAmounts = [constants.ZERO_AMOUNT, new BigNumber(5), takerAssetAmount];
|
||||
it('returns last two orders and non-zero remainingFillAmount when trying to fill original takerAssetAmounts', async () => {
|
||||
// try to fill 30 units of takerAsset
|
||||
const fillAmount = new BigNumber(30);
|
||||
const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount(
|
||||
inputOrders,
|
||||
fillAmount,
|
||||
{
|
||||
remainingFillableTakerAssetAmounts,
|
||||
},
|
||||
);
|
||||
expect(resultOrders).to.be.deep.equal([inputOrders[1], inputOrders[2]]);
|
||||
expect(remainingFillAmount).to.be.bignumber.equal(new BigNumber(15));
|
||||
});
|
||||
it('returns last two orders and zero remainingFillAmount when trying to fill exactly takerAssetAmounts remaining', async () => {
|
||||
// try to fill 15 units of takerAsset
|
||||
const fillAmount = new BigNumber(15);
|
||||
const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverTakerAssetFillAmount(
|
||||
inputOrders,
|
||||
fillAmount,
|
||||
{
|
||||
remainingFillableTakerAssetAmounts,
|
||||
},
|
||||
);
|
||||
expect(resultOrders).to.be.deep.equal([inputOrders[1], inputOrders[2]]);
|
||||
expect(remainingFillAmount).to.be.bignumber.equal(new BigNumber(0));
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('#findOrdersThatCoverMakerAssetFillAmount', () => {
|
||||
describe('no orders', () => {
|
||||
it('returns empty and unchanged remainingFillAmount', async () => {
|
||||
|
||||
Reference in New Issue
Block a user