@0x/asset-swapper: Add exponential sampling, exposed by sampleDistributionBase.

`@0x/asset-swapper`: Disable ethgasstation polling in tests.
`@0x/asset-swapper`: Tweak default hyper parameters to be friendlier to big fills.
This commit is contained in:
Lawrence Forman
2020-01-15 16:41:32 -05:00
committed by Jacob Evans
parent 391f9b31f6
commit 7f56091fbd
10 changed files with 35 additions and 24 deletions

View File

@@ -13,6 +13,10 @@
{
"note": "Added `getBatchMarketBuySwapQuoteForAssetDataAsync` on `SwapQuoter`",
"pr": 2427
},
{
"note": "Add exponential sampling distribution and `sampleDistributionBase` option to `SwapQuoter`",
"pr": 2427
}
]
},

View File

@@ -24,12 +24,14 @@ export const SELL_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Da
export const BUY_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai];
export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
runLimit: 4096,
// tslint:disable-next-line: custom-no-magic-numbers
runLimit: 2 ** 15,
excludedSources: [],
bridgeSlippage: 0.0005,
dustFractionThreshold: 0.01,
numSamples: 10,
dustFractionThreshold: 0.0025,
numSamples: 12,
noConflicts: true,
sampleDistributionBase: 1.25,
};
export const constants = {
@@ -40,5 +42,5 @@ export const constants = {
DEFAULT_GET_MARKET_ORDERS_OPTS,
ERC20_PROXY_ID: '0xf47261b0',
WALLET_SIGNATURE: '0x04',
SAMPLER_CONTRACT_GAS_LIMIT: 10e6,
SAMPLER_CONTRACT_GAS_LIMIT: 16e6,
};

View File

@@ -63,7 +63,7 @@ export class MarketOperationUtils {
};
const [fillableAmounts, dexQuotes] = await this._dexSampler.getFillableAmountsAndSampleMarketSellAsync(
nativeOrders,
DexOrderSampler.getSampleAmounts(takerAmount, _opts.numSamples),
DexOrderSampler.getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase),
difference(SELL_SOURCES, _opts.excludedSources),
);
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
@@ -78,7 +78,6 @@ export class MarketOperationUtils {
_opts.dustFractionThreshold,
);
const clippedNativePath = clipPathToInput(prunedNativePath, takerAmount);
const dexPaths = createPathsFromDexQuotes(dexQuotes, _opts.noConflicts);
const allPaths = [...dexPaths];
const allFills = flattenDexPaths(dexPaths);
@@ -134,7 +133,7 @@ export class MarketOperationUtils {
const [fillableAmounts, dexQuotes] = await this._dexSampler.getFillableAmountsAndSampleMarketBuyAsync(
nativeOrders,
DexOrderSampler.getSampleAmounts(makerAmount, _opts.numSamples),
DexOrderSampler.getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase),
difference(BUY_SOURCES, _opts.excludedSources),
);
const signedOrderWithFillableAmounts = this._createBuyOrdersPathFromSamplerResultIfExists(

View File

@@ -13,16 +13,14 @@ export class DexOrderSampler {
/**
* Generate sample amounts up to `maxFillAmount`.
*/
public static getSampleAmounts(maxFillAmount: BigNumber, numSamples: number): BigNumber[] {
const amounts = [];
for (let i = 0; i < numSamples; i++) {
amounts.push(
maxFillAmount
.times(i + 1)
.div(numSamples)
.integerValue(BigNumber.ROUND_UP),
);
}
public static getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] {
const distribution = [...Array<BigNumber>(numSamples)].map((v, i) => new BigNumber(expBase).pow(i));
const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution)));
const amounts = stepSizes.map((s, i) => {
return maxFillAmount
.times(BigNumber.sum(...[0, ...stepSizes.slice(0, i + 1)]))
.integerValue(BigNumber.ROUND_UP);
});
return amounts;
}

View File

@@ -114,4 +114,12 @@ export interface GetMarketOrdersOpts {
* Default is 0.01 (100 basis points).
*/
dustFractionThreshold: number;
/**
* The exponential sampling distribution base.
* A value of 1 will result in evenly spaced samples.
* > 1 will result in more samples at lower sizes.
* < 1 will result in more samples at higher sizes.
* Default: 1.25.
*/
sampleDistributionBase: number;
}

View File

@@ -9,9 +9,9 @@ export class ProtocolFeeUtils {
public gasPriceEstimation: BigNumber;
private readonly _gasPriceHeart: any;
constructor(gasPricePollingIntervalInMs: number) {
constructor(gasPricePollingIntervalInMs: number, initialGasPrice: BigNumber = constants.ZERO_AMOUNT) {
this._gasPriceHeart = heartbeats.createHeart(gasPricePollingIntervalInMs);
this.gasPriceEstimation = constants.ZERO_AMOUNT;
this.gasPriceEstimation = initialGasPrice;
this._initializeHeartBeat();
}

View File

@@ -123,7 +123,7 @@ describe('ExchangeSwapQuoteConsumer', () => {
};
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS);
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, new BigNumber(1));
expectMakerAndTakerBalancesForTakerAssetAsync = expectMakerAndTakerBalancesAsyncFactory(
erc20TakerTokenContract,
makerAddress,

View File

@@ -126,7 +126,7 @@ describe('ForwarderSwapQuoteConsumer', () => {
};
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS);
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, new BigNumber(1));
expectMakerAndTakerBalancesAsync = expectMakerAndTakerBalancesAsyncFactory(
erc20TokenContract,
makerAddress,

View File

@@ -306,7 +306,7 @@ describe('MarketOperationUtils tests', () => {
_.times(3, () => SOURCE_RATES[ERC20BridgeSource.Native][0]),
);
const DEFAULT_SAMPLER = createSamplerFromSellRates(SOURCE_RATES);
const DEFAULT_OPTS = { numSamples: 3, runLimit: 0 };
const DEFAULT_OPTS = { numSamples: 3, runLimit: 0, sampleDistributionBase: 1 };
const defaultMarketOperationUtils = new MarketOperationUtils(
DEFAULT_SAMPLER,
contractAddresses,
@@ -552,7 +552,7 @@ describe('MarketOperationUtils tests', () => {
_.times(3, () => SOURCE_RATES[ERC20BridgeSource.Native][0]),
);
const DEFAULT_SAMPLER = createSamplerFromBuyRates(SOURCE_RATES);
const DEFAULT_OPTS = { numSamples: 3, runLimit: 0 };
const DEFAULT_OPTS = { numSamples: 3, runLimit: 0, sampleDistributionBase: 1 };
const defaultMarketOperationUtils = new MarketOperationUtils(
DEFAULT_SAMPLER,
contractAddresses,

View File

@@ -119,7 +119,7 @@ describe('swapQuoteConsumerUtils', () => {
};
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS);
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, new BigNumber(1));
forwarderOrderFactory = new OrderFactory(privateKey, defaultForwarderOrderParams);
swapQuoteConsumer = new SwapQuoteConsumer(provider, {