UNPKG

aftermath-ts-sdk

Version:
449 lines (448 loc) 25.8 kB
"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% };