[WIP] Slippage calculations from Ethereum DEX Prices Service (EDPS)

This commit is contained in:
askeluv
2019-02-08 18:54:38 +08:00
parent ffd8349889
commit 7ad4cb0078
7 changed files with 182 additions and 0 deletions

View 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;
}
}

View File

@@ -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';

View 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;
}

View File

@@ -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,

View 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;
}
}

View 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);

View 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);
}
});
});