Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
tyleroooo committed Dec 13, 2024
1 parent c419034 commit eadb0b4
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 42 deletions.
115 changes: 100 additions & 15 deletions src/abacus-ts/calculators/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,77 @@ import {
IndexerPositionSide,
} from '@/types/indexer/indexerApiGen';
import BigNumber from 'bignumber.js';
import { mapValues } from 'lodash';

import { NUM_PARENT_SUBACCOUNTS } from '@/constants/account';

import { calc } from '@/lib/do';
import { MaybeBigNumber, MustBigNumber } from '@/lib/numbers';
import { MaybeBigNumber, MustBigNumber, ToBigNumber } from '@/lib/numbers';
import { isPresent } from '@/lib/typeUtils';

import { ChildSubaccountData, MarketsData } from '../rawTypes';
import { ChildSubaccountData, MarketsData, ParentSubaccountData } from '../rawTypes';
import {
GroupedSubaccountSummary,
SubaccountPosition,
SubaccountPositionBase,
SubaccountPositionDerivedCore,
SubaccountPositionDerivedExtra,
SubaccountSummary,
SubaccountSummaryCore,
SubaccountSummaryDerived,
} from '../summaryTypes';

const NUM_PARENT_SUBACCOUNTS = 128;
const BN_0 = MustBigNumber(0);
const BN_1 = MustBigNumber(1);

export function calculateSubaccountSummaryCore(
export function calculateParentSubaccountPositions(
parent: Omit<ParentSubaccountData, 'ephemeral'>,
markets: MarketsData
): SubaccountPosition[] {
return Object.values(parent.childSubaccounts)
.filter(isPresent)
.flatMap((child) => {
const subaccount = calculateSubaccountSummary(child, markets);
return Object.values(child.openPerpetualPositions)
.filter(isPresent)
.map((perp) => calculateSubaccountPosition(subaccount, perp, markets[perp.market]));
});
}

export function calculateParentSubaccountSummary(
parent: Omit<ParentSubaccountData, 'ephemeral'>,
markets: MarketsData
): GroupedSubaccountSummary {
const summaries = mapValues(parent.childSubaccounts, (subaccount) =>
subaccount != null ? calculateSubaccountSummary(subaccount, markets) : subaccount
);
const parentSummary = summaries[parent.parentSubaccount];
if (parentSummary == null) {
throw new Error('Parent subaccount not found in ParentSubaccountData');
}
return {
marginUsage: parentSummary.marginUsage,
leverage: parentSummary.leverage,
freeCollateral: parentSummary.freeCollateral,
equity: Object.values(summaries)
.filter(isPresent)
.map((s) => s.equity)
.reduce((a, b) => a.plus(b), BN_0),
};
}

function calculateSubaccountSummary(
subaccountData: ChildSubaccountData,
markets: MarketsData
): SubaccountSummary {
const core = calculateSubaccountSummaryCore(subaccountData, markets);
return {
...core,
...calculateSubaccountSummaryDerived(core),
};
}

function calculateSubaccountSummaryCore(
subaccountData: ChildSubaccountData,
markets: MarketsData
): SubaccountSummaryCore {
Expand All @@ -47,7 +100,7 @@ export function calculateSubaccountSummaryCore(
notional: positionNotional,
initialRisk: positionInitialRisk,
maintenanceRisk: positionMaintenanceRisk,
} = calculateDerivedPositionCore(position, market);
} = calculateDerivedPositionCore(getBnPosition(position), market);
return {
valueTotal: acc.valueTotal.plus(positionValue),
notionalTotal: acc.notionalTotal.plus(positionNotional),
Expand Down Expand Up @@ -94,17 +147,48 @@ function calculateSubaccountSummaryDerived(core: SubaccountSummaryCore): Subacco
};
}

function calculateDerivedPositionCore(
function calculateSubaccountPosition(
subaccountSummary: SubaccountSummary,
position: IndexerPerpetualPositionResponseObject,
market: IndexerPerpetualMarketResponseObject
market: IndexerPerpetualMarketResponseObject | undefined
): SubaccountPosition {
const bnPosition = getBnPosition(position);
const core = calculateDerivedPositionCore(bnPosition, market);
return {
...bnPosition,
...core,
...calculatePositionDerivedExtra(core, subaccountSummary),
};
}

function getBnPosition(position: IndexerPerpetualPositionResponseObject): SubaccountPositionBase {
return {
...position,
size: ToBigNumber(position.size),
maxSize: ToBigNumber(position.maxSize),
entryPrice: ToBigNumber(position.entryPrice),
realizedPnl: ToBigNumber(position.realizedPnl),
createdAtHeight: ToBigNumber(position.createdAtHeight),
sumOpen: ToBigNumber(position.sumOpen),
sumClose: ToBigNumber(position.sumClose),
netFunding: ToBigNumber(position.netFunding),
unrealizedPnl: ToBigNumber(position.unrealizedPnl),
exitPrice: position.exitPrice != null ? ToBigNumber(position.exitPrice) : position.exitPrice,
};
}

function calculateDerivedPositionCore(
position: SubaccountPositionBase,
market: IndexerPerpetualMarketResponseObject | undefined
): SubaccountPositionDerivedCore {
const marginMode = position.subaccountNumber < NUM_PARENT_SUBACCOUNTS ? 'CROSS' : 'ISOLATED';
const effectiveImf = getMarketEffectiveInitialMarginForMarket(market) ?? BN_0;
const effectiveMmf = MaybeBigNumber(market.maintenanceMarginFraction) ?? BN_0;
const effectiveImf =
market != null ? getMarketEffectiveInitialMarginForMarket(market) ?? BN_0 : BN_0;
const effectiveMmf = MaybeBigNumber(market?.maintenanceMarginFraction) ?? BN_0;

// indexer position size is already signed I think but we will be extra sure
const unsignedSize = MustBigNumber(position.size).abs();
const oracle = MustBigNumber(market.oraclePrice);
const unsignedSize = position.size.abs();
const oracle = MaybeBigNumber(market?.oraclePrice) ?? BN_0;
const signedSize =
position.side === IndexerPositionSide.SHORT ? unsignedSize.negated() : unsignedSize;

Expand All @@ -127,11 +211,12 @@ function calculateDerivedPositionCore(
}
return BN_1.div(effectiveImf);
}),
baseEntryPrice: position.entryPrice,
baseNetFunding: position.netFunding,
};
}

export function calculatePositionDerivedExtra(
basePosition: IndexerPerpetualPositionResponseObject,
function calculatePositionDerivedExtra(
position: SubaccountPositionDerivedCore,
subaccountSummary: SubaccountSummary
): SubaccountPositionDerivedExtra {
Expand Down Expand Up @@ -164,8 +249,8 @@ export function calculatePositionDerivedExtra(
unrealizedPnlInner: updatedUnrealizedPnl,
unrealizedPnlPercentInner: updatedUnrealizedPnlPercent,
} = calc(() => {
const entryValue = signedSize.multipliedBy(MustBigNumber(basePosition.entryPrice));
const unrealizedPnlInner = value.minus(entryValue).plus(MustBigNumber(basePosition.netFunding));
const entryValue = signedSize.multipliedBy(MustBigNumber(position.baseEntryPrice));
const unrealizedPnlInner = value.minus(entryValue).plus(MustBigNumber(position.baseNetFunding));

const scaledLeverage = leverage ? BigNumber.max(leverage.abs(), BN_1) : BN_1;

Expand Down
51 changes: 24 additions & 27 deletions src/abacus-ts/summaryTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,26 @@ export type SubaccountSummaryDerived = {
};

export type SubaccountSummary = SubaccountSummaryCore & SubaccountSummaryDerived;

// type SubaccountPositionCore = ConvertStringToBigNumber<
// IndexerPerpetualPositionResponseObject,
// | 'size'
// | 'maxSize'
// | 'entryPrice'
// | 'realizedPnl'
// | 'createdAtHeight'
// | 'sumOpen'
// | 'sumClose'
// | 'netFunding'
// | 'unrealizedPnl'
// | 'exitPrice'
// >;

export type SubaccountPositionBase = IndexerPerpetualPositionResponseObject;

export type SubaccountPositionDerivedArgs = {
marketConfigs: {
effectiveInitialMarginFraction: BigNumber;
maintenanceMarginFraction: BigNumber;
};
marketOraclePrice: BigNumber;
containingSubaccountInfo: SubaccountSummaryCore;
};
export type GroupedSubaccountSummary = SubaccountSummaryDerived;

export type SubaccountPositionBase = ConvertStringToBigNumber<
IndexerPerpetualPositionResponseObject,
| 'size'
| 'maxSize'
| 'entryPrice'
| 'realizedPnl'
| 'createdAtHeight'
| 'sumOpen'
| 'sumClose'
| 'netFunding'
| 'unrealizedPnl'
| 'exitPrice'
>;

export type SubaccountPositionDerivedCore = {
marginMode: 'ISOLATED' | 'CROSS';

// indexer size is signed by default I think
signedSize: BigNumber;
signedSize: BigNumber; // indexer size is signed by default but we make it obvious here
unsignedSize: BigNumber; // always positive
notional: BigNumber; // always positive
value: BigNumber; // can be negative
Expand All @@ -72,6 +61,10 @@ export type SubaccountPositionDerivedCore = {
initialRisk: BigNumber;
maintenanceRisk: BigNumber;
maxLeverage: BigNumber | null;

// these are just copied from the perpetual position for aesthetic reasons honestly
baseEntryPrice: BigNumber;
baseNetFunding: BigNumber;
};

export type SubaccountPositionDerivedExtra = {
Expand All @@ -83,3 +76,7 @@ export type SubaccountPositionDerivedExtra = {
updatedUnrealizedPnl: BigNumber;
updatedUnrealizedPnlPercent: BigNumber | null;
};

export type SubaccountPosition = SubaccountPositionBase &
SubaccountPositionDerivedCore &
SubaccountPositionDerivedExtra;
8 changes: 8 additions & 0 deletions src/lib/numbers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,21 @@ export const BIG_NUMBERS = {
ONE: new BigNumber(1),
};

// defaults to zero if null or empty
export const MustBigNumber = (amount?: BigNumberish | null): BigNumber =>
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
new BigNumber(amount || 0);

// undefined if falsey otherwise a valid bignumber
export const MaybeBigNumber = (amount?: BigNumberish | null): BigNumber | undefined =>
amount ? MustBigNumber(amount) : undefined;

// doesnt allow null, always returns big number
// empty string becomes null though
export const ToBigNumber = (amount: BigNumberish): BigNumber => {
return MustBigNumber(amount);
};

/**
* @description Rounds the input to the nearest multiple of `factor`, which must be non-zero.
*/
Expand Down

0 comments on commit eadb0b4

Please sign in to comment.