ts-sdk
ts-sdk copied to clipboard
Compute trader PnL from amm and position
We can display the current value of a position from the current state of the amm object and the position object. Creating this function in the ts-sdk allows the web-app to display this value without needing to query the node.
1. Computing Quote Reserve Amount (GetQuoteReserveAmt)
The GetQuoteReserveAmt function takes in a base reserve amount and a direction (LONG or SHORT) and calculates the quote reserve delta.
The logic is as follows:
- Check if the base reserve amount is negative or zero.
- Calculate an invariant by multiplying the quote reserve with the base reserve (
invariant = x * y = k
). - Depending on the direction, either add or subtract the base reserve amount from the base reserves.
- If the base reserves after the operation is not positive, return an error.
- Calculate the quote reserves after the operation by dividing the invariant by the base reserves after.
- The quote reserve delta is the absolute difference between the quote reserves after and the current quote reserve.
2. Computing Unrealized PnL (UnrealizedPnl)
The UnrealizedPnl function calculates the unrealized profits and losses (PnL) of a position based on the position's size (whether it's LONG or SHORT) and the position notional.
The logic is:
- If the position size is positive (LONG), then the unrealized PnL is
positionNotional - position.OpenNotional
. - If the position size is negative (SHORT), then the unrealized PnL is
position.OpenNotional - positionNotional
.
3. Computing Position Value
The position value is computed using the equation:
positionValue = currentPosition.Margin + resp.RealizedPnl - resp.FundingPayment
Where:
- RealizedPnL is calculated using the UnrealizedPnl function.
- FundingPayment is calculated using the given formula:
marketLatestCumulativePremiumFraction - position.LatestCumulativePremiumFraction * position.Size_
.
Now let's translate this into a JavaScript code.
const Direction = {
LONG: 'long',
SHORT: 'short'
};
function GetQuoteReserveAmt(baseReserveAmt, dir, amm) {
if (baseReserveAmt < 0) {
throw new Error('Base Reserve Amount is Negative');
}
if (baseReserveAmt === 0) {
return 0;
}
const invariant = amm.QuoteReserve * amm.BaseReserve;
let baseReservesAfter;
if (dir === Direction.LONG) {
baseReservesAfter = amm.BaseReserve - baseReserveAmt;
} else {
baseReservesAfter = amm.BaseReserve + baseReserveAmt;
}
if (baseReservesAfter <= 0) {
throw new Error('Base assets below zero');
}
const quoteReservesAfter = invariant / baseReservesAfter;
const quoteReserveDelta = Math.abs(quoteReservesAfter - amm.QuoteReserve);
return quoteReserveDelta;
}
function UnrealizedPnl(position, positionNotional) {
if (position.Size_ > 0) {
return positionNotional - position.OpenNotional;
} else {
return position.OpenNotional - positionNotional;
}
}
function calculatePositionValue(currentPosition, position, marketLatestCumulativePremiumFraction, amm) {
const positionNotional = GetQuoteReserveAmt(Math.abs(position.Size_), Direction.LONG, amm) * amm.P;
const FundingPayment = marketLatestCumulativePremiumFraction - position.LatestCumulativePremiumFraction * position.Size_;
const RealizedPnl = UnrealizedPnl(currentPosition, positionNotional);
const positionValue = currentPosition.Margin + RealizedPnl - FundingPayment;
return positionValue;
}
This JavaScript code defines the functions GetQuoteReserveAmt and UnrealizedPnl according to the logic provided, and adds a calculatePositionValue function to compute the position value. It also defines the Direction object to hold the LONG and SHORT constants. Make sure to define the amm
, currentPosition
, position
, marketLatestCumulativePremiumFraction
, and P
accordingly as per your application requirements when calling the calculatePositionValue
function.
Here's the logic of the golang code:
position notional = amm.GetQuoteReserveAmt(position.Size_.Abs(), dir) * amm.P FundingPayment = marketLatestCumulativePremiumFraction - position.LatestCumulativePremiumFraction * position.Size_ RealizedPnl = UnrealizedPnl(currentPosition, positionNotional)
positionValue = currentPosition.Margin + resp.RealizedPnl - resp.FundingPayment
GetQuoteReserveAmt:
func (amm AMM) GetQuoteReserveAmt(
baseReserveAmt sdk.Dec,
dir Direction,
) (quoteReserveDelta sdk.Dec, err error) {
if baseReserveAmt.IsNegative() {
return sdk.Dec{}, ErrInputBaseAmtNegative
}
if baseReserveAmt.IsZero() {
return sdk.ZeroDec(), nil
}
invariant := amm.QuoteReserve.Mul(amm.BaseReserve) // x * y = k
var baseReservesAfter sdk.Dec
if dir == Direction_LONG {
baseReservesAfter = amm.BaseReserve.Sub(baseReserveAmt)
} else {
baseReservesAfter = amm.BaseReserve.Add(baseReserveAmt)
}
if !baseReservesAfter.IsPositive() {
return sdk.Dec{}, ErrBaseReserveAtZero.Wrapf(
"base assets below zero (%s) after trying to swap %s base assets",
baseReservesAfter.String(),
baseReserveAmt.String(),
)
}
quoteReservesAfter := invariant.Quo(baseReservesAfter)
quoteReserveDelta = quoteReservesAfter.Sub(amm.QuoteReserve).Abs()
return quoteReserveDelta, nil
}
UnrealizedPnl:
// UnrealizedPnl calculates the unrealized profits and losses (PnL) of a position.
func UnrealizedPnl(position types.Position, positionNotional sdk.Dec) (unrealizedPnlSigned sdk.Dec) {
if position.Size_.IsPositive() {
// LONG
return positionNotional.Sub(position.OpenNotional)
} else {
// SHORT
return position.OpenNotional.Sub(positionNotional)
}
}
Closing as wont-do