119 lines
4.5 KiB
TypeScript
119 lines
4.5 KiB
TypeScript
import { ObjectMap } from '@0x/types';
|
|
import { BigNumber } from '@0x/utils';
|
|
import { RadarBook, RadarMarket, RadarSignedOrder } from '@radarrelay/types';
|
|
import * as R from 'ramda';
|
|
|
|
import { aggregateOrders, GenericRawOrder } from '../utils';
|
|
|
|
import { RADAR_SOURCE } from '../../data_sources/radar';
|
|
import { TokenOrderbookSnapshot as TokenOrder } from '../../entities';
|
|
import { OrderType } from '../../types';
|
|
|
|
export interface AggregateOrdersByMaker {
|
|
makerAddress: string;
|
|
price: string;
|
|
amount: BigNumber;
|
|
}
|
|
|
|
/**
|
|
* Marquee function of this file.
|
|
* 1) Takes in orders from an orderbook,
|
|
* other information attached.
|
|
* @param radarOrderbook A raw orderbook that we pull from the radar API.
|
|
* @param radarMarket An object containing market data also directly from the API.
|
|
* @param observedTimestamp Time at which the orders for the market were pulled.
|
|
*/
|
|
export function parseRadarOrders(
|
|
radarOrderbook: RadarBook,
|
|
radarMarket: RadarMarket,
|
|
observedTimestamp: number,
|
|
): TokenOrder[] {
|
|
const aggregatedBids = _aggregateOrdersByMaker(radarMarket, radarOrderbook.bids);
|
|
const aggregatedAsks = _aggregateOrdersByMaker(radarMarket, radarOrderbook.asks);
|
|
const parsedBids = aggregatedBids.map(order =>
|
|
parseRadarOrder(radarMarket, observedTimestamp, OrderType.Bid, order),
|
|
);
|
|
const parsedAsks = aggregatedAsks.map(order =>
|
|
parseRadarOrder(radarMarket, observedTimestamp, OrderType.Ask, order),
|
|
);
|
|
return parsedBids.concat(parsedAsks);
|
|
}
|
|
|
|
/**
|
|
* Parse a single aggregated radar order in order to form a tokenOrder entity
|
|
* which can be saved into the database.
|
|
* @param radarMarket An object containing information about the market where these
|
|
* trades have been placed.
|
|
* @param observedTimestamp The time when the API response returned back to us.
|
|
* @param orderType 'bid' or 'ask' enum.
|
|
* @param aggregateOrder An AggregateOrdersByMaker instance which we will convert to volume-basis.
|
|
*/
|
|
export function parseRadarOrder(
|
|
radarMarket: RadarMarket,
|
|
observedTimestamp: number,
|
|
orderType: OrderType,
|
|
aggregateOrder: AggregateOrdersByMaker,
|
|
): TokenOrder {
|
|
const tokenOrder = new TokenOrder();
|
|
const price = new BigNumber(aggregateOrder.price);
|
|
const amount = aggregateOrder.amount;
|
|
const splitId = radarMarket.id.split('-');
|
|
|
|
tokenOrder.source = RADAR_SOURCE;
|
|
tokenOrder.observedTimestamp = observedTimestamp;
|
|
tokenOrder.orderType = orderType;
|
|
tokenOrder.price = price;
|
|
|
|
tokenOrder.baseAssetSymbol = splitId[0];
|
|
tokenOrder.baseAssetAddress = radarMarket.baseTokenAddress || null;
|
|
tokenOrder.baseVolume = amount;
|
|
|
|
tokenOrder.quoteAssetSymbol = splitId[1];
|
|
tokenOrder.quoteAssetAddress = radarMarket.quoteTokenAddress || null;
|
|
tokenOrder.quoteVolume = price.times(amount);
|
|
|
|
tokenOrder.makerAddress = aggregateOrder.makerAddress;
|
|
return tokenOrder;
|
|
}
|
|
|
|
function _toGeneric(radarMarket: RadarMarket, radarOrder: RadarSignedOrder): GenericRawOrder | undefined {
|
|
if (radarMarket.baseTokenDecimals === undefined) {
|
|
return undefined;
|
|
}
|
|
return {
|
|
price: radarOrder.price.toString(),
|
|
// Use the remaining fillable amount
|
|
amount: radarOrder.remainingBaseTokenAmount.toString(),
|
|
};
|
|
}
|
|
|
|
function _aggregateOrdersByMaker(radarMarket: RadarMarket, radarOrders: RadarSignedOrder[]): AggregateOrdersByMaker[] {
|
|
// group all orders by their maker
|
|
const ordersByMaker: ObjectMap<RadarSignedOrder[]> = radarOrders.reduce(
|
|
(acc: ObjectMap<RadarSignedOrder[]>, val: RadarSignedOrder) => {
|
|
const makerAddress = val.signedOrder.makerAddress;
|
|
if (acc[makerAddress]) {
|
|
acc[makerAddress].push(val);
|
|
} else {
|
|
acc[makerAddress] = [];
|
|
}
|
|
return acc;
|
|
},
|
|
{},
|
|
);
|
|
const transformToGeneric = (radarOrder: RadarSignedOrder) => _toGeneric(radarMarket, radarOrder);
|
|
const aggregationTuples: AggregateOrdersByMaker[][] = (R.keys(ordersByMaker) as string[]).map((maker: string) => {
|
|
const generalizedOrders = _removeUndefined(R.map(transformToGeneric, ordersByMaker[maker]));
|
|
const aggregatedOrders = aggregateOrders(generalizedOrders);
|
|
return aggregatedOrders.map((order: [string, BigNumber]) => ({
|
|
makerAddress: maker,
|
|
price: order[0],
|
|
amount: order[1],
|
|
}));
|
|
});
|
|
return R.unnest(aggregationTuples);
|
|
}
|
|
|
|
// tslint:disable-next-line:no-unbound-method
|
|
const _removeUndefined = R.reject(R.isNil);
|