update health computer for perps (#823)

* update health computer for perps

* update health computer + fix comments
This commit is contained in:
Bob van der Helm 2024-02-23 13:57:27 +01:00 committed by GitHub
parent 5846e893d1
commit 06db4a2faa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 500 additions and 402 deletions

View File

@ -14,7 +14,7 @@ type Props = {
export default function PnL(props: Props) { export default function PnL(props: Props) {
return ( return (
<Tooltip content={<PnLTooltip {...props} />} type='info' underline> <Tooltip content={<PnLTooltip {...props} />} type='info' underline className='w-min ml-auto'>
<DisplayCurrency <DisplayCurrency
className='inline text-xs' className='inline text-xs'
coin={props.pnl.net} coin={props.pnl.net}

View File

@ -104,7 +104,7 @@ export function PerpsModule() {
/> />
<AssetAmountInput <AssetAmountInput
label='Amount' label='Amount'
max={BN(100000000)} // TODO: Implement max calculation max={BN(10000000000)} // TODO: Implement max calculation
amount={amount.abs()} amount={amount.abs()}
setAmount={onChangeAmount} setAmount={onChangeAmount}
asset={perpsAsset} asset={perpsAsset}

View File

@ -3,6 +3,7 @@ import BigNumber from 'bignumber.js'
import { CircularProgress } from 'components/common/CircularProgress' import { CircularProgress } from 'components/common/CircularProgress'
import DisplayCurrency from 'components/common/DisplayCurrency' import DisplayCurrency from 'components/common/DisplayCurrency'
import useTradingFeeAndPrice from 'hooks/perps/useTradingFeeAndPrice' import useTradingFeeAndPrice from 'hooks/perps/useTradingFeeAndPrice'
import { BNCoin } from 'types/classes/BNCoin'
type Props = { type Props = {
denom: string denom: string
@ -20,5 +21,12 @@ export default function TradingFee(props: Props) {
if (isLoading) return <CircularProgress className='h-full' size={12} /> if (isLoading) return <CircularProgress className='h-full' size={12} />
if (props.newAmount.isEqualTo(props.previousAmount) || !tradingFeeAndPrice?.fee) return '-' if (props.newAmount.isEqualTo(props.previousAmount) || !tradingFeeAndPrice?.fee) return '-'
return <DisplayCurrency coin={tradingFeeAndPrice.fee} /> return (
<DisplayCurrency
coin={BNCoin.fromDenomAndBigNumber(
tradingFeeAndPrice.baseDenom,
tradingFeeAndPrice.fee.opening.plus(tradingFeeAndPrice.fee.closing),
)}
/>
)
} }

View File

@ -6,7 +6,6 @@ import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useChainConfig from 'hooks/useChainConfig' import useChainConfig from 'hooks/useChainConfig'
import useClients from 'hooks/useClients' import useClients from 'hooks/useClients'
import useDebounce from 'hooks/useDebounce' import useDebounce from 'hooks/useDebounce'
import { BNCoin } from 'types/classes/BNCoin'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
export default function useTradingFeeAndPrice( export default function useTradingFeeAndPrice(
@ -34,14 +33,15 @@ export default function useTradingFeeAndPrice(
}) })
return { return {
baseDenom: positionFees.base_denom,
price: positionFees.opening_exec_price price: positionFees.opening_exec_price
? BN(positionFees.opening_exec_price) ? BN(positionFees.opening_exec_price)
: BN(positionFees.closing_exec_price ?? BN_ZERO), : BN(positionFees.closing_exec_price ?? BN_ZERO),
fee: BNCoin.fromDenomAndBigNumber( fee: {
positionFees.base_denom, opening: BN(positionFees.opening_fee),
BN(positionFees.opening_fee).plus(positionFees.closing_fee), closing: BN(positionFees.closing_fee),
), },
rate: BN(0.005), rate: BN(0.0005),
} }
} }
@ -61,15 +61,13 @@ export default function useTradingFeeAndPrice(
return await Promise.all([closingPositionFees$, openingPositionFees$]).then( return await Promise.all([closingPositionFees$, openingPositionFees$]).then(
([closingPositionFees, openingPositionFees]) => ({ ([closingPositionFees, openingPositionFees]) => ({
baseDenom: openingPositionFees.base_denom,
price: BN(openingPositionFees.opening_exec_price ?? 0), price: BN(openingPositionFees.opening_exec_price ?? 0),
fee: BNCoin.fromDenomAndBigNumber( fee: {
closingPositionFees.base_denom, opening: BN(closingPositionFees.opening_fee).plus(openingPositionFees.opening_fee),
BN(closingPositionFees.opening_fee) closing: BN(closingPositionFees.closing_fee).plus(openingPositionFees.closing_fee),
.plus(closingPositionFees.closing_fee) },
.plus(openingPositionFees.opening_fee) rate: BN(0.0005), // TODO: Make this rate the actula rate again!
.plus(openingPositionFees.closing_fee),
),
rate: BN(0.005),
}), }),
) )
}, },

View File

@ -14,6 +14,7 @@ import { VaultConfigBaseForString } from 'types/generated/mars-params/MarsParams
import { import {
AssetParamsBaseForAddr, AssetParamsBaseForAddr,
HealthComputer, HealthComputer,
Positions,
} from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types' } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
import { convertAccountToPositions } from 'utils/accounts' import { convertAccountToPositions } from 'utils/accounts'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
@ -32,7 +33,8 @@ import { BN } from 'utils/helpers'
// Pyth returns prices with up to 32 decimals. Javascript only supports 18 decimals. So we need to scale by 14 t // Pyth returns prices with up to 32 decimals. Javascript only supports 18 decimals. So we need to scale by 14 t
// avoid "too many decimals" errors. // avoid "too many decimals" errors.
const VALUE_SCALE_FACTOR = 14 // TODO: Remove adjustment properly (after testing). We will just ignore the last 14 decimals.
const VALUE_SCALE_FACTOR = 0
export default function useHealthComputer(account?: Account) { export default function useHealthComputer(account?: Account) {
const assets = useAllAssets() const assets = useAllAssets()
@ -42,10 +44,10 @@ export default function useHealthComputer(account?: Account) {
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage) const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
const [healthFactor, setHealthFactor] = useState(0) const [healthFactor, setHealthFactor] = useState(0)
const positions: PositionsWithoutPerps | null = useMemo(() => { const positions: Positions | null = useMemo(() => {
if (!account) return null if (!account) return null
return convertAccountToPositions(account) return convertAccountToPositions(account, prices)
}, [account]) }, [account, prices])
const vaultPositionValues = useMemo(() => { const vaultPositionValues = useMemo(() => {
if (!account?.vaults) return null if (!account?.vaults) return null
@ -87,6 +89,7 @@ export default function useHealthComputer(account?: Account) {
prev[curr.denom] = curr.amount prev[curr.denom] = curr.amount
.shiftedBy(VALUE_SCALE_FACTOR) .shiftedBy(VALUE_SCALE_FACTOR)
.shiftedBy(-decimals + 6) .shiftedBy(-decimals + 6)
.decimalPlaces(18)
.toString() .toString()
return prev return prev
}, },

View File

@ -1,4 +1,4 @@
import { ActionCoin } from 'types/generated/mars-credit-manager/MarsCreditManager.types' import { ActionCoin, PnL } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
export class BNCoin { export class BNCoin {
@ -23,7 +23,7 @@ export class BNCoin {
toCoin(): Coin { toCoin(): Coin {
return { return {
denom: this.denom, denom: this.denom,
amount: this.amount.toString(), amount: this.amount.integerValue().toString(),
} }
} }
@ -33,7 +33,7 @@ export class BNCoin {
amount: max amount: max
? 'account_balance' ? 'account_balance'
: { : {
exact: this.amount.toString(), exact: this.amount.integerValue().toString(),
}, },
} }
} }
@ -41,7 +41,23 @@ export class BNCoin {
toSignedCoin(): any { toSignedCoin(): any {
return { return {
denom: this.denom, denom: this.denom,
size: this.amount.toString(), size: this.amount.integerValue().toString(),
}
}
toPnLCoin(): PnL {
if (this.amount.isZero()) {
return 'break_even'
}
if (this.amount.isPositive()) {
return {
profit: this.toCoin(),
}
}
return {
loss: this.abs().toCoin(),
} }
} }

View File

@ -69,6 +69,7 @@ import {
Positions, Positions,
DebtAmount, DebtAmount,
PerpPosition, PerpPosition,
PnlAmounts,
PositionPnl, PositionPnl,
PnlCoins, PnlCoins,
PnlValues, PnlValues,

View File

@ -623,11 +623,11 @@ export interface PerpPosition {
denom: string denom: string
entry_exec_price: Decimal entry_exec_price: Decimal
entry_price: Decimal entry_price: Decimal
realised_pnl: RealizedPnlAmounts realised_pnl: PnlAmounts
size: SignedDecimal size: SignedDecimal
unrealised_pnl: PositionPnl unrealised_pnl: PositionPnl
} }
export interface RealizedPnlAmounts { export interface PnlAmounts {
accrued_funding: SignedDecimal accrued_funding: SignedDecimal
closing_fee: SignedDecimal closing_fee: SignedDecimal
opening_fee: SignedDecimal opening_fee: SignedDecimal
@ -635,6 +635,7 @@ export interface RealizedPnlAmounts {
price_pnl: SignedDecimal price_pnl: SignedDecimal
} }
export interface PositionPnl { export interface PositionPnl {
amounts: PnlAmounts
coins: PnlCoins coins: PnlCoins
values: PnlValues values: PnlValues
} }

View File

@ -27,10 +27,11 @@ import {
DebtAmount, DebtAmount,
Coin, Coin,
PerpPosition, PerpPosition,
PnlAmounts,
SignedDecimal,
PositionPnl, PositionPnl,
PnlCoins, PnlCoins,
PnlValues, PnlValues,
SignedDecimal,
VaultPosition, VaultPosition,
LockingVaultAmount, LockingVaultAmount,
VaultUnlockingPosition, VaultUnlockingPosition,

View File

@ -27,10 +27,11 @@ import {
DebtAmount, DebtAmount,
Coin, Coin,
PerpPosition, PerpPosition,
PnlAmounts,
SignedDecimal,
PositionPnl, PositionPnl,
PnlCoins, PnlCoins,
PnlValues, PnlValues,
SignedDecimal,
VaultPosition, VaultPosition,
LockingVaultAmount, LockingVaultAmount,
VaultUnlockingPosition, VaultUnlockingPosition,

View File

@ -41,7 +41,7 @@ export type UnlockingPositions = VaultUnlockingPosition[]
export interface HealthComputer { export interface HealthComputer {
denoms_data: DenomsData denoms_data: DenomsData
kind: AccountKind kind: AccountKind
positions: PositionsWithoutPerps positions: Positions
vaults_data: VaultsData vaults_data: VaultsData
} }
export interface DenomsData { export interface DenomsData {
@ -102,13 +102,29 @@ export interface Coin {
export interface PerpPosition { export interface PerpPosition {
base_denom: string base_denom: string
closing_fee_rate: Decimal closing_fee_rate: Decimal
current_exec_price: Decimal
current_price: Decimal current_price: Decimal
denom: string denom: string
entry_exec_price: Decimal
entry_price: Decimal entry_price: Decimal
pnl: PositionPnl realised_pnl: PnlAmounts
size: SignedDecimal size: SignedDecimal
unrealised_pnl: PositionPnl
}
export interface PnlAmounts {
accrued_funding: SignedDecimal
closing_fee: SignedDecimal
opening_fee: SignedDecimal
pnl: SignedDecimal
price_pnl: SignedDecimal
}
export interface SignedDecimal {
abs: Decimal
negative: boolean
[k: string]: unknown
} }
export interface PositionPnl { export interface PositionPnl {
amounts: PnlAmounts
coins: PnlCoins coins: PnlCoins
values: PnlValues values: PnlValues
} }
@ -122,11 +138,6 @@ export interface PnlValues {
pnl: SignedDecimal pnl: SignedDecimal
price_pnl: SignedDecimal price_pnl: SignedDecimal
} }
export interface SignedDecimal {
abs: Decimal
negative: boolean
[k: string]: unknown
}
export interface VaultPosition { export interface VaultPosition {
amount: VaultPositionAmount amount: VaultPositionAmount
vault: VaultBaseForAddr vault: VaultBaseForAddr

View File

@ -1,11 +1,5 @@
type TradeDirection = 'long' | 'short' type TradeDirection = 'long' | 'short'
// TODO: 📈Remove this type when healthcomputer is implemented
type PositionsWithoutPerps = Omit<
import('types/generated/mars-credit-manager/MarsCreditManager.types').Positions,
'perps'
>
interface PerpsPosition { interface PerpsPosition {
denom: string denom: string
baseDenom: string baseDenom: string
@ -13,6 +7,7 @@ interface PerpsPosition {
amount: BigNumber amount: BigNumber
pnl: PerpsPnL pnl: PerpsPnL
entryPrice: BigNumber entryPrice: BigNumber
closingFeeRate: BigNumber
} }
interface PerpPositionRow extends PerpsPosition { interface PerpPositionRow extends PerpsPosition {

View File

@ -4,6 +4,7 @@ import { BN_ZERO } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle' import { ORACLE_DENOM } from 'constants/oracle'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { VaultPosition } from 'types/generated/mars-credit-manager/MarsCreditManager.types' import { VaultPosition } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { Positions } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
import { convertApyToApr } from 'utils/parsers' import { convertApyToApr } from 'utils/parsers'
@ -174,7 +175,7 @@ export function accumulateAmounts(denom: string, coins: BNCoin[]): BigNumber {
} }
// TODO: 📈 Add correct type mapping // TODO: 📈 Add correct type mapping
export function convertAccountToPositions(account: Account): PositionsWithoutPerps { export function convertAccountToPositions(account: Account, prices: BNCoin[]): Positions {
return { return {
account_id: account.id, account_id: account.id,
debts: account.debts.map((debt) => ({ debts: account.debts.map((debt) => ({
@ -188,6 +189,66 @@ export function convertAccountToPositions(account: Account): PositionsWithoutPer
amount: lend.amount.toString(), amount: lend.amount.toString(),
denom: lend.denom, denom: lend.denom,
})), })),
perps: account.perps.map((perpPosition) => {
// TODO: Check if this needs to be converted (in regards to HC decimal scaling)
const currentPrice = prices.find(byDenom(perpPosition.denom))?.amount ?? BN_ZERO
return {
// Used
base_denom: perpPosition.baseDenom,
// Used
closing_fee_rate: perpPosition.closingFeeRate.toString(),
// Used
current_price: currentPrice.toString(), // Check what prices we should pass to current and entry prices. Entry price will change when modifying positionl
current_exec_price: currentPrice.toString(), // TODO: 📈 This needs to be queried
denom: perpPosition.denom,
// Used (for now, this might be changed)
entry_price: currentPrice.toString(),
// Used (not actually used, but it's in todo)
entry_exec_price: currentPrice.toString(), // TODO: 📈 Check if this matters (currently just using entry price)
// Used
size: perpPosition.amount.toString() as any,
unrealised_pnl: {
coins: {
closing_fee: perpPosition.pnl.unrealized.fees.abs().toCoin(),
// Used
pnl: perpPosition.pnl.unrealized.net.toPnLCoin(), // Used
},
amounts: {
// CHeck if these are correct
accrued_funding: perpPosition.pnl.unrealized.funding.amount
.integerValue()
.toString() as any,
opening_fee: perpPosition.pnl.unrealized.fees.amount
.abs()
.integerValue()
.toString() as any, // Add openning fee for modifying position
closing_fee: perpPosition.pnl.unrealized.fees.amount
.abs()
.integerValue()
.toString() as any, // Add closing fee for modifying position
pnl: perpPosition.pnl.unrealized.net.amount.integerValue().toString() as any,
price_pnl: perpPosition.pnl.unrealized.price.amount.integerValue().toString() as any,
},
values: {
// This does not matter for health calculation
accrued_funding: perpPosition.pnl.unrealized.funding.amount
.integerValue()
.toString() as any,
closing_fee: perpPosition.pnl.unrealized.fees.amount.integerValue().toString() as any,
pnl: perpPosition.pnl.unrealized.net.amount.integerValue().toString() as any,
price_pnl: perpPosition.pnl.unrealized.price.amount.integerValue().toString() as any,
},
},
realised_pnl: {
// This does not matter for the health calculation
accrued_funding: perpPosition.pnl.realized.funding.amount.toString() as any,
closing_fee: perpPosition.pnl.realized.fees.amount.toString() as any,
opening_fee: perpPosition.pnl.realized.fees.amount.toString() as any,
pnl: perpPosition.pnl.realized.net.amount.toString() as any,
price_pnl: perpPosition.pnl.realized.price.amount.toString() as any,
},
}
}),
vaults: account.vaults.map( vaults: account.vaults.map(
(vault) => (vault) =>
({ ({

View File

@ -2,16 +2,17 @@ import BigNumber from 'bignumber.js'
import { BN_ONE, BN_ZERO } from 'constants/math' import { BN_ONE, BN_ZERO } from 'constants/math'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { BN } from 'utils/helpers'
export default function getPerpsPosition( export default function getPerpsPosition(
asset: Asset, asset: Asset,
amount: BigNumber, amount: BigNumber,
tradeDirection: TradeDirection, tradeDirection: TradeDirection,
) { ): PerpsPosition {
const perpsBaseDenom = 'ibc/F91EA2C0A23697A1048E08C2F787E3A58AC6F706A1CD2257A504925158CFC0F3' const perpsBaseDenom = 'ibc/F91EA2C0A23697A1048E08C2F787E3A58AC6F706A1CD2257A504925158CFC0F3'
return { return {
amount, amount,
closingFee: BNCoin.fromDenomAndBigNumber(perpsBaseDenom, BN_ONE), closingFeeRate: BN(0.0005), // TODO: Pass the actual rate
pnl: { pnl: {
net: BNCoin.fromDenomAndBigNumber(perpsBaseDenom, BN_ONE), net: BNCoin.fromDenomAndBigNumber(perpsBaseDenom, BN_ONE),
realized: { realized: {

View File

@ -96,10 +96,10 @@ export interface InitOutput {
h: number, h: number,
) => void ) => void
readonly liquidation_price_js: (a: number, b: number, c: number, d: number, e: number) => void readonly liquidation_price_js: (a: number, b: number, c: number, d: number, e: number) => void
readonly interface_version_8: () => void
readonly allocate: (a: number) => number readonly allocate: (a: number) => number
readonly deallocate: (a: number) => void readonly deallocate: (a: number) => void
readonly requires_iterator: () => void readonly requires_iterator: () => void
readonly interface_version_8: () => void
readonly __wbindgen_malloc: (a: number, b: number) => number readonly __wbindgen_malloc: (a: number, b: number) => number
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number
readonly __wbindgen_add_to_stack_pointer: (a: number) => number readonly __wbindgen_add_to_stack_pointer: (a: number) => number

View File

@ -296,6 +296,7 @@ async function __wbg_load(module, imports) {
function __wbg_get_imports() { function __wbg_get_imports() {
const imports = {} const imports = {}
imports.wbg = {} imports.wbg = {}
imports.wbg.__wbg_log_117d9799fa4f6287 = function (arg0, arg1, arg2, arg3) {}
imports.wbg.__wbindgen_object_clone_ref = function (arg0) { imports.wbg.__wbindgen_object_clone_ref = function (arg0) {
const ret = getObject(arg0) const ret = getObject(arg0)
return addHeapObject(ret) return addHeapObject(ret)

View File

@ -15,10 +15,10 @@ export function max_swap_estimate_js(
h: number, h: number,
): void ): void
export function liquidation_price_js(a: number, b: number, c: number, d: number, e: number): void export function liquidation_price_js(a: number, b: number, c: number, d: number, e: number): void
export function interface_version_8(): void
export function allocate(a: number): number export function allocate(a: number): number
export function deallocate(a: number): void export function deallocate(a: number): void
export function requires_iterator(): void export function requires_iterator(): void
export function interface_version_8(): void
export function __wbindgen_malloc(a: number, b: number): number export function __wbindgen_malloc(a: number, b: number): number
export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number
export function __wbindgen_add_to_stack_pointer(a: number): number export function __wbindgen_add_to_stack_pointer(a: number): number

View File

@ -118,7 +118,7 @@ export function resolvePerpsPositions(
baseDenom: position.base_denom, baseDenom: position.base_denom,
amount: BN(position.size as any), // Amount is negative for SHORT positions amount: BN(position.size as any), // Amount is negative for SHORT positions
tradeDirection: BN(position.size as any).isNegative() ? 'short' : 'long', tradeDirection: BN(position.size as any).isNegative() ? 'short' : 'long',
// closingFee: BNCoin.fromCoin(position.pnl.coins.closing_fee), closingFeeRate: BN(position.closing_fee_rate),
pnl: { pnl: {
net: BNCoin.fromDenomAndBigNumber( net: BNCoin.fromDenomAndBigNumber(
position.base_denom, position.base_denom,