Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into tu/take-profit
Browse files Browse the repository at this point in the history
  • Loading branch information
tyleroooo committed Jan 27, 2025
2 parents d10d00a + 16f5913 commit 8dc4eba
Show file tree
Hide file tree
Showing 22 changed files with 478 additions and 81 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"@dydxprotocol/v4-localization": "^1.1.259",
"@dydxprotocol/v4-proto": "^7.0.0-dev.0",
"@emotion/is-prop-valid": "^1.3.0",
"@funkit/connect": "^4.1.6",
"@funkit/connect": "^4.1.7",
"@hugocxl/react-to-image": "^0.0.9",
"@js-joda/core": "^5.5.3",
"@keplr-wallet/types": "^0.12.121",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 11 additions & 14 deletions src/abacus-ts/calculators/accountActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,14 @@ function addUsdcAssetPosition(
});
}

export type UsdcDepositArgs = {
subaccountNumber: number;
depositAmount: string;
};

export function createUsdcDepositOperations(
parentSubaccount: ParentSubaccountData,
{
subaccountNumber,
depositAmount,
}: {
subaccountNumber: number;
depositAmount: string;
}
{ subaccountNumber, depositAmount }: UsdcDepositArgs
): SubaccountBatchedOperations {
const updatedParentSubaccountData = addUsdcAssetPosition(parentSubaccount, {
side: IndexerPositionSide.LONG,
Expand All @@ -107,15 +106,13 @@ export function createUsdcDepositOperations(
};
}

export type UsdcWithdrawArgs = {
subaccountNumber: number;
withdrawAmount: string;
};
export function createUsdcWithdrawalOperations(
parentSubaccount: ParentSubaccountData,
{
subaccountNumber,
withdrawAmount,
}: {
subaccountNumber: number;
withdrawAmount: string;
}
{ subaccountNumber, withdrawAmount }: UsdcWithdrawArgs
): SubaccountBatchedOperations {
const updatedParentSubaccountData = addUsdcAssetPosition(parentSubaccount, {
side: IndexerPositionSide.SHORT,
Expand Down
9 changes: 8 additions & 1 deletion src/abacus-ts/calculators/funding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ export const getDirectionFromFundingRate = (fundingRate: string) => {
: FundingDirection.ToLong;
};

export const mapFundingChartObject = (funding: IndexerHistoricalFundingResponseObject) => ({
export type HistoricalFundingObject = {
fundingRate: number;
time: number;
direction: FundingDirection;
};
export const mapFundingChartObject = (
funding: IndexerHistoricalFundingResponseObject
): HistoricalFundingObject => ({
fundingRate: MustBigNumber(funding.rate).toNumber(),
time: new Date(funding.effectiveAt).getTime(),
direction: getDirectionFromFundingRate(funding.rate),
Expand Down
25 changes: 17 additions & 8 deletions src/abacus-ts/calculators/markets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,27 @@ function calculateDerivedMarketDisplayItems(market: IndexerWsBaseMarketObject) {
};
}

function calculatePriceChangePercent(
priceChange24H: string | null | undefined,
oraclePrice: string | null | undefined
): BigNumber | null {
if (priceChange24H == null || oraclePrice == null) {
return null;
}

const price24hAgo = MustBigNumber(oraclePrice).minus(priceChange24H);
return price24hAgo.gt(0) ? MustBigNumber(priceChange24H).div(price24hAgo) : null;
}

function calculateDerivedMarketCore(market: IndexerWsBaseMarketObject) {
return {
effectiveInitialMarginFraction:
getMarketEffectiveInitialMarginForMarket(market)?.toNumber() ?? null,
openInterestUSDC: MustBigNumber(market.openInterest)
.times(market.oraclePrice ?? 0)
.toNumber(),
percentChange24h: MustBigNumber(market.oraclePrice).isZero()
? null
: MustBigNumber(market.priceChange24H)
.div(market.oraclePrice ?? 0)
.toNumber(),
percentChange24h:
calculatePriceChangePercent(market.priceChange24H, market.oraclePrice)?.toNumber() ?? null,
stepSizeDecimals: MaybeBigNumber(market.stepSize)?.decimalPlaces() ?? TOKEN_DECIMALS,
tickSizeDecimals: MaybeBigNumber(market.tickSize)?.decimalPlaces() ?? USD_DECIMALS,
};
Expand All @@ -110,7 +119,7 @@ export function formatSparklineData(sparklines?: {
if (sparklines == null) return sparklines;
return mapValues(sparklines, (map) => {
return mapValues(map, (sparkline) => {
return sparkline.map((point) => MustBigNumber(point).toNumber());
return sparkline.map((point) => MustBigNumber(point).toNumber()).toReversed();
});
});
}
Expand All @@ -120,9 +129,9 @@ export function createMarketSummary(
sparklines: PerpetualMarketSparklines | undefined,
assetInfo: AllAssetData | undefined,
listOfFavorites: string[]
): PerpetualMarketSummaries | null {
): PerpetualMarketSummaries | undefined {
if (markets == null || assetInfo == null) {
return null;
return undefined;
}

return pickBy(
Expand Down
155 changes: 155 additions & 0 deletions src/abacus-ts/calculators/orderbook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import BigNumber from 'bignumber.js';
import { weakMapMemoize } from 'reselect';

import { isTruthy } from '@/lib/isTruthy';
import { BIG_NUMBERS, MustBigNumber } from '@/lib/numbers';
import { objectEntries } from '@/lib/objectHelpers';

import { OrderbookData } from '../types/rawTypes';
import { OrderbookLine, OrderbookProcessedData } from '../types/summaryTypes';

type OrderbookLineBN = {
price: BigNumber;
size: BigNumber;
sizeCost: BigNumber;
offset: number;
depth?: BigNumber;
depthCost?: BigNumber;
};

export const calculateOrderbook = weakMapMemoize(
(orderbook: OrderbookData | undefined): OrderbookProcessedData | undefined => {
if (orderbook == null) {
return undefined;
}

const { asks, bids } = orderbook;

/**
* 1. Process raw orderbook data
* 2. filter out lines with size <= 0
* 3. sort by price
*/
const asksBase: OrderbookLineBN[] = objectEntries(asks)
.map(mapRawOrderbookLineToBN)
.filter(isTruthy)
.sort((a, b) => a.price.minus(b.price).toNumber());

const bidsBase: OrderbookLineBN[] = objectEntries(bids)
.map(mapRawOrderbookLineToBN)
.filter(isTruthy)
.sort((a, b) => a.price.minus(b.price).toNumber());

// calculate depth and depthCost
const asksComposite = calculateDepthAndDepthCost(asksBase);
const bidsComposite = calculateDepthAndDepthCost(bidsBase);

// un-cross orderbook
const uncrossedBook: { asks: OrderbookLineBN[]; bids: OrderbookLineBN[] } = uncrossOrderbook(
asksComposite,
bidsComposite
);

// calculate midPrice, spread, and spreadPercent
const lowestAsk = uncrossedBook.asks.at(0);
const highestBid = uncrossedBook.bids.at(-1);
const midPriceBN = lowestAsk && highestBid && lowestAsk.price.plus(highestBid.price).div(2);
const spreadBN = lowestAsk && highestBid && lowestAsk.price.minus(highestBid.price);
const spreadPercentBN = spreadBN && midPriceBN && spreadBN.div(midPriceBN).times(100);

return {
bids: uncrossedBook.bids.map(mapOrderbookLineToNumber),
asks: uncrossedBook.asks.map(mapOrderbookLineToNumber),
midPrice: midPriceBN?.toNumber(),
spread: spreadBN?.toNumber(),
spreadPercent: spreadPercentBN?.toNumber(),
};
}
);

function uncrossOrderbook(asks: OrderbookLineBN[], bids: OrderbookLineBN[]) {
if (asks.length === 0 || bids.length === 0) {
return { asks, bids };
}

const asksCopy = [...asks];
const bidsCopy = [...bids];

let ask = asksCopy.at(-1);
let bid = bidsCopy.at(0);

while (ask && bid && isCrossed(ask, bid)) {
if (ask.offset === bid.offset) {
// If offsets are the same, give precedence to the larger size. In this case,
// one of the sizes "should" be zero, but we simply check for the larger size.
if (ask.size.gt(bid.size)) {
// remove the bid
bid = bidsCopy.shift();
} else {
// remove the ask
ask = asksCopy.shift();
}
} else {
// If offsets are different, remove the older offset.
if (ask.offset < bid.offset) {
// remove the ask
ask = asksCopy.shift();
} else {
// remove the bid
bid = bidsCopy.shift();
}
}
}

return { asks: asksCopy, bids: bidsCopy };
}

function calculateDepthAndDepthCost(lines: OrderbookLineBN[]) {
let depth = BIG_NUMBERS.ZERO;
let depthCost = BIG_NUMBERS.ZERO;

return lines.map((line) => {
depth = depth.plus(line.size);
depthCost = depthCost.plus(line.sizeCost);

return {
...line,
depth,
depthCost,
};
});
}

function mapRawOrderbookLineToBN(
rawOrderbookLineEntry: [string, { size: string; offset: number }]
) {
const [price, { size, offset }] = rawOrderbookLineEntry;
const sizeBN = MustBigNumber(size);
const priceBN = MustBigNumber(price);

if (sizeBN.isZero()) return undefined;

return {
price: priceBN,
size: sizeBN,
sizeCost: priceBN.times(sizeBN),
offset,
};
}

function mapOrderbookLineToNumber(orderbookLineBN: OrderbookLineBN): OrderbookLine {
const { price, size, sizeCost, depth, depthCost, offset } = orderbookLineBN;

return {
price: price.toNumber(),
size: size.toNumber(),
sizeCost: sizeCost.toNumber(),
offset,
depth: depth?.toNumber() ?? 0,
depthCost: depthCost?.toNumber() ?? 0,
};
}

function isCrossed(ask: OrderbookLineBN, bid: OrderbookLineBN) {
return ask.price.lte(bid.price);
}
1 change: 1 addition & 0 deletions src/abacus-ts/lib/loadable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type LoadableSuccess<T> = { status: 'success'; data: T };
export type LoadableIdle = { status: 'idle'; data: undefined };
export type LoadableError<T> = { status: 'error'; data?: T; error: any };
export type Loadable<T> = LoadableIdle | LoadableSuccess<T> | LoadablePending<T> | LoadableError<T>;
export type LoadableStatus = Loadable<any>['status'];

export function loadablePending<T>() {
return { status: 'pending' } as LoadablePending<T>;
Expand Down
Loading

0 comments on commit 8dc4eba

Please sign in to comment.