2023-06-28 06:03:32 -07:00
|
|
|
import { PublicKey } from '@solana/web3.js';
|
|
|
|
import { Group } from './accounts/group';
|
2023-07-04 00:26:59 -07:00
|
|
|
import { MangoAccount, PerpPosition } from './accounts/mangoAccount';
|
2023-07-02 23:20:13 -07:00
|
|
|
import { PerpMarket } from './accounts/perp';
|
2023-06-28 06:03:32 -07:00
|
|
|
import { MangoClient } from './client';
|
|
|
|
import { I80F48 } from './numbers/I80F48';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of perp positions alongwith their mango account, sorted descending by notional value
|
|
|
|
* @param client
|
|
|
|
* @param group
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
export async function getLargestPerpPositions(
|
|
|
|
client: MangoClient,
|
|
|
|
group: Group,
|
2023-07-04 00:26:59 -07:00
|
|
|
accounts?: MangoAccount[],
|
2023-07-02 23:20:13 -07:00
|
|
|
perpMarket?: PerpMarket,
|
2023-06-28 06:03:32 -07:00
|
|
|
): Promise<{ mangoAccount: PublicKey; perpPosition: PerpPosition }[]> {
|
2023-07-04 00:26:59 -07:00
|
|
|
if (!accounts) {
|
|
|
|
accounts = await client.getAllMangoAccounts(group, true);
|
|
|
|
}
|
2023-06-28 06:03:32 -07:00
|
|
|
|
2023-07-02 23:20:13 -07:00
|
|
|
let allPps = accounts
|
2023-06-28 06:03:32 -07:00
|
|
|
.map((a) => {
|
|
|
|
const pps = a.perpActive().map((pp) => {
|
|
|
|
pp['mangoAccount'] = a.publicKey;
|
|
|
|
return pp;
|
|
|
|
});
|
|
|
|
return pps;
|
|
|
|
})
|
|
|
|
.flat();
|
|
|
|
|
2023-07-02 23:20:13 -07:00
|
|
|
if (perpMarket) {
|
|
|
|
allPps = allPps.filter(
|
|
|
|
(pp) => pp.marketIndex == perpMarket?.perpMarketIndex,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-06-28 06:03:32 -07:00
|
|
|
allPps.sort(
|
|
|
|
(a, b) =>
|
|
|
|
b.getNotionalValueUi(group.getPerpMarketByMarketIndex(b.marketIndex)) -
|
|
|
|
a.getNotionalValueUi(group.getPerpMarketByMarketIndex(a.marketIndex)),
|
|
|
|
);
|
|
|
|
|
|
|
|
return allPps.map((pp) => ({
|
|
|
|
mangoAccount: pp['mangoAccount'],
|
|
|
|
perpPosition: pp,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of perp positions alongwith their mango account, sorted ascending by closest to liquidation
|
|
|
|
* @param client
|
|
|
|
* @param group
|
|
|
|
* @param filterByNotionalValueUi
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
export async function getClosestToLiquidationPerpPositions(
|
|
|
|
client: MangoClient,
|
|
|
|
group: Group,
|
2023-07-04 00:26:59 -07:00
|
|
|
accounts?: MangoAccount[],
|
2023-06-28 06:03:32 -07:00
|
|
|
filterByNotionalValueUi = 10,
|
|
|
|
): Promise<
|
|
|
|
{ mangoAccount: PublicKey; perpPosition: PerpPosition; pct: I80F48 }[]
|
|
|
|
> {
|
2023-07-04 00:26:59 -07:00
|
|
|
if (!accounts) {
|
|
|
|
accounts = await client.getAllMangoAccounts(group, true);
|
|
|
|
}
|
2023-06-28 06:03:32 -07:00
|
|
|
const accountsMap = new Map(accounts.map((a) => [a.publicKey.toBase58(), a]));
|
|
|
|
|
|
|
|
let allPps: any = accounts
|
|
|
|
.map((a) => {
|
|
|
|
const pps = a
|
|
|
|
.perpActive()
|
|
|
|
.filter(
|
|
|
|
(pp) =>
|
|
|
|
pp.getNotionalValueUi(
|
|
|
|
group.getPerpMarketByMarketIndex(pp.marketIndex),
|
|
|
|
) > filterByNotionalValueUi,
|
|
|
|
)
|
|
|
|
.map((pp) => {
|
|
|
|
pp['mangoAccount'] = a.publicKey;
|
|
|
|
return pp;
|
|
|
|
});
|
|
|
|
return pps;
|
|
|
|
})
|
|
|
|
.flat();
|
|
|
|
|
|
|
|
function getChangeToLiquidation(pp: PerpPosition): I80F48 {
|
|
|
|
const lp = pp.getLiquidationPrice(
|
|
|
|
group,
|
|
|
|
accountsMap.get(pp['mangoAccount'].toBase58())!,
|
|
|
|
)!;
|
|
|
|
const op = group.getPerpMarketByMarketIndex(pp.marketIndex).price;
|
|
|
|
return lp.sub(op).abs().div(op).mul(I80F48.fromNumber(100));
|
|
|
|
}
|
|
|
|
|
|
|
|
allPps = allPps.filter(
|
|
|
|
(pp) =>
|
|
|
|
pp.getLiquidationPrice(
|
|
|
|
group,
|
|
|
|
accountsMap.get(pp['mangoAccount'].toBase58())!,
|
|
|
|
) != null,
|
|
|
|
);
|
|
|
|
|
|
|
|
return allPps
|
|
|
|
.map((pp) => ({
|
|
|
|
mangoAccount: pp['mangoAccount'],
|
|
|
|
perpPosition: pp,
|
|
|
|
pct: getChangeToLiquidation(pp),
|
|
|
|
}))
|
|
|
|
.sort((a, b) => (a.pct.lte(b.pct) ? -1 : 1));
|
|
|
|
}
|