Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

Commit

Permalink
feat: Depth Chart API endpoint (#290)
Browse files Browse the repository at this point in the history
* feat: Depth Chart API endpoint

* added tests

* fix excessive accumulation when multiple prices land in the same bucket

* Update to latest asset-swapper

* refactor and accumulate sources over buckets
  • Loading branch information
dekz authored Jul 27, 2020
1 parent 5736c74 commit 540f92e
Show file tree
Hide file tree
Showing 10 changed files with 718 additions and 6 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
},
"dependencies": {
"@0x/assert": "^3.0.4",
"@0x/asset-swapper": "0xProject/gitpkg-registry#0x-asset-swapper-v4.6.0-9a16f5736",
"@0x/asset-swapper": "0xProject/gitpkg-registry#0x-asset-swapper-v4.6.0-ae2a6fb68",
"@0x/connect": "^6.0.4",
"@0x/contract-addresses": "^4.11.0",
"@0x/contract-wrappers": "^13.7.0",
Expand Down
4 changes: 2 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ export const ASSET_SWAPPER_MARKET_ORDERS_V0_OPTS: Partial<SwapQuoteRequestOpts>
feeSchedule: FEE_SCHEDULE_V0,
gasSchedule: GAS_SCHEDULE_V0,
shouldBatchBridgeOrders: true,
runLimit: 2 ** 13,
runLimit: 2 ** 8,
};

export const GAS_SCHEDULE_V1: FeeSchedule = {
Expand All @@ -356,7 +356,7 @@ export const ASSET_SWAPPER_MARKET_ORDERS_V1_OPTS: Partial<SwapQuoteRequestOpts>
feeSchedule: FEE_SCHEDULE_V1,
gasSchedule: GAS_SCHEDULE_V1,
shouldBatchBridgeOrders: false,
runLimit: 2 ** 13,
runLimit: 2 ** 8,
};

export const SAMPLER_OVERRIDES: SamplerOverrides | undefined = (() => {
Expand Down
5 changes: 5 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,8 @@ export const GST2_WALLET_ADDRESSES = {
[ChainId.Kovan]: NULL_ADDRESS,
[ChainId.Ganache]: NULL_ADDRESS,
};

// Market Depth
export const MARKET_DEPTH_MAX_SAMPLES = 50;
export const MARKET_DEPTH_DEFAULT_DISTRIBUTION = 1.05;
export const MARKET_DEPTH_END_PRICE_SLIPPAGE_PERC = 20;
28 changes: 27 additions & 1 deletion src/handlers/swap_handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import * as express from 'express';
import * as HttpStatus from 'http-status-codes';

import { CHAIN_ID } from '../config';
import { DEFAULT_QUOTE_SLIPPAGE_PERCENTAGE, SWAP_DOCS_URL } from '../constants';
import {
DEFAULT_QUOTE_SLIPPAGE_PERCENTAGE,
MARKET_DEPTH_DEFAULT_DISTRIBUTION,
MARKET_DEPTH_MAX_SAMPLES,
SWAP_DOCS_URL,
} from '../constants';
import {
InternalServerError,
RevertAPIError,
Expand Down Expand Up @@ -125,6 +130,27 @@ export class SwapHandlers {
const records = await this._swapService.getTokenPricesAsync(baseAsset, unitAmount);
res.status(HttpStatus.OK).send({ records });
}

public async getMarketDepthAsync(req: express.Request, res: express.Response): Promise<void> {
const makerToken = getTokenMetadataIfExists(req.query.buyToken as string, CHAIN_ID);
const takerToken = getTokenMetadataIfExists(req.query.sellToken as string, CHAIN_ID);
const response = await this._swapService.calculateMarketDepthAsync({
buyToken: makerToken,
sellToken: takerToken,
sellAmount: new BigNumber(req.query.sellAmount as string),
// tslint:disable-next-line:radix custom-no-magic-numbers
numSamples: req.query.numSamples ? parseInt(req.query.numSamples as string) : MARKET_DEPTH_MAX_SAMPLES,
sampleDistributionBase: req.query.sampleDistributionBase
? parseFloat(req.query.sampleDistributionBase as string)
: MARKET_DEPTH_DEFAULT_DISTRIBUTION,
excludedSources:
req.query.excludedSources === undefined
? []
: parseUtils.parseStringArrForERC20BridgeSources((req.query.excludedSources as string).split(',')),
});
res.status(HttpStatus.OK).send({ ...response, buyToken: makerToken, sellToken: takerToken });
}

private async _calculateSwapQuoteAsync(
params: GetSwapQuoteRequestParams,
swapVersion: SwapVersion,
Expand Down
1 change: 1 addition & 0 deletions src/routers/swap_router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function createSwapRouter(swapService: SwapService): express.Router {
router.get('/v0', asyncHandler(SwapHandlers.rootAsync.bind(SwapHandlers)));
router.get('/v0/prices', asyncHandler(handlers.getTokenPricesAsync.bind(handlers)));
router.get('/v0/tokens', asyncHandler(handlers.getSwapTokensAsync.bind(handlers)));
router.get('/v0/depth', asyncHandler(handlers.getMarketDepthAsync.bind(handlers)));

router.get('/v0/quote', asyncHandler(handlers.getSwapQuoteAsync.bind(handlers, SwapVersion.V0)));
router.get('/v0/price', asyncHandler(handlers.getSwapPriceAsync.bind(handlers, SwapVersion.V0)));
Expand Down
56 changes: 56 additions & 0 deletions src/services/swap_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SwapQuoteRequestOpts, SwapQuoterOpts } from '@0x/asset-swapper/lib/src/
import { ContractAddresses, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { ERC20TokenContract, WETH9Contract } from '@0x/contract-wrappers';
import { assetDataUtils, SupportedProvider } from '@0x/order-utils';
import { MarketOperation } from '@0x/types';
import { BigNumber, decodeThrownErrorAsRevertError, RevertError } from '@0x/utils';
import { TxData, Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
Expand All @@ -38,6 +39,8 @@ import { InsufficientFundsError } from '../errors';
import { logger } from '../logger';
import { TokenMetadatasForChains } from '../token_metadatas_for_networks';
import {
BucketedPriceDepth,
CalaculateMarketDepthParams,
CalculateSwapQuoteParams,
GetSwapQuoteResponse,
GetTokenPricesResponse,
Expand All @@ -46,6 +49,7 @@ import {
SwapVersion,
TokenMetadata,
} from '../types';
import { marketDepthUtils } from '../utils/market_depth_utils';
import { createResultCache, ResultCache } from '../utils/result_cache';
import { serviceUtils } from '../utils/service_utils';
import { getTokenMetadataIfExists } from '../utils/token_metadata_utils';
Expand Down Expand Up @@ -269,6 +273,58 @@ export class SwapService {
return prices;
}

public async calculateMarketDepthAsync(
params: CalaculateMarketDepthParams,
): Promise<{
asks: { depth: BucketedPriceDepth[] };
bids: { depth: BucketedPriceDepth[] };
}> {
const { buyToken, sellToken, sellAmount, numSamples, sampleDistributionBase, excludedSources } = params;
const marketDepth = await this._swapQuoter.getBidAskLiquidityForMakerTakerAssetPairAsync(
buyToken.tokenAddress,
sellToken.tokenAddress,
sellAmount,
{
numSamples,
excludedSources: [...(excludedSources || []), ERC20BridgeSource.MultiBridge],
sampleDistributionBase,
},
);

const maxEndSlippagePercentage = 20;
const scalePriceByDecimals = (priceDepth: BucketedPriceDepth[]) =>
priceDepth.map(b => ({
...b,
price: b.price.times(new BigNumber(10).pow(sellToken.decimals - buyToken.decimals)),
}));
const askDepth = scalePriceByDecimals(
marketDepthUtils.calculateDepthForSide(
marketDepth.asks,
MarketOperation.Sell,
numSamples * 2,
sampleDistributionBase,
maxEndSlippagePercentage,
),
);
const bidDepth = scalePriceByDecimals(
marketDepthUtils.calculateDepthForSide(
marketDepth.bids,
MarketOperation.Buy,
numSamples * 2,
sampleDistributionBase,
maxEndSlippagePercentage,
),
);
return {
// We're buying buyToken and SELLING sellToken (DAI) (50k)
// Price goes from HIGH to LOW
asks: { depth: askDepth },
// We're BUYING sellToken (DAI) (50k) and selling buyToken
// Price goes from LOW to HIGH
bids: { depth: bidDepth },
};
}

private async _getSwapQuoteForWethAsync(
params: CalculateSwapQuoteParams,
isUnwrap: boolean,
Expand Down
16 changes: 16 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -613,4 +613,20 @@ export interface HttpServiceConfig {
meshHttpUri?: string;
metaTxnRateLimiters?: MetaTransactionRateLimitConfig;
}

export interface CalaculateMarketDepthParams {
buyToken: TokenMetadata;
sellToken: TokenMetadata;
sellAmount: BigNumber;
numSamples: number;
sampleDistributionBase: number;
excludedSources?: ERC20BridgeSource[];
}

export interface BucketedPriceDepth {
cumulative: BigNumber;
price: BigNumber;
bucket: number;
bucketTotal: BigNumber;
}
// tslint:disable-line:max-file-line-count
Loading

0 comments on commit 540f92e

Please sign in to comment.