aftermath-ts-sdk
Version:
Aftermath TypeScript SDK
449 lines (448 loc) • 25.8 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PerpetualsAccount = void 0;
const caller_1 = require("../../general/utils/caller");
const types_1 = require("../../types");
const iFixedUtils_1 = require("../../general/utils/iFixedUtils");
const utils_1 = require("../../general/utils");
const perpetuals_1 = require("./perpetuals");
const __1 = require("..");
class PerpetualsAccount extends caller_1.Caller {
// =========================================================================
// Constructor
// =========================================================================
constructor(account, accountCap, config) {
super(config, "perpetuals");
this.account = account;
this.accountCap = accountCap;
// =========================================================================
// Calculations
// =========================================================================
this.calcFreeCollateralForPosition = (inputs) => {
var _a, _b;
const marketId = inputs.market.marketId;
const position = (_b = (_a = inputs.position) !== null && _a !== void 0 ? _a : this.positionForMarketId({ marketId })) !== null && _b !== void 0 ? _b : this.emptyPosition({ market: inputs.market });
const funding = this.calcUnrealizedFundingsForPosition(inputs);
const { pnl, minInitialMargin } = this.calcPnLAndMarginForPosition(inputs);
let collateralUsd = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.collateral) *
inputs.collateralPrice;
collateralUsd += funding;
let cappedMargin;
if (pnl < 0) {
cappedMargin = collateralUsd + pnl;
}
else {
cappedMargin = collateralUsd;
}
if (cappedMargin >= minInitialMargin) {
return (cappedMargin - minInitialMargin) / inputs.collateralPrice;
}
else
return 0;
};
this.calcMarginRatioAndLeverageForPosition = (inputs) => {
var _a, _b;
const { market, indexPrice, collateralPrice } = inputs;
const marketId = market.marketId;
const position = (_b = (_a = inputs.position) !== null && _a !== void 0 ? _a : this.positionForMarketId({ marketId })) !== null && _b !== void 0 ? _b : this.emptyPosition({ market });
const funding = this.calcUnrealizedFundingsForPosition({
market,
position,
});
const collateralUsd = iFixedUtils_1.IFixedUtils.numberFromIFixed(position === null || position === void 0 ? void 0 : position.collateral) *
collateralPrice +
funding;
const { pnl, netAbsBaseValue } = this.calcPnLAndMarginForPosition({
market,
indexPrice,
position,
});
const marginRatio = netAbsBaseValue === 0 ? 0 : (collateralUsd + pnl) / netAbsBaseValue;
const leverage = marginRatio === 0 ? 0 : 1 / marginRatio;
return {
marginRatio,
leverage,
};
};
this.calcUnrealizedFundings = (inputs) => {
let totalFunding = 0;
inputs.markets.forEach((market) => {
totalFunding += this.calcUnrealizedFundingsForPosition({
market,
});
});
return totalFunding;
};
this.calcUnrealizedFundingsForPosition = (inputs) => {
var _a, _b;
const marketId = inputs.market.marketId;
const position = (_b = (_a = inputs.position) !== null && _a !== void 0 ? _a : this.positionForMarketId({ marketId })) !== null && _b !== void 0 ? _b : this.emptyPosition({ market: inputs.market });
const baseAmount = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.baseAssetAmount);
const isLong = Math.sign(baseAmount);
if (isLong < 0) {
const fundingShort = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.cumFundingRateShort);
const marketFundingShort = iFixedUtils_1.IFixedUtils.numberFromIFixed(inputs.market.marketState.cumFundingRateShort);
return -baseAmount * (marketFundingShort - fundingShort);
}
else {
const fundingLong = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.cumFundingRateLong);
const marketFundingLong = iFixedUtils_1.IFixedUtils.numberFromIFixed(inputs.market.marketState.cumFundingRateLong);
return -baseAmount * (marketFundingLong - fundingLong);
}
};
this.calcPnLAndMarginForPosition = (inputs) => {
var _a, _b;
const marketId = inputs.market.marketId;
const position = (_b = (_a = inputs.position) !== null && _a !== void 0 ? _a : this.positionForMarketId({ marketId })) !== null && _b !== void 0 ? _b : this.emptyPosition({ market: inputs.market });
const marginRatioInitial = 1 / position.leverage;
// const marginRatioInitial = inputs.market.initialMarginRatio();
const marginRatioMaintenance = iFixedUtils_1.IFixedUtils.numberFromIFixed(inputs.market.marketParams.marginRatioMaintenance);
const baseAssetAmount = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.baseAssetAmount);
const quoteAssetAmount = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.quoteAssetNotionalAmount);
const bidsQuantity = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.bidsQuantity);
const asksQuantity = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.asksQuantity);
const pnl = baseAssetAmount * inputs.indexPrice - quoteAssetAmount;
const netAbs = Math.max(Math.abs(baseAssetAmount + bidsQuantity), Math.abs(baseAssetAmount - asksQuantity));
const netAbsBaseValue = netAbs * inputs.indexPrice;
const minInitialMargin = netAbsBaseValue * marginRatioInitial;
const minMaintenanceMargin = netAbsBaseValue * marginRatioMaintenance;
return { pnl, minInitialMargin, minMaintenanceMargin, netAbsBaseValue };
};
this.calcLiquidationPriceForPosition = (inputs) => {
var _a, _b;
const marketId = inputs.market.marketId;
const position = (_b = (_a = inputs.position) !== null && _a !== void 0 ? _a : this.positionForMarketId({ marketId })) !== null && _b !== void 0 ? _b : this.emptyPosition({ market: inputs.market });
const funding = this.calcUnrealizedFundingsForPosition(inputs);
const collateralUsd = iFixedUtils_1.IFixedUtils.numberFromIFixed(position === null || position === void 0 ? void 0 : position.collateral) *
inputs.collateralPrice +
funding;
const baseAssetAmount = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.baseAssetAmount);
const quoteAssetAmount = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.quoteAssetNotionalAmount);
const MMR = iFixedUtils_1.IFixedUtils.numberFromIFixed(inputs.market.marketParams.marginRatioMaintenance);
const numerator = collateralUsd - quoteAssetAmount;
const price = (() => {
if (baseAssetAmount > 0) {
return numerator / ((1 - MMR) * -baseAssetAmount);
}
else {
return numerator / ((1 + MMR) * -baseAssetAmount);
}
})();
return price < 0 ? 0 : price;
};
this.calcFreeMarginUsdForPosition = (inputs) => {
var _a, _b;
const marketId = inputs.market.marketId;
const position = (_b = (_a = inputs.position) !== null && _a !== void 0 ? _a : this.positionForMarketId({ marketId })) !== null && _b !== void 0 ? _b : this.emptyPosition({ market: inputs.market });
const totalFunding = this.calcUnrealizedFundingsForPosition(inputs);
const { pnl, minInitialMargin } = this.calcPnLAndMarginForPosition(inputs);
let collateralUsd = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.collateral) *
inputs.collateralPrice;
const margin = collateralUsd + totalFunding + pnl;
if (margin >= minInitialMargin) {
return margin - minInitialMargin;
}
else
return 0;
};
this.calcAccountState = (inputs) => {
const zipped = utils_1.Helpers.zip(inputs.markets, inputs.indexPrices);
let accountEquity = 0;
let totalPnL = 0;
let totalFunding = 0;
let totalCollateralAllocated = 0;
zipped.forEach(([market, indexPrice]) => {
var _a;
const marketId = market.marketId;
const position = (_a = this.positionForMarketId({ marketId })) !== null && _a !== void 0 ? _a : this.emptyPosition({ market });
const funding = this.calcUnrealizedFundingsForPosition({
market,
position,
});
const { pnl } = this.calcPnLAndMarginForPosition({
market,
indexPrice,
position,
});
let collateralUsd = iFixedUtils_1.IFixedUtils.numberFromIFixed(position.collateral) *
inputs.collateralPrice;
totalPnL += pnl;
totalFunding += funding;
totalCollateralAllocated += collateralUsd;
accountEquity += collateralUsd + funding + pnl;
});
return {
accountEquity,
totalPnL,
totalFunding,
totalCollateralAllocated,
};
};
this.closePositionTxInputs = (inputs) => {
var _a;
const { size, market, walletAddress, orderDatas, collateralPrice } = inputs;
const marketId = market.marketId;
const position = (_a = this.positionForMarketId({ marketId })) !== null && _a !== void 0 ? _a : this.emptyPosition({ market });
// TODO: move conversion to helper function, since used often
const ordersCollateral = __1.Coin.normalizeBalance(utils_1.Helpers.sum(orderDatas
.filter((orderData) => orderData.marketId === market.marketId)
.map((orderData) => market.calcCollateralUsedForOrder(Object.assign(Object.assign({}, inputs), { orderData, leverage: position.leverage })).collateral)), this.collateralDecimals());
const fullPositionCollateralChange = utils_1.Helpers.maxBigInt(BigInt(Math.floor(Number(__1.Coin.normalizeBalance(this.calcFreeMarginUsdForPosition(inputs) *
collateralPrice, this.collateralDecimals()) - ordersCollateral) *
(1 -
PerpetualsAccount.constants
.closePositionMarginOfError))), BigInt(0)) * BigInt(-1);
const positionSize = BigInt(Math.round(Math.abs(utils_1.Casting.IFixed.numberFromIFixed(position.baseAssetAmount) /
market.lotSize())));
const collateralChange = BigInt(Math.round(Number(fullPositionCollateralChange) *
(Number(size) / Number(positionSize))));
const positionSide = perpetuals_1.Perpetuals.positionSide(position);
return {
size,
marketId,
walletAddress,
collateralChange,
side: positionSide === types_1.PerpetualsOrderSide.Bid
? types_1.PerpetualsOrderSide.Ask
: types_1.PerpetualsOrderSide.Bid,
hasPosition: this.positionForMarketId({ marketId }) !== undefined,
};
};
this.emptyPosition = (inputs) => {
const { market } = inputs;
return {
marketId: market.marketId,
collateralCoinType: this.accountCap.collateralCoinType,
collateral: BigInt(0),
baseAssetAmount: BigInt(0),
quoteAssetNotionalAmount: BigInt(0),
cumFundingRateLong: market.marketState.cumFundingRateLong,
cumFundingRateShort: market.marketState.cumFundingRateShort,
asksQuantity: BigInt(0),
bidsQuantity: BigInt(0),
pendingOrders: [],
makerFee: BigInt(1000000000000000000), // 100%
takerFee: BigInt(1000000000000000000), // 100%
leverage: 1,
};
};
}
// =========================================================================
// Transactions
// =========================================================================
// =========================================================================
// Collateral Txs
// =========================================================================
getDepositCollateralTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApiTransaction("transactions/deposit-collateral", Object.assign(Object.assign({}, inputs), { collateralCoinType: this.accountCap.collateralCoinType, accountCapId: this.accountCap.objectId }));
});
}
getWithdrawCollateralTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApiTransaction("transactions/withdraw-collateral", Object.assign(Object.assign({}, inputs), { collateralCoinType: this.accountCap.collateralCoinType, accountCapId: this.accountCap.objectId }));
});
}
getAllocateCollateralTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
const { market } = inputs;
return this.fetchApiTransaction("transactions/allocate-collateral", Object.assign(Object.assign({}, inputs), { packageId: market.marketData.packageId, marketInitialSharedVersion: market.marketData.initialSharedVersion, marketId: market.marketId, collateralCoinType: this.accountCap.collateralCoinType, accountCapId: this.accountCap.objectId }));
});
}
getDeallocateCollateralTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
const { market } = inputs;
return this.fetchApiTransaction("transactions/deallocate-collateral", Object.assign(Object.assign({}, inputs), { packageId: market.marketData.packageId, marketInitialSharedVersion: market.marketData.initialSharedVersion, marketId: market.marketId, basePriceFeedId: market.marketParams.basePriceFeedId, collateralPriceFeedId: market.marketParams.collateralPriceFeedId, collateralCoinType: this.accountCap.collateralCoinType, accountCapId: this.accountCap.objectId }));
});
}
getTransferCollateralTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApiTransaction("transactions/transfer-collateral", Object.assign(Object.assign({}, inputs), { collateralCoinType: this.accountCap.collateralCoinType, fromAccountCapId: this.accountCap.objectId }));
});
}
// =========================================================================
// Order Txs
// =========================================================================
getPlaceMarketOrderTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApiTransaction("transactions/market-order", Object.assign(Object.assign({}, inputs), { accountObjectId: this.accountCap.objectId, accountObjectVersion: this.accountCap.objectVersion, accountObjectDigest: this.accountCap.objectDigest, hasPosition: this.positionForMarketId(inputs) !== undefined }));
});
}
getPlaceLimitOrderTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApiTransaction("transactions/limit-order", Object.assign(Object.assign({}, inputs), { accountObjectId: this.accountCap.objectId, accountObjectVersion: this.accountCap.objectVersion, accountObjectDigest: this.accountCap.objectDigest, hasPosition: this.positionForMarketId(inputs) !== undefined }));
});
}
getPlaceSLTPOrder(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApiTransaction("transactions/sltp-order", Object.assign(Object.assign({}, inputs), { accountObjectId: this.accountCap.objectId, accountObjectVersion: this.accountCap.objectVersion, accountObjectDigest: this.accountCap.objectDigest }));
});
}
getCancelOrderTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApiTransaction("transactions/cancel-order", Object.assign(Object.assign({}, inputs), { collateralCoinType: this.accountCap.collateralCoinType, accountCapId: this.accountCap.objectId }));
});
}
getCancelOrdersTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApiTransaction("transactions/cancel-orders", Object.assign(Object.assign({}, inputs), { collateralCoinType: this.accountCap.collateralCoinType, accountCapId: this.accountCap.objectId }));
});
}
getReduceOrdersTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
const { market, orderDatas } = inputs;
return this.fetchApiTransaction("transactions/reduce-orders", Object.assign(Object.assign({}, inputs), { orderIds: orderDatas.map((order) => order.orderId), sizesToSubtract: orderDatas.map((order) => order.sizeToSubtract), packageId: market.marketData.packageId, marketInitialSharedVersion: market.marketData.initialSharedVersion, marketId: market.marketId, basePriceFeedId: market.marketParams.basePriceFeedId, collateralPriceFeedId: market.marketParams.collateralPriceFeedId, collateralCoinType: this.accountCap.collateralCoinType, accountCapId: this.accountCap.objectId }));
});
}
// =========================================================================
// Position Txs
// =========================================================================
getClosePositionTx(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.getPlaceMarketOrderTx(this.closePositionTxInputs(inputs));
});
}
// =========================================================================
// Inspections
// =========================================================================
setPositionLeverage(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApi(`${this.accountCap.collateralCoinType}/accounts/${this.accountCap.accountId}/set-position-leverage`, inputs);
});
}
// public async getAllPositionLeverages(): Promise<
// {
// marketId: PerpetualsMarketId;
// leverage: number;
// }[]
// > {
// return this.fetchApi(
// `${this.accountCap.collateralCoinType}/accounts/${this.accountCap.accountId}/position-leverages`
// );
// }
getPositionLeverages(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApi(`${this.accountCap.collateralCoinType}/accounts/${this.accountCap.accountId}/position-leverages/${JSON.stringify(inputs.marketIds)}`);
});
}
setPositionLeverageMessageToSign(inputs) {
return {
account_id: Number(this.accountCap.accountId),
market_id: inputs.marketId,
leverage: inputs.leverage,
};
}
getOrderPreview(inputs, abortSignal) {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.fetchApi("preview-order", Object.assign(Object.assign({}, inputs), { accountId: this.accountCap.accountId, collateralCoinType: this.accountCap.collateralCoinType }), abortSignal);
if ("error" in response)
return response;
const { collateralChange } = response, remainingResponse = __rest(response, ["collateralChange"]);
return Object.assign(Object.assign({}, remainingResponse), { collateralChange: __1.Coin.normalizeBalance(collateralChange, this.collateralDecimals()) });
});
}
getCancelOrdersPreview(inputs) {
return __awaiter(this, void 0, void 0, function* () {
// NOTE: should this case return an error instead ?
if (Object.keys(inputs.marketIdsToData).length <= 0)
return {
collateralChange: BigInt(0),
marketIdsToPositionAfterCancelOrders: {},
};
const response = yield this.fetchApi("preview-cancel-orders", Object.assign(Object.assign({}, inputs), { accountId: this.accountCap.accountId, collateralCoinType: this.accountCap.collateralCoinType }));
if ("error" in response)
return response;
return {
marketIdsToPositionAfterCancelOrders: response.marketIdsToPositionAfterCancelOrders,
collateralChange: __1.Coin.normalizeBalance(response.collateralChange, this.collateralDecimals()),
};
});
}
getReduceOrdersPreview(inputs) {
return __awaiter(this, void 0, void 0, function* () {
// NOTE: should this case not throw an error instead ?
if (Object.keys(inputs.orderDatas).length <= 0)
throw new Error("no orderDatas provided");
const response = yield this.fetchApi("preview-reduce-orders", Object.assign(Object.assign({}, inputs), { orderIds: inputs.orderDatas.map((order) => order.orderId), sizesToSubtract: inputs.orderDatas.map((order) => order.sizeToSubtract), accountId: this.accountCap.accountId, collateralCoinType: this.accountCap.collateralCoinType }));
if ("error" in response)
return response;
return {
positionAfterReduceOrders: response.positionAfterReduceOrders,
collateralChange: __1.Coin.normalizeBalance(response.collateralChange, this.collateralDecimals()),
};
});
}
getOrderDatas() {
return __awaiter(this, void 0, void 0, function* () {
const orderDatas = this.account.positions.reduce((acc, position) => [
...acc,
...position.pendingOrders.map((order) => ({
orderId: order.orderId,
currentSize: order.size,
})),
], []);
if (orderDatas.length <= 0)
return [];
return this.fetchApi(`${this.accountCap.collateralCoinType}/accounts/${this.accountCap.accountId}/order-datas`, {
orderDatas,
});
});
}
getCollateralHistory(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApi(`${this.accountCap.collateralCoinType}/accounts/${this.accountCap.accountId}/collateral-history`, inputs);
});
}
getOrderHistory(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApi(`${this.accountCap.collateralCoinType}/accounts/${this.accountCap.accountId}/order-history`, inputs);
});
}
// =========================================================================
// Helpers
// =========================================================================
positionForMarketId(inputs) {
try {
return this.account.positions.find((pos) => pos.marketId === inputs.marketId);
}
catch (e) {
return undefined;
}
}
collateral() {
return utils_1.Casting.IFixed.numberFromIFixed(this.accountCap.collateral);
}
collateralDecimals() {
return this.accountCap.collateralDecimals;
}
collateralBalance() {
return __1.Coin.normalizeBalance(this.collateral(), this.collateralDecimals());
}
}
exports.PerpetualsAccount = PerpetualsAccount;
// =========================================================================
// Private Constants
// =========================================================================
PerpetualsAccount.constants = {
closePositionMarginOfError: 0.1, // 10%
};