Skip to content

Commit

Permalink
[PFT-1475] [PFT-1476] Fix liquidation price and add more functions (#74)
Browse files Browse the repository at this point in the history
* Fix liquidation price issue when position is at a loss

* Add INVALID OrderStatus from subgraph recent changes

* 1.0.12

* Add function to get position history
  • Loading branch information
sudeepb02 authored Aug 8, 2024
1 parent 98c64e9 commit ab5e30b
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 20 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@parifi/sdk",
"version": "1.0.11",
"version": "1.0.12",
"description": "Parifi SDK with common utility functions",
"files": [
"dist",
Expand Down
18 changes: 9 additions & 9 deletions src/core/order-manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,23 +554,23 @@ export const getLiquidationPrice = (
normalizedMarketPrice,
);

const collateralInUsd = convertCollateralAmountToUsd(collateral, collateralDecimals, normalizedCollateralPrice);
const maxLossLimitInUsd = collateralInUsd.times(market.liquidationThreshold).div(PRECISION_MULTIPLIER);

let lossLimitAfterFees = maxLossLimitInUsd.sub(totalFeesInUsd);

let { totalProfitOrLoss: totalProfitOrLossInUsd, isProfit } = getProfitOrLossInUsd(
position,
normalizedMarketPrice,
marketDecimals,
);

// Update the sign of the totalProfitOrLoss. The values is negative when in loss
// and positive when in profit
if (!isProfit) {
totalProfitOrLossInUsd = totalProfitOrLossInUsd.times(-1);
// If the position is profitable, add the profits to increase the maxLoss limit, else skip losses
// as its already factored in
if (isProfit) {
lossLimitAfterFees = lossLimitAfterFees.add(totalProfitOrLossInUsd);
}

const collateralInUsd = convertCollateralAmountToUsd(collateral, collateralDecimals, normalizedCollateralPrice);
const maxLossLimitInUsd = collateralInUsd.times(market.liquidationThreshold).div(PRECISION_MULTIPLIER);

const lossLimitAfterFees = maxLossLimitInUsd.sub(totalFeesInUsd).add(totalProfitOrLossInUsd);

// @todo Revisit this
// If loss is already more than the max loss, the position can be liquidated at the current price
if (lossLimitAfterFees.lessThan(DECIMAL_ZERO)) return normalizedMarketPrice;
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/subgraphTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export enum OrderStatus {
PENDING = 'PENDING',
CANCELLED = 'CANCELLED',
SETTLED = 'SETTLED',
INVALID = 'INVALID'
}

export enum PositionStatus {
Expand Down
6 changes: 6 additions & 0 deletions src/subgraph/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getLiquidatedPositionsByUserAddress,
getOpenPositionsByUserAddress,
getPositionById,
getPositionsHistory,
getPositionsToLiquidate,
getPositionsToRefresh,
getPythPriceIdsForPositionIds,
Expand Down Expand Up @@ -232,6 +233,11 @@ export class Subgraph {
const subgraphEndpoint = this.getSubgraphEndpoint(this.rpcConfig.chainId);
return await getAllOrdersForPosition(subgraphEndpoint, positionId);
}

public async getPositionsHistory(userAddress: string, count: number = 100, skip: number = 0): Promise<Position[]> {
const subgraphEndpoint = this.getSubgraphEndpoint(this.rpcConfig.chainId);
return await getPositionsHistory(subgraphEndpoint, userAddress, count, skip);
}
////////////////////////////////////////////////////////////////
////////////////////// MARKET ////////////////////////////
////////////////////////////////////////////////////////////////
Expand Down
22 changes: 22 additions & 0 deletions src/subgraph/positions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
fetchAllPositionsForCollateralData,
fetchAllPositionsUnrealizedPnl,
fetchPositionByIdQuery,
fetchPositionHistoryQuery,
fetchPositionsByUserQuery,
fetchPositionsByUserQueryAndStatus,
fetchPositionsToLiquidateQuery,
Expand Down Expand Up @@ -282,3 +283,24 @@ export const getAllOrdersForPosition = async (subgraphEndpoint: string, position
throw error;
}
};

// Get all positions by user address
export const getPositionsHistory = async (
subgraphEndpoint: string,
userAddress: string,
count: number = 100,
skip: number = 0,
): Promise<Position[]> => {
try {
const query = fetchPositionHistoryQuery(userAddress, count, skip);
let subgraphResponse: any = await request(subgraphEndpoint, query);
if (!subgraphResponse) throw Error(`Error fetching All Positions By UserAddress`);
const positions = mapPositionsArrayToInterface(subgraphResponse);
if (positions) {
return positions;
}
throw new NotFoundError('Positions not found');
} catch (error) {
throw error;
}
};
46 changes: 46 additions & 0 deletions src/subgraph/positions/subgraphQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,49 @@ export const fetchAllOrdersForPosition = (positionId: string) => gql`
}
}
}`;

// Fetches all positions by a user (Both open and closed)
export const fetchPositionHistoryQuery = (userAddress: string, count: number = 100, skip: number = 0) =>
gql`
{
positions(
first: ${count}
skip: ${skip}
orderBy: createdTimestamp
orderDirection: desc
where: {user: "${userAddress.toLowerCase()}", status_not: OPEN}
) {
id
market {
id
}
user {
id
}
positionSize
positionCollateral
avgPrice
avgPriceDec
isLong
createdTimestamp
lastCumulativeFee
status
txHash
liquidationTxHash
closingPrice
realizedPnl
realizedPnlCollateral
realizedFee
realizedFeeCollateral
netRealizedPnl
createdTimestamp
lastRefresh
lastRefreshISO
netUnrealizedPnlInCollateral
netUnrealizedPnlInUsd
liquidationNetPnlInCollateral
accruedBorrowingFeesInCollateral
canBeLiquidated
lossToCollateralRatioPercent
}
}`;
3 changes: 0 additions & 3 deletions test/core/dataFabric.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { ethers } from 'ethers';
import { getParifiSdkInstanceForTesting } from '..';
import { TEST_MARKET_ID1, TEST_OPEN_POSITION, TEST_POSITION_ID1, TEST_SETTLE_ORDER_ID } from '../common/constants';
import { DECIMAL_ZERO, OrderStatus, getNormalizedPriceByIdFromPriceIdArray } from '../../src';
import Decimal from 'decimal.js';

describe('Data Fabric tests', () => {
Expand Down
10 changes: 6 additions & 4 deletions test/core/orderManager.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ethers } from 'ethers';
import { getParifiSdkInstanceForTesting } from '..';
import { TEST_MARKET_ID1, TEST_OPEN_POSITION, TEST_POSITION_ID1, TEST_SETTLE_ORDER_ID } from '../common/constants';
import { TEST_MARKET_ID1, TEST_SETTLE_ORDER_ID } from '../common/constants';
import { DECIMAL_ZERO, OrderStatus, getCurrentTimestampInSeconds } from '../../src';
import Decimal from 'decimal.js';

Expand Down Expand Up @@ -55,7 +55,9 @@ describe('Order Manager tests', () => {

it('should return valid liquidation price', async () => {
const parifiSdk = await getParifiSdkInstanceForTesting();
const position = await parifiSdk.subgraph.getPositionById(TEST_OPEN_POSITION);
const position = await parifiSdk.subgraph.getPositionById(
'0x46baf7296ea013c80cb814c3a4c3f0b1547b03a2f02b370a093cead75cfadc94',
);
const market = await parifiSdk.subgraph.getMarketById(position.market?.id ?? TEST_MARKET_ID1);

const normalizedPrice = await parifiSdk.pyth.getLatestPricesNormalized([
Expand All @@ -76,14 +78,14 @@ describe('Order Manager tests', () => {
const accruedBorrowFeesInMarket = await parifiSdk.core.getAccruedBorrowFeesInMarket(position, market);

if (market.marketDecimals && market.depositToken?.decimals && normalizedCollateralPrice) {
const accruedFeesInUsd = await parifiSdk.core.convertMarketAmountToCollateral(
const accruedFeesInUsdc = await parifiSdk.core.convertMarketAmountToCollateral(
accruedBorrowFeesInMarket,
new Decimal(market.marketDecimals),
new Decimal(market.depositToken?.decimals),
normalizedMarketPrice,
normalizedCollateralPrice,
);
console.log('accruedFeesInUsd', accruedFeesInUsd);
console.log('accruedFeesInUsdc', accruedFeesInUsdc);
} else {
console.log('Invalid values sdk test');
}
Expand Down
1 change: 0 additions & 1 deletion test/core/parifi-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ describe('Parifi Utils tests', () => {
const ordersCount = 10;

const pendingOrders = await parifiSdk.subgraph.getAllPendingOrders(expiryTimestamp, ordersCount, 0);
console.log(pendingOrders);

// Return if orders are not available for settlement
if (pendingOrders.length == 0) return;
Expand Down
12 changes: 12 additions & 0 deletions test/subgraph-tests/position.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getParifiSdkInstanceForTesting } from '..';
import { PositionStatus } from '../../src';
import {
TEST_POSITION_ID1,
TEST_POSITION_ID2,
Expand Down Expand Up @@ -123,4 +124,15 @@ describe('Order fetching logic from subgraph', () => {
const orders = await parifiSdk.subgraph.getAllOrdersForPosition(TEST_POSITION_ID1);
console.log('Orders for position ID:', orders);
});

it('should return position history for a user', async () => {
const parifiSdk = await getParifiSdkInstanceForTesting();

const userAddress = TEST_USER_ID2;
const positions = await parifiSdk.subgraph.getPositionsHistory(userAddress);
console.log(positions.length);
positions.forEach((position) => {
expect(position.status).not.toBe(PositionStatus.OPEN);
});
});
});

0 comments on commit ab5e30b

Please sign in to comment.