Skip to content

Commit

Permalink
Merge pull request #179 from Kwenta/fix/stats-pnl
Browse files Browse the repository at this point in the history
Pnl improvements
  • Loading branch information
avclarke authored Feb 21, 2024
2 parents cd894ef + dfc045d commit c6739f9
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 24 deletions.
93 changes: 70 additions & 23 deletions src/perps-v3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import {
PerpsV3Position,
PerpsV3Stat,
SettlementStrategy,
PnlSnapshot,
MarketPriceUpdate,
PositionLiquidation,
} from '../generated/subgraphs/perps-v3/schema';
import {
AccountCreated,
Expand Down Expand Up @@ -47,6 +50,7 @@ const AGG_PERIODS = [ONE_HOUR_SECONDS, DAY_SECONDS];
export function handleMarketCreated(event: MarketCreated): void {
let marketId = event.params.perpsMarketId.toString();
let market = new PerpsV3Market(marketId);
market.lastPrice = ZERO;
market.marketSymbol = event.params.marketSymbol;
market.marketName = event.params.marketName;
market.save();
Expand All @@ -73,12 +77,30 @@ export function handlePositionLiquidated(event: PositionLiquidatedEvent): void {
const openPosition = OpenPerpsV3Position.load(positionId);

let account = Account.load(event.params.accountId.toString());
let market = PerpsV3Market.load(event.params.marketId.toString());

if (market === null) {
log.error('Market not found for marketId: {}', [event.params.marketId.toString()]);
return;
}

if (account === null) {
log.error('Account not found for accountId: {}', [event.params.accountId.toString()]);
return;
}

let estiamtedNotionalSize = event.params.amountLiquidated.abs().times(market.lastPrice).div(ETHER).abs();
let liquidation = new PositionLiquidation(
event.params.marketId.toString() + '-' + event.params.accountId.toString() + '-' + event.block.timestamp.toString(),
);
liquidation.marketId = event.params.marketId;
liquidation.amount = event.params.amountLiquidated;
liquidation.accountId = event.params.accountId;
liquidation.notionalAmount = estiamtedNotionalSize;
liquidation.estimatedPrice = market.lastPrice;
liquidation.timestamp = event.block.timestamp;
liquidation.save();

let statId = event.params.accountId.toString() + '-' + account.owner.toHexString();
let statEntity = PerpsV3Stat.load(statId);

Expand Down Expand Up @@ -195,6 +217,7 @@ export function handleOrderSettled(event: OrderSettledEvent): void {
positionEntity.netFunding = event.params.accruedFunding;
positionEntity.pnlWithFeesPaid = ZERO;
positionEntity.totalVolume = volume;
positionEntity.totalReducedNotional = ZERO;

updateAggregateStatEntities(
positionEntity.marketId,
Expand All @@ -211,10 +234,13 @@ export function handleOrderSettled(event: OrderSettledEvent): void {
positionEntity.save();
statEntity.save();
} else {
const tradeNotionalValue = event.params.sizeDelta.abs().times(event.params.fillPrice);

if (event.params.newSize.isZero()) {
positionEntity.isOpen = false;
positionEntity.closeTimestamp = event.block.timestamp;
positionEntity.exitPrice = event.params.fillPrice;
positionEntity.totalReducedNotional = positionEntity.totalReducedNotional.plus(tradeNotionalValue);
openPositionEntity.position = null;
openPositionEntity.save();

Expand All @@ -237,11 +263,11 @@ export function handleOrderSettled(event: OrderSettledEvent): void {
} else if (event.params.newSize.abs().gt(positionEntity.size.abs())) {
// If ths positions size is increasing then recalculate the average entry price
const existingNotionalValue = positionEntity.size.abs().times(positionEntity.avgEntryPrice);
const tradeNotionalValue = event.params.sizeDelta.abs().times(event.params.fillPrice);
positionEntity.avgEntryPrice = existingNotionalValue.plus(tradeNotionalValue).div(event.params.newSize.abs());
} else {
// If decreasing calc the pnl
calculatePnl(positionEntity, order, event, statEntity);
// Track the total amount reduced
}
}
positionEntity.feesPaid = positionEntity.feesPaid.plus(event.params.totalFees);
Expand Down Expand Up @@ -304,9 +330,20 @@ export function handleSettlementStrategyEnabled(event: SettlementStrategySet): v
strategy.save();
}

export function handleFundingRecomputed(event: MarketUpdated): void {
export function handleMarketUpdated(event: MarketUpdated): void {
let marketEntity = PerpsV3Market.load(event.params.marketId.toString());

let price = event.params.price;
let timestamp = event.block.timestamp;

let marketPriceUpdate = new MarketPriceUpdate(
price.toString() + '-' + timestamp.toString() + '-' + event.params.marketId.toString(),
);
marketPriceUpdate.marketId = event.params.marketId;
marketPriceUpdate.timestamp = timestamp;
marketPriceUpdate.price = price;
marketPriceUpdate.save();

let fundingRateUpdateEntity = new FundingRateUpdate(
event.params.marketId.toString() + '-' + event.transaction.hash.toHex(),
);
Expand All @@ -319,6 +356,9 @@ export function handleFundingRecomputed(event: MarketUpdated): void {
fundingRateUpdateEntity.marketSymbol = marketEntity.marketSymbol;
fundingRateUpdateEntity.marketName = marketEntity.marketName;
updateFundingRatePeriods(event.block.timestamp, marketEntity.marketSymbol, fundingRateUpdateEntity);

marketEntity.lastPrice = price;
marketEntity.save();
}

fundingRateUpdateEntity.save();
Expand Down Expand Up @@ -379,27 +419,6 @@ function updateFundingRatePeriods(timestamp: BigInt, asset: string, rate: Fundin
}
}

function calculatePnl(
position: PerpsV3Position,
order: OrderSettled,
event: OrderSettledEvent,
statEntity: PerpsV3Stat,
): void {
let pnl = event.params.fillPrice
.minus(position.avgEntryPrice)
.times(event.params.sizeDelta.abs())
.times(position.size.gt(ZERO) ? BigInt.fromI32(1) : BigInt.fromI32(-1))
.div(ETHER);
position.realizedPnl = position.realizedPnl.plus(pnl);
position.pnlWithFeesPaid = position.realizedPnl.minus(position.feesPaid).plus(position.netFunding);
order.pnl = order.pnl.plus(pnl);
statEntity.pnl = statEntity.pnl.plus(pnl);
statEntity.pnlWithFeesPaid = statEntity.pnlWithFeesPaid.plus(pnl).minus(position.feesPaid).plus(position.netFunding);
order.save();
position.save();
statEntity.save();
}

export function handleCollateralModified(event: CollateralModifiedEvent): void {
const accountId = event.params.accountId;
const account = Account.load(accountId.toString());
Expand Down Expand Up @@ -445,6 +464,34 @@ export function handleOrderCommitted(event: OrderCommittedEvent): void {
orderCommitted.save();
}

function calculatePnl(
position: PerpsV3Position,
order: OrderSettled,
event: OrderSettledEvent,
statEntity: PerpsV3Stat,
): void {
let pnl = event.params.fillPrice
.minus(position.avgEntryPrice)
.times(event.params.sizeDelta.abs())
.times(position.size.gt(ZERO) ? BigInt.fromI32(1) : BigInt.fromI32(-1))
.div(ETHER);
position.realizedPnl = position.realizedPnl.plus(pnl);
position.pnlWithFeesPaid = position.realizedPnl.minus(position.feesPaid).plus(position.netFunding);
order.pnl = order.pnl.plus(pnl);
statEntity.pnl = statEntity.pnl.plus(pnl);
statEntity.pnlWithFeesPaid = statEntity.pnlWithFeesPaid.plus(pnl).minus(position.feesPaid).plus(position.netFunding);
let pnlSnapshot = new PnlSnapshot(
position.id + '-' + event.block.timestamp.toString() + '-' + event.transaction.hash.toHex(),
);
pnlSnapshot.pnl = statEntity.pnl;
pnlSnapshot.accountId = position.accountId;
pnlSnapshot.timestamp = event.block.timestamp;
pnlSnapshot.save();
order.save();
position.save();
statEntity.save();
}

function getOrCreateMarketAggregateStats(
marketId: BigInt,
marketSymbol: string,
Expand Down
26 changes: 26 additions & 0 deletions subgraphs/perps-v3.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type PerpsV3Position @entity {
totalVolume: BigInt!
size: BigInt!
realizedPnl: BigInt!
totalReducedNotional: BigInt!
feesPaid: BigInt!
netFunding: BigInt!
pnlWithFeesPaid: BigInt!
Expand All @@ -98,6 +99,7 @@ type PerpsV3Market @entity {
id: ID!
marketName: String!
marketSymbol: String!
lastPrice: BigInt!
}

type SettlementStrategy @entity {
Expand Down Expand Up @@ -177,3 +179,27 @@ type PerpsV3AggregateStat @entity {
trades: BigInt!
volume: BigInt!
}

type PnlSnapshot @entity {
id: ID!
accountId: BigInt!
pnl: BigInt!
timestamp: BigInt!
}

type MarketPriceUpdate @entity {
id: ID!
timestamp: BigInt!
marketId: BigInt!
price: BigInt!
}

type PositionLiquidation @entity {
id: ID!
accountId: BigInt!
timestamp: BigInt!
marketId: BigInt!
amount: BigInt!
notionalAmount: BigInt!
estimatedPrice: BigInt!
}
2 changes: 1 addition & 1 deletion subgraphs/perps-v3.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ manifest.push({
},
{
event: 'MarketUpdated(uint128,uint256,int256,uint256,int256,int256,int256)',
handler: 'handleFundingRecomputed',
handler: 'handleMarketUpdated',
},
{
event: 'PermissionGranted(indexed uint128,indexed bytes32,indexed address,address)',
Expand Down

0 comments on commit c6739f9

Please sign in to comment.