[WIP] Slippage calculations from Ethereum DEX Prices Service (EDPS)
This commit is contained in:
59
packages/pipeline/src/data_sources/slippage/index.ts
Normal file
59
packages/pipeline/src/data_sources/slippage/index.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { fetchAsync, logUtils } from '@0x/utils';
|
||||
|
||||
const EDPS_BASE_URL = 'https://ethereum-dex-prices-service.production.airswap.io';
|
||||
const PRICE_BASE_URL = 'https://min-api.cryptocompare.com/data/price?tsyms=USD'
|
||||
|
||||
export type EdpsResponse = EdpsWrapper[]
|
||||
|
||||
export interface EdpsWrapper {
|
||||
[key: string]: EdpsExchange
|
||||
}
|
||||
|
||||
export interface EdpsExchange {
|
||||
exchangeName: string,
|
||||
totalPrice: number,
|
||||
tokenAmount: number,
|
||||
tokenSymbol: string,
|
||||
avgPrice: number,
|
||||
timestamp: number,
|
||||
error: string
|
||||
}
|
||||
|
||||
export interface PriceResponse {
|
||||
USD: number;
|
||||
}
|
||||
|
||||
// tslint:disable:prefer-function-over-method
|
||||
// ^ Keep consistency with other sources and help logical organization
|
||||
export class EdpsSource {
|
||||
/**
|
||||
* Call Ethereum DEX Price Service API.
|
||||
*/
|
||||
public async getEdpsAsync(direction: string, symbol: string, amount: number): Promise<Map<string, EdpsExchange>> {
|
||||
logUtils.log('Getting EDPS response');
|
||||
const edpsUrl = `${EDPS_BASE_URL}/${direction}?symbol=${symbol}&amount=${amount}`;
|
||||
const resp = await fetchAsync(edpsUrl);
|
||||
const respJson: EdpsResponse = await resp.json();
|
||||
const allExchanges = new Map<string, EdpsExchange>();
|
||||
for (let entry of respJson) {
|
||||
for (let key in entry) {
|
||||
allExchanges.set(key, entry[key]);
|
||||
}
|
||||
}
|
||||
logUtils.log(`Got ${allExchanges.size} exchanges.`);
|
||||
return allExchanges;
|
||||
}
|
||||
}
|
||||
|
||||
export class PriceSource {
|
||||
/**
|
||||
* Call CryptoCompare Price API to get USD price of token.
|
||||
*/
|
||||
public async getUsdPriceAsync(symbol: string): Promise<number> {
|
||||
logUtils.log(`Fetching USD price for ${symbol}`);
|
||||
const priceUrl = `${PRICE_BASE_URL}&fsym=${symbol}`
|
||||
const resp = await fetchAsync(priceUrl);
|
||||
const respJson: PriceResponse = await resp.json();
|
||||
return respJson.USD;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ export { ExchangeCancelUpToEvent } from './exchange_cancel_up_to_event';
|
||||
export { ExchangeFillEvent } from './exchange_fill_event';
|
||||
export { OHLCVExternal } from './ohlcv_external';
|
||||
export { Relayer } from './relayer';
|
||||
export { SlippageRecord } from './slippage';
|
||||
export { SraOrder } from './sra_order';
|
||||
export { SraOrdersObservedTimeStamp, createObservedTimestampForOrder } from './sra_order_observed_timestamp';
|
||||
export { TokenMetadata } from './token_metadata';
|
||||
|
||||
15
packages/pipeline/src/entities/slippage.ts
Normal file
15
packages/pipeline/src/entities/slippage.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Column, Entity, PrimaryColumn } from 'typeorm';
|
||||
|
||||
@Entity({ name: 'slippage_records', schema: 'raw' })
|
||||
export class SlippageRecord {
|
||||
@PrimaryColumn({ name: 'time', type: 'number'})
|
||||
public time!: number;
|
||||
@PrimaryColumn({ name: 'symbol' })
|
||||
public symbol!: string;
|
||||
@PrimaryColumn({ name: 'exchange' })
|
||||
public exchange!: string;
|
||||
@PrimaryColumn({ name: 'usdAmount', type: 'number' })
|
||||
public usdAmount!: number;
|
||||
@Column({ name: 'slippage', type: 'number' })
|
||||
public slippage!: number;
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
ExchangeFillEvent,
|
||||
OHLCVExternal,
|
||||
Relayer,
|
||||
SlippageRecord,
|
||||
SraOrder,
|
||||
SraOrdersObservedTimeStamp,
|
||||
TokenMetadata,
|
||||
@@ -35,6 +36,7 @@ const entities = [
|
||||
ERC20ApprovalEvent,
|
||||
OHLCVExternal,
|
||||
Relayer,
|
||||
SlippageRecord,
|
||||
SraOrder,
|
||||
SraOrdersObservedTimeStamp,
|
||||
TokenMetadata,
|
||||
|
||||
35
packages/pipeline/src/parsers/slippage/index.ts
Normal file
35
packages/pipeline/src/parsers/slippage/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as R from 'ramda';
|
||||
|
||||
import { EdpsExchange } from '../../data_sources/slippage';
|
||||
import { SlippageRecord } from '../../entities';
|
||||
import { symbol } from 'prop-types';
|
||||
|
||||
/**
|
||||
* Calculates slippage and returns SlippageRecord entity.
|
||||
*
|
||||
* @param usdAmount
|
||||
* @param exchange
|
||||
* @param buyEdps
|
||||
* @param sellEdps
|
||||
*/
|
||||
|
||||
export function calculateSlippage(usdAmount: number, exchange: string,
|
||||
buyEdps: Map<string, EdpsExchange>, sellEdps: Map<string, EdpsExchange>) {
|
||||
const b = buyEdps.get(exchange);
|
||||
const s = sellEdps.get(exchange);
|
||||
if (b && s && b.avgPrice && s.avgPrice) {
|
||||
var slippage = (b.avgPrice - s.avgPrice) / b.avgPrice;
|
||||
const observedTimestamp = Date.now();
|
||||
const slippageRecord = new SlippageRecord();
|
||||
slippageRecord.time = observedTimestamp;
|
||||
slippageRecord.symbol = b.tokenSymbol;
|
||||
slippageRecord.exchange = exchange;
|
||||
slippageRecord.usdAmount = usdAmount;
|
||||
slippageRecord.slippage = slippage;
|
||||
return slippageRecord;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
38
packages/pipeline/src/scripts/pull_slippage.ts
Normal file
38
packages/pipeline/src/scripts/pull_slippage.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import * as R from 'ramda';
|
||||
import { Connection, ConnectionOptions, createConnection, Repository } from 'typeorm';
|
||||
import { logUtils } from '@0x/utils';
|
||||
import { EdpsExchange, EdpsSource, PriceResponse, PriceSource } from '../data_sources/slippage';
|
||||
import { handleError } from '../utils';
|
||||
import { string, number } from 'prop-types';
|
||||
import { calculateSlippage } from '../parsers/slippage';
|
||||
import { SlippageRecord } from '../entities';
|
||||
import * as ormConfig from '../ormconfig';
|
||||
|
||||
// Number of orders to save at once.
|
||||
const BATCH_SAVE_SIZE = 1000;
|
||||
|
||||
// USD amounts for slippage depths
|
||||
const USD_AMOUNTS = [10, 100, 1000];
|
||||
const TOKENS = ['ZRX', 'MKR', 'DAI', 'KNC', 'BNB']; // TODO: fetch from database
|
||||
|
||||
(async () => {
|
||||
const priceSource = new PriceSource();
|
||||
const edpsSource = new EdpsSource();
|
||||
const resultsPerAmount = await TOKENS.map(async (symbol) => {
|
||||
const usdPrice = await priceSource.getUsdPriceAsync(symbol);
|
||||
USD_AMOUNTS.map(async (usdAmount) => {
|
||||
const amount = usdAmount / usdPrice;
|
||||
console.log(amount);
|
||||
const buyEdps = await edpsSource.getEdpsAsync('buy', symbol, amount);
|
||||
const sellEdps = await edpsSource.getEdpsAsync('sell', symbol, amount);
|
||||
|
||||
for(let exchange of buyEdps.keys()) {
|
||||
const slippageRecord = await calculateSlippage(usdAmount, exchange, buyEdps, sellEdps)
|
||||
if (slippageRecord)
|
||||
console.log(slippageRecord);
|
||||
}
|
||||
|
||||
}
|
||||
)});
|
||||
//process.exit(0);
|
||||
})().catch(handleError);
|
||||
32
packages/pipeline/test/entities/slippage.ts
Normal file
32
packages/pipeline/test/entities/slippage.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import 'mocha';
|
||||
import * as R from 'ramda';
|
||||
import 'reflect-metadata';
|
||||
|
||||
import { SlippageRecord } from '../../src/entities';
|
||||
import { createDbConnectionOnceAsync } from '../db_setup';
|
||||
import { chaiSetup } from '../utils/chai_setup';
|
||||
|
||||
import { testSaveAndFindEntityAsync } from './util';
|
||||
|
||||
chaiSetup.configure();
|
||||
|
||||
const slippageRecord = {
|
||||
time: 1234,
|
||||
symbol: 'ZRX',
|
||||
exchange: 'Paradex',
|
||||
usdAmount: 10,
|
||||
slippage: 0.01
|
||||
};
|
||||
|
||||
// tslint:disable:custom-no-magic-numbers
|
||||
describe('Slippage entity', () => {
|
||||
it('save/find', async () => {
|
||||
const connection = await createDbConnectionOnceAsync();
|
||||
const slippageRecords = [slippageRecord];
|
||||
const slippageRepository = connection.getRepository(SlippageRecord);
|
||||
for (const record of slippageRecords) {
|
||||
await testSaveAndFindEntityAsync(slippageRepository, record);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user