fix signal and get position during closing
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Globalization;
|
||||||
using Managing.Core;
|
using Managing.Core;
|
||||||
using Managing.Domain.Candles;
|
using Managing.Domain.Candles;
|
||||||
using Managing.Domain.Users;
|
using Managing.Domain.Users;
|
||||||
@@ -34,7 +35,7 @@ namespace Managing.Domain.Strategies
|
|||||||
StrategyType = strategyType;
|
StrategyType = strategyType;
|
||||||
User = user;
|
User = user;
|
||||||
|
|
||||||
Identifier = $"{StrategyType}-{direction}-{ticker}-{candle?.Close}-{date:yyyyMMdd-HHmmss}";
|
Identifier = $"{StrategyType}-{direction}-{ticker}-{candle?.Close.ToString(CultureInfo.InvariantCulture)}-{date:yyyyMMdd-HHmmss}";
|
||||||
SignalType = signalType;
|
SignalType = signalType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -660,7 +660,9 @@ public class EvmManager : IEvmManager
|
|||||||
{
|
{
|
||||||
account = account.Key,
|
account = account.Key,
|
||||||
ticker = ticker.ToString(),
|
ticker = ticker.ToString(),
|
||||||
direction = direction.ToString(),
|
direction = direction == TradeDirection.Long
|
||||||
|
? TradeDirection.Short.ToString()
|
||||||
|
: TradeDirection.Long.ToString(),
|
||||||
});
|
});
|
||||||
trade = new Trade(
|
trade = new Trade(
|
||||||
DateTime.UtcNow,
|
DateTime.UtcNow,
|
||||||
|
|||||||
@@ -15,483 +15,481 @@ import {EntryField, SidecarSlTpOrderEntryValid} from "../../types/sidecarOrders.
|
|||||||
|
|
||||||
/** Base Optional params for helpers, allows to avoid calling markets, tokens and uiFeeFactor methods if they are already passed */
|
/** Base Optional params for helpers, allows to avoid calling markets, tokens and uiFeeFactor methods if they are already passed */
|
||||||
interface BaseOptionalParams {
|
interface BaseOptionalParams {
|
||||||
marketsInfoData?: MarketsInfoData;
|
marketsInfoData?: MarketsInfoData;
|
||||||
tokensData?: TokensData;
|
tokensData?: TokensData;
|
||||||
uiFeeFactor?: bigint;
|
uiFeeFactor?: bigint;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PositionIncreaseParams = (
|
export type PositionIncreaseParams = (
|
||||||
| {
|
| {
|
||||||
/** Increase amounts will be calculated based on collateral amount */
|
/** Increase amounts will be calculated based on collateral amount */
|
||||||
payAmount: bigint;
|
payAmount: bigint;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
/** Increase amounts will be calculated based on position size amount */
|
/** Increase amounts will be calculated based on position size amount */
|
||||||
sizeAmount: bigint;
|
sizeAmount: bigint;
|
||||||
}
|
}
|
||||||
) & {
|
) & {
|
||||||
marketAddress: string;
|
marketAddress: string;
|
||||||
payTokenAddress: string;
|
payTokenAddress: string;
|
||||||
collateralTokenAddress: string;
|
collateralTokenAddress: string;
|
||||||
|
|
||||||
/** @default 100 */
|
/** @default 100 */
|
||||||
allowedSlippageBps?: number;
|
allowedSlippageBps?: number;
|
||||||
referralCodeForTxn?: string;
|
referralCodeForTxn?: string;
|
||||||
|
|
||||||
leverage?: bigint;
|
leverage?: bigint;
|
||||||
/** If presented, then it's limit order */
|
/** If presented, then it's limit order */
|
||||||
limitPrice?: bigint;
|
limitPrice?: bigint;
|
||||||
acceptablePriceImpactBuffer?: number;
|
acceptablePriceImpactBuffer?: number;
|
||||||
fixedAcceptablePriceImpactBps?: bigint;
|
fixedAcceptablePriceImpactBps?: bigint;
|
||||||
|
|
||||||
skipSimulation?: boolean;
|
skipSimulation?: boolean;
|
||||||
stopLossPrice?: bigint;
|
stopLossPrice?: bigint;
|
||||||
takeProfitPrice?: bigint;
|
takeProfitPrice?: bigint;
|
||||||
} & BaseOptionalParams;
|
} & BaseOptionalParams;
|
||||||
|
|
||||||
async function getAndValidateBaseParams(sdk: GmxSdk, params: BaseOptionalParams) {
|
async function getAndValidateBaseParams(sdk: GmxSdk, params: BaseOptionalParams) {
|
||||||
let tokensData: TokensData | undefined = params.tokensData;
|
let tokensData: TokensData | undefined = params.tokensData;
|
||||||
let marketsInfoData: MarketsInfoData | undefined = params.marketsInfoData;
|
let marketsInfoData: MarketsInfoData | undefined = params.marketsInfoData;
|
||||||
|
|
||||||
if (!params.marketsInfoData && !params.tokensData) {
|
if (!params.marketsInfoData && !params.tokensData) {
|
||||||
const result = await sdk.markets.getMarketsInfo();
|
const result = await sdk.markets.getMarketsInfo();
|
||||||
marketsInfoData = result.marketsInfoData;
|
marketsInfoData = result.marketsInfoData;
|
||||||
tokensData = result.tokensData;
|
tokensData = result.tokensData;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tokensData) {
|
if (!tokensData) {
|
||||||
throw new Error("Tokens data is not available");
|
throw new Error("Tokens data is not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!marketsInfoData) {
|
if (!marketsInfoData) {
|
||||||
throw new Error("Markets info data is not available");
|
throw new Error("Markets info data is not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
let uiFeeFactor = params.uiFeeFactor;
|
let uiFeeFactor = params.uiFeeFactor;
|
||||||
if (!uiFeeFactor) {
|
if (!uiFeeFactor) {
|
||||||
uiFeeFactor = await sdk.utils.getUiFeeFactor();
|
uiFeeFactor = await sdk.utils.getUiFeeFactor();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tokensData,
|
tokensData,
|
||||||
marketsInfoData,
|
marketsInfoData,
|
||||||
uiFeeFactor,
|
uiFeeFactor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function increaseOrderHelper(
|
export async function increaseOrderHelper(
|
||||||
sdk: GmxSdk,
|
sdk: GmxSdk,
|
||||||
params: PositionIncreaseParams & {
|
params: PositionIncreaseParams & {
|
||||||
isLong: boolean;
|
isLong: boolean;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const { tokensData, marketsInfoData, uiFeeFactor } = await getAndValidateBaseParams(sdk, params);
|
const {tokensData, marketsInfoData, uiFeeFactor} = await getAndValidateBaseParams(sdk, params);
|
||||||
|
|
||||||
const isLimit = Boolean(params.limitPrice);
|
const isLimit = Boolean(params.limitPrice);
|
||||||
|
|
||||||
const fromToken = tokensData[params.payTokenAddress];
|
const fromToken = tokensData[params.payTokenAddress];
|
||||||
const collateralToken = tokensData[params.collateralTokenAddress];
|
const collateralToken = tokensData[params.collateralTokenAddress];
|
||||||
|
|
||||||
if (!fromToken) {
|
if (!fromToken) {
|
||||||
throw new Error("From token is not available");
|
throw new Error("From token is not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!collateralToken) {
|
if (!collateralToken) {
|
||||||
throw new Error("Collateral token is not available");
|
throw new Error("Collateral token is not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
const marketInfo = getByKey(marketsInfoData, params.marketAddress);
|
const marketInfo = getByKey(marketsInfoData, params.marketAddress);
|
||||||
|
|
||||||
if (!marketInfo) {
|
if (!marketInfo) {
|
||||||
throw new Error("Market info is not available");
|
throw new Error("Market info is not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
const collateralTokenAddress = collateralToken.address;
|
const collateralTokenAddress = collateralToken.address;
|
||||||
const allowedSlippage = params.allowedSlippageBps ?? 100;
|
const allowedSlippage = params.allowedSlippageBps ?? 100;
|
||||||
|
|
||||||
const graph = getMarketsGraph(Object.values(marketsInfoData));
|
const graph = getMarketsGraph(Object.values(marketsInfoData));
|
||||||
const wrappedFromAddress = getWrappedAddress(sdk.chainId, params.payTokenAddress);
|
const wrappedFromAddress = getWrappedAddress(sdk.chainId, params.payTokenAddress);
|
||||||
const wrappedToAddress = getWrappedAddress(sdk.chainId, collateralTokenAddress);
|
const wrappedToAddress = getWrappedAddress(sdk.chainId, collateralTokenAddress);
|
||||||
|
|
||||||
const allPaths = findAllSwapPaths({
|
const allPaths = findAllSwapPaths({
|
||||||
chainId: sdk.chainId,
|
chainId: sdk.chainId,
|
||||||
fromTokenAddress: params.payTokenAddress,
|
fromTokenAddress: params.payTokenAddress,
|
||||||
toTokenAddress: collateralTokenAddress,
|
toTokenAddress: collateralTokenAddress,
|
||||||
marketsInfoData,
|
marketsInfoData,
|
||||||
graph,
|
graph,
|
||||||
wrappedFromAddress,
|
wrappedFromAddress,
|
||||||
wrappedToAddress,
|
wrappedToAddress,
|
||||||
});
|
|
||||||
|
|
||||||
const estimator = createSwapEstimator(marketsInfoData);
|
|
||||||
|
|
||||||
const findSwapPath = createFindSwapPath({
|
|
||||||
chainId: sdk.chainId,
|
|
||||||
fromTokenAddress: params.payTokenAddress,
|
|
||||||
toTokenAddress: collateralTokenAddress,
|
|
||||||
marketsInfoData,
|
|
||||||
estimator,
|
|
||||||
allPaths,
|
|
||||||
});
|
|
||||||
|
|
||||||
const payOrSizeAmount = "payAmount" in params ? params.payAmount : params.sizeAmount;
|
|
||||||
|
|
||||||
const increaseAmounts = getIncreasePositionAmounts({
|
|
||||||
marketInfo,
|
|
||||||
indexToken: marketInfo.indexToken,
|
|
||||||
initialCollateralToken: fromToken,
|
|
||||||
collateralToken,
|
|
||||||
isLong: params.isLong,
|
|
||||||
initialCollateralAmount: payOrSizeAmount,
|
|
||||||
position: undefined,
|
|
||||||
indexTokenAmount: payOrSizeAmount,
|
|
||||||
leverage: params.leverage,
|
|
||||||
triggerPrice: params.limitPrice,
|
|
||||||
limitOrderType: params.limitPrice ? OrderType.LimitIncrease : undefined,
|
|
||||||
userReferralInfo: undefined,
|
|
||||||
strategy: "payAmount" in params ? "leverageByCollateral" : "leverageBySize",
|
|
||||||
findSwapPath: findSwapPath,
|
|
||||||
uiFeeFactor,
|
|
||||||
acceptablePriceImpactBuffer: params.acceptablePriceImpactBuffer,
|
|
||||||
fixedAcceptablePriceImpactBps: params.fixedAcceptablePriceImpactBps,
|
|
||||||
externalSwapQuote: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
const createSltpEntries: SidecarSlTpOrderEntryValid[] = [
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
let stopLossDecreaseAmounts: DecreasePositionAmounts | undefined;
|
|
||||||
if (params.stopLossPrice) {
|
|
||||||
const stopLossCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.stopLossPrice);
|
|
||||||
|
|
||||||
const acceptablePriceInfo = getAcceptablePriceInfo({
|
|
||||||
marketInfo,
|
|
||||||
isIncrease: false,
|
|
||||||
isLong: params.isLong,
|
|
||||||
indexPrice: params.stopLossPrice,
|
|
||||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
|
||||||
maxNegativePriceImpactBps: marketInfo.maxPositionImpactFactorForLiquidations,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
stopLossDecreaseAmounts = {
|
const estimator = createSwapEstimator(marketsInfoData);
|
||||||
isFullClose: true,
|
|
||||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
|
||||||
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
|
|
||||||
collateralDeltaUsd: stopLossCollateralDeltaUsd,
|
|
||||||
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
|
|
||||||
indexPrice: params.stopLossPrice,
|
|
||||||
collateralPrice: 0n,
|
|
||||||
acceptablePrice: params.isLong ? 2n ** 256n - 1n : 0n,
|
|
||||||
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps,
|
|
||||||
recommendedAcceptablePriceDeltaBps: 0n,
|
|
||||||
estimatedPnl: 0n,
|
|
||||||
estimatedPnlPercentage: 0n,
|
|
||||||
realizedPnl: 0n,
|
|
||||||
realizedPnlPercentage: 0n,
|
|
||||||
positionFeeUsd: 0n,
|
|
||||||
uiFeeUsd: 0n,
|
|
||||||
swapUiFeeUsd: 0n,
|
|
||||||
feeDiscountUsd: 0n,
|
|
||||||
borrowingFeeUsd: 0n,
|
|
||||||
fundingFeeUsd: 0n,
|
|
||||||
swapProfitFeeUsd: 0n,
|
|
||||||
positionPriceImpactDeltaUsd: 0n,
|
|
||||||
priceImpactDiffUsd: 0n,
|
|
||||||
payedRemainingCollateralAmount: 0n,
|
|
||||||
payedOutputUsd: 0n,
|
|
||||||
payedRemainingCollateralUsd: 0n,
|
|
||||||
receiveTokenAmount: 0n,
|
|
||||||
receiveUsd: 0n,
|
|
||||||
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
|
|
||||||
triggerOrderType: OrderType.StopLossDecrease,
|
|
||||||
triggerPrice: params.stopLossPrice,
|
|
||||||
}
|
|
||||||
|
|
||||||
const stopLossEntry: SidecarSlTpOrderEntryValid = {
|
const findSwapPath = createFindSwapPath({
|
||||||
decreaseAmounts: stopLossDecreaseAmounts,
|
chainId: sdk.chainId,
|
||||||
id: "sl-order",
|
fromTokenAddress: params.payTokenAddress,
|
||||||
price: {
|
toTokenAddress: collateralTokenAddress,
|
||||||
input: params.stopLossPrice?.toString() ?? "",
|
marketsInfoData,
|
||||||
value: params.stopLossPrice,
|
estimator,
|
||||||
error: null,
|
allPaths,
|
||||||
} as EntryField,
|
|
||||||
sizeUsd: {
|
|
||||||
input: increaseAmounts.sizeDeltaUsd.toString(),
|
|
||||||
value: increaseAmounts.sizeDeltaUsd,
|
|
||||||
error: null,
|
|
||||||
} as EntryField,
|
|
||||||
percentage: {
|
|
||||||
input: "100",
|
|
||||||
value: 100n,
|
|
||||||
error: null,
|
|
||||||
} as EntryField,
|
|
||||||
txnType: "create",
|
|
||||||
mode: "keepSize",
|
|
||||||
order: null,
|
|
||||||
increaseAmounts: undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
createSltpEntries.push(stopLossEntry)
|
|
||||||
}
|
|
||||||
|
|
||||||
let takeProfitDecreaseAmounts: DecreasePositionAmounts | undefined;
|
|
||||||
if (params.takeProfitPrice) {
|
|
||||||
const takeProfitCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.takeProfitPrice);
|
|
||||||
|
|
||||||
const acceptablePriceInfo = getAcceptablePriceInfo({
|
|
||||||
marketInfo,
|
|
||||||
isIncrease: false,
|
|
||||||
isLong: params.isLong,
|
|
||||||
indexPrice: params.takeProfitPrice,
|
|
||||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
|
||||||
maxNegativePriceImpactBps: marketInfo.maxPositionImpactFactorForLiquidations,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
takeProfitDecreaseAmounts = {
|
const payOrSizeAmount = "payAmount" in params ? params.payAmount : params.sizeAmount;
|
||||||
isFullClose: true,
|
|
||||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
const increaseAmounts = getIncreasePositionAmounts({
|
||||||
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
|
marketInfo,
|
||||||
collateralDeltaUsd: takeProfitCollateralDeltaUsd,
|
indexToken: marketInfo.indexToken,
|
||||||
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
|
initialCollateralToken: fromToken,
|
||||||
indexPrice: params.takeProfitPrice, // Keep original trigger price for indexPrice
|
collateralToken,
|
||||||
collateralPrice: 0n, // Consider if this needs calculation
|
isLong: params.isLong,
|
||||||
acceptablePrice: acceptablePriceInfo.acceptablePrice,
|
initialCollateralAmount: payOrSizeAmount,
|
||||||
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps,
|
position: undefined,
|
||||||
recommendedAcceptablePriceDeltaBps: 0n,
|
indexTokenAmount: payOrSizeAmount,
|
||||||
estimatedPnl: 0n,
|
leverage: params.leverage,
|
||||||
estimatedPnlPercentage: 0n,
|
triggerPrice: params.limitPrice,
|
||||||
realizedPnl: 0n,
|
limitOrderType: params.limitPrice ? OrderType.LimitIncrease : undefined,
|
||||||
realizedPnlPercentage: 0n,
|
userReferralInfo: undefined,
|
||||||
positionFeeUsd: 0n,
|
strategy: "payAmount" in params ? "leverageByCollateral" : "leverageBySize",
|
||||||
uiFeeUsd: 0n,
|
findSwapPath: findSwapPath,
|
||||||
swapUiFeeUsd: 0n,
|
uiFeeFactor,
|
||||||
feeDiscountUsd: 0n,
|
acceptablePriceImpactBuffer: params.acceptablePriceImpactBuffer,
|
||||||
borrowingFeeUsd: 0n,
|
fixedAcceptablePriceImpactBps: params.fixedAcceptablePriceImpactBps,
|
||||||
fundingFeeUsd: 0n,
|
externalSwapQuote: undefined,
|
||||||
swapProfitFeeUsd: 0n,
|
});
|
||||||
positionPriceImpactDeltaUsd: 0n,
|
|
||||||
priceImpactDiffUsd: 0n,
|
const createSltpEntries: SidecarSlTpOrderEntryValid[] = []
|
||||||
payedRemainingCollateralAmount: 0n,
|
|
||||||
payedOutputUsd: 0n,
|
let stopLossDecreaseAmounts: DecreasePositionAmounts | undefined;
|
||||||
payedRemainingCollateralUsd: 0n,
|
if (params.stopLossPrice) {
|
||||||
receiveTokenAmount: 0n,
|
const stopLossCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.stopLossPrice);
|
||||||
receiveUsd: 0n,
|
|
||||||
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
|
const acceptablePriceInfo = getAcceptablePriceInfo({
|
||||||
triggerOrderType: OrderType.LimitDecrease,
|
marketInfo,
|
||||||
triggerPrice: params.takeProfitPrice,
|
isIncrease: false,
|
||||||
|
isLong: params.isLong,
|
||||||
|
indexPrice: params.stopLossPrice,
|
||||||
|
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||||
|
maxNegativePriceImpactBps: marketInfo.maxPositionImpactFactorForLiquidations,
|
||||||
|
});
|
||||||
|
|
||||||
|
stopLossDecreaseAmounts = {
|
||||||
|
isFullClose: true,
|
||||||
|
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||||
|
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
|
||||||
|
collateralDeltaUsd: stopLossCollateralDeltaUsd,
|
||||||
|
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
|
||||||
|
indexPrice: params.stopLossPrice,
|
||||||
|
collateralPrice: 0n,
|
||||||
|
acceptablePrice: params.isLong ? 2n ** 256n - 1n : 0n,
|
||||||
|
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps + 50n,
|
||||||
|
recommendedAcceptablePriceDeltaBps: 50n,
|
||||||
|
estimatedPnl: 0n,
|
||||||
|
estimatedPnlPercentage: 0n,
|
||||||
|
realizedPnl: 0n,
|
||||||
|
realizedPnlPercentage: 0n,
|
||||||
|
positionFeeUsd: 0n,
|
||||||
|
uiFeeUsd: 0n,
|
||||||
|
swapUiFeeUsd: 0n,
|
||||||
|
feeDiscountUsd: 0n,
|
||||||
|
borrowingFeeUsd: 0n,
|
||||||
|
fundingFeeUsd: 0n,
|
||||||
|
swapProfitFeeUsd: 0n,
|
||||||
|
positionPriceImpactDeltaUsd: 0n,
|
||||||
|
priceImpactDiffUsd: 0n,
|
||||||
|
payedRemainingCollateralAmount: 0n,
|
||||||
|
payedOutputUsd: 0n,
|
||||||
|
payedRemainingCollateralUsd: 0n,
|
||||||
|
receiveTokenAmount: 0n,
|
||||||
|
receiveUsd: 0n,
|
||||||
|
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
|
||||||
|
triggerOrderType: OrderType.StopLossDecrease,
|
||||||
|
triggerPrice: params.stopLossPrice,
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopLossEntry: SidecarSlTpOrderEntryValid = {
|
||||||
|
decreaseAmounts: stopLossDecreaseAmounts,
|
||||||
|
id: "sl-order",
|
||||||
|
price: {
|
||||||
|
input: params.stopLossPrice?.toString() ?? "",
|
||||||
|
value: params.stopLossPrice,
|
||||||
|
error: null,
|
||||||
|
} as EntryField,
|
||||||
|
sizeUsd: {
|
||||||
|
input: increaseAmounts.sizeDeltaUsd.toString(),
|
||||||
|
value: increaseAmounts.sizeDeltaUsd,
|
||||||
|
error: null,
|
||||||
|
} as EntryField,
|
||||||
|
percentage: {
|
||||||
|
input: "100",
|
||||||
|
value: 100n,
|
||||||
|
error: null,
|
||||||
|
} as EntryField,
|
||||||
|
txnType: "create",
|
||||||
|
mode: "keepSize",
|
||||||
|
order: null,
|
||||||
|
increaseAmounts: undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
createSltpEntries.push(stopLossEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
const takeProfitEntry: SidecarSlTpOrderEntryValid = {
|
let takeProfitDecreaseAmounts: DecreasePositionAmounts | undefined;
|
||||||
decreaseAmounts: takeProfitDecreaseAmounts,
|
if (params.takeProfitPrice) {
|
||||||
id: "tp-order",
|
const takeProfitCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.takeProfitPrice);
|
||||||
price: {
|
|
||||||
input: params.takeProfitPrice?.toString() ?? "",
|
const acceptablePriceInfo = getAcceptablePriceInfo({
|
||||||
value: params.takeProfitPrice,
|
marketInfo,
|
||||||
error: null,
|
isIncrease: false,
|
||||||
} as EntryField,
|
isLong: params.isLong,
|
||||||
sizeUsd: {
|
indexPrice: params.takeProfitPrice,
|
||||||
input: increaseAmounts.sizeDeltaUsd.toString(),
|
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||||
value: increaseAmounts.sizeDeltaUsd,
|
maxNegativePriceImpactBps: marketInfo.maxPositionImpactFactorForLiquidations,
|
||||||
error: null,
|
});
|
||||||
} as EntryField,
|
|
||||||
percentage: {
|
takeProfitDecreaseAmounts = {
|
||||||
input: "100",
|
isFullClose: true,
|
||||||
value: 100n,
|
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||||
error: null,
|
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
|
||||||
} as EntryField,
|
collateralDeltaUsd: takeProfitCollateralDeltaUsd,
|
||||||
txnType: "create",
|
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
|
||||||
mode: "keepSize",
|
indexPrice: params.takeProfitPrice, // Keep original trigger price for indexPrice
|
||||||
order: null,
|
collateralPrice: 0n, // Consider if this needs calculation
|
||||||
increaseAmounts: undefined
|
acceptablePrice: acceptablePriceInfo.acceptablePrice,
|
||||||
|
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps + 50n, // Add 0.5% buffer to acceptable price impact
|
||||||
|
recommendedAcceptablePriceDeltaBps: 50n, // Set recommended buffer to 0.5%
|
||||||
|
estimatedPnl: 0n,
|
||||||
|
estimatedPnlPercentage: 0n,
|
||||||
|
realizedPnl: 0n,
|
||||||
|
realizedPnlPercentage: 0n,
|
||||||
|
positionFeeUsd: 0n,
|
||||||
|
uiFeeUsd: 0n,
|
||||||
|
swapUiFeeUsd: 0n,
|
||||||
|
feeDiscountUsd: 0n,
|
||||||
|
borrowingFeeUsd: 0n,
|
||||||
|
fundingFeeUsd: 0n,
|
||||||
|
swapProfitFeeUsd: 0n,
|
||||||
|
positionPriceImpactDeltaUsd: 0n,
|
||||||
|
priceImpactDiffUsd: 0n,
|
||||||
|
payedRemainingCollateralAmount: 0n,
|
||||||
|
payedOutputUsd: 0n,
|
||||||
|
payedRemainingCollateralUsd: 0n,
|
||||||
|
receiveTokenAmount: 0n,
|
||||||
|
receiveUsd: 0n,
|
||||||
|
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
|
||||||
|
triggerOrderType: OrderType.LimitDecrease,
|
||||||
|
triggerPrice: params.takeProfitPrice,
|
||||||
|
}
|
||||||
|
|
||||||
|
const takeProfitEntry: SidecarSlTpOrderEntryValid = {
|
||||||
|
decreaseAmounts: takeProfitDecreaseAmounts,
|
||||||
|
id: "tp-order",
|
||||||
|
price: {
|
||||||
|
input: params.takeProfitPrice?.toString() ?? "",
|
||||||
|
value: params.takeProfitPrice,
|
||||||
|
error: null,
|
||||||
|
} as EntryField,
|
||||||
|
sizeUsd: {
|
||||||
|
input: increaseAmounts.sizeDeltaUsd.toString(),
|
||||||
|
value: increaseAmounts.sizeDeltaUsd,
|
||||||
|
error: null,
|
||||||
|
} as EntryField,
|
||||||
|
percentage: {
|
||||||
|
input: "100",
|
||||||
|
value: 100n,
|
||||||
|
error: null,
|
||||||
|
} as EntryField,
|
||||||
|
txnType: "create",
|
||||||
|
mode: "keepSize",
|
||||||
|
order: null,
|
||||||
|
increaseAmounts: undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
createSltpEntries.push(takeProfitEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
createSltpEntries.push(takeProfitEntry)
|
const createIncreaseOrderParams: Parameters<typeof sdk.orders.createIncreaseOrder>[0] = {
|
||||||
}
|
marketsInfoData,
|
||||||
|
tokensData,
|
||||||
|
isLimit,
|
||||||
|
marketAddress: params.marketAddress,
|
||||||
|
fromToken: tokensData[params.payTokenAddress],
|
||||||
|
allowedSlippage,
|
||||||
|
collateralToken,
|
||||||
|
referralCodeForTxn: params.referralCodeForTxn,
|
||||||
|
triggerPrice: params.limitPrice,
|
||||||
|
collateralTokenAddress: collateralToken.address,
|
||||||
|
isLong: params.isLong,
|
||||||
|
receiveTokenAddress: collateralTokenAddress,
|
||||||
|
indexToken: marketInfo.indexToken,
|
||||||
|
marketInfo,
|
||||||
|
skipSimulation: params.skipSimulation,
|
||||||
|
increaseAmounts,
|
||||||
|
createSltpEntries: createSltpEntries.length > 0 ? createSltpEntries : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
const createIncreaseOrderParams: Parameters<typeof sdk.orders.createIncreaseOrder>[0] = {
|
return sdk.orders.createIncreaseOrder(createIncreaseOrderParams);
|
||||||
marketsInfoData,
|
|
||||||
tokensData,
|
|
||||||
isLimit,
|
|
||||||
marketAddress: params.marketAddress,
|
|
||||||
fromToken: tokensData[params.payTokenAddress],
|
|
||||||
allowedSlippage,
|
|
||||||
collateralToken,
|
|
||||||
referralCodeForTxn: params.referralCodeForTxn,
|
|
||||||
triggerPrice: params.limitPrice,
|
|
||||||
collateralTokenAddress: collateralToken.address,
|
|
||||||
isLong: params.isLong,
|
|
||||||
receiveTokenAddress: collateralTokenAddress,
|
|
||||||
indexToken: marketInfo.indexToken,
|
|
||||||
marketInfo,
|
|
||||||
skipSimulation: params.skipSimulation,
|
|
||||||
increaseAmounts,
|
|
||||||
createSltpEntries: createSltpEntries.length > 0 ? createSltpEntries : undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
return sdk.orders.createIncreaseOrder(createIncreaseOrderParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTriggerRatio({
|
function getTriggerRatio({
|
||||||
toToken,
|
toToken,
|
||||||
fromToken,
|
fromToken,
|
||||||
triggerPrice,
|
triggerPrice,
|
||||||
}: {
|
}: {
|
||||||
toToken: TokenData;
|
toToken: TokenData;
|
||||||
fromToken: TokenData;
|
fromToken: TokenData;
|
||||||
triggerPrice: bigint;
|
triggerPrice: bigint;
|
||||||
}) {
|
}) {
|
||||||
const fromTokenPrice = fromToken?.prices.minPrice;
|
const fromTokenPrice = fromToken?.prices.minPrice;
|
||||||
const markPrice = toToken.prices.minPrice;
|
const markPrice = toToken.prices.minPrice;
|
||||||
|
|
||||||
const markRatio = getTokensRatioByPrice({
|
const markRatio = getTokensRatioByPrice({
|
||||||
fromToken,
|
fromToken,
|
||||||
toToken,
|
toToken,
|
||||||
fromPrice: fromTokenPrice,
|
fromPrice: fromTokenPrice,
|
||||||
toPrice: markPrice,
|
toPrice: markPrice,
|
||||||
});
|
});
|
||||||
|
|
||||||
const triggerRatio: TokensRatio = {
|
const triggerRatio: TokensRatio = {
|
||||||
ratio: triggerPrice > 0 ? triggerPrice : markRatio.ratio,
|
ratio: triggerPrice > 0 ? triggerPrice : markRatio.ratio,
|
||||||
largestToken: markRatio.largestToken,
|
largestToken: markRatio.largestToken,
|
||||||
smallestToken: markRatio.smallestToken,
|
smallestToken: markRatio.smallestToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
return triggerRatio;
|
return triggerRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SwapParams = (
|
export type SwapParams = (
|
||||||
| {
|
| {
|
||||||
fromAmount: bigint;
|
fromAmount: bigint;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
toAmount: bigint;
|
toAmount: bigint;
|
||||||
}
|
}
|
||||||
) & {
|
) & {
|
||||||
fromTokenAddress: string;
|
fromTokenAddress: string;
|
||||||
toTokenAddress: string;
|
toTokenAddress: string;
|
||||||
allowedSlippageBps?: number;
|
allowedSlippageBps?: number;
|
||||||
referralCodeForTxn?: string;
|
referralCodeForTxn?: string;
|
||||||
|
|
||||||
/** If presented, then it's limit swap order */
|
/** If presented, then it's limit swap order */
|
||||||
triggerPrice?: bigint;
|
triggerPrice?: bigint;
|
||||||
} & BaseOptionalParams;
|
} & BaseOptionalParams;
|
||||||
|
|
||||||
export async function swap(sdk: GmxSdk, params: SwapParams) {
|
export async function swap(sdk: GmxSdk, params: SwapParams) {
|
||||||
const { tokensData, marketsInfoData, uiFeeFactor } = await getAndValidateBaseParams(sdk, params);
|
const {tokensData, marketsInfoData, uiFeeFactor} = await getAndValidateBaseParams(sdk, params);
|
||||||
|
|
||||||
const fromToken = tokensData[params.fromTokenAddress];
|
const fromToken = tokensData[params.fromTokenAddress];
|
||||||
const toToken = tokensData[params.toTokenAddress];
|
const toToken = tokensData[params.toTokenAddress];
|
||||||
|
|
||||||
if (!fromToken || !toToken) {
|
if (!fromToken || !toToken) {
|
||||||
throw new Error("From or to token is not available");
|
throw new Error("From or to token is not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLimit = Boolean(params.triggerPrice);
|
const isLimit = Boolean(params.triggerPrice);
|
||||||
|
|
||||||
if (!fromToken || !toToken) {
|
if (!fromToken || !toToken) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const graph = getMarketsGraph(Object.values(marketsInfoData));
|
const graph = getMarketsGraph(Object.values(marketsInfoData));
|
||||||
const wrappedFromAddress = getWrappedAddress(sdk.chainId, params.fromTokenAddress);
|
const wrappedFromAddress = getWrappedAddress(sdk.chainId, params.fromTokenAddress);
|
||||||
const wrappedToAddress = getWrappedAddress(sdk.chainId, params.toTokenAddress);
|
const wrappedToAddress = getWrappedAddress(sdk.chainId, params.toTokenAddress);
|
||||||
|
|
||||||
const allPaths = findAllSwapPaths({
|
const allPaths = findAllSwapPaths({
|
||||||
chainId: sdk.chainId,
|
chainId: sdk.chainId,
|
||||||
fromTokenAddress: params.fromTokenAddress,
|
fromTokenAddress: params.fromTokenAddress,
|
||||||
toTokenAddress: params.toTokenAddress,
|
toTokenAddress: params.toTokenAddress,
|
||||||
marketsInfoData,
|
marketsInfoData,
|
||||||
graph,
|
graph,
|
||||||
wrappedFromAddress,
|
wrappedFromAddress,
|
||||||
wrappedToAddress,
|
wrappedToAddress,
|
||||||
});
|
});
|
||||||
|
|
||||||
const estimator = createSwapEstimator(marketsInfoData);
|
const estimator = createSwapEstimator(marketsInfoData);
|
||||||
|
|
||||||
const findSwapPath = createFindSwapPath({
|
const findSwapPath = createFindSwapPath({
|
||||||
chainId: sdk.chainId,
|
chainId: sdk.chainId,
|
||||||
fromTokenAddress: params.fromTokenAddress,
|
fromTokenAddress: params.fromTokenAddress,
|
||||||
toTokenAddress: params.toTokenAddress,
|
toTokenAddress: params.toTokenAddress,
|
||||||
marketsInfoData,
|
marketsInfoData,
|
||||||
estimator,
|
estimator,
|
||||||
allPaths,
|
allPaths,
|
||||||
});
|
});
|
||||||
|
|
||||||
const isWrapOrUnwrap = Boolean(
|
const isWrapOrUnwrap = Boolean(
|
||||||
fromToken && toToken && (getIsWrap(fromToken, toToken) || getIsUnwrap(fromToken, toToken))
|
fromToken && toToken && (getIsWrap(fromToken, toToken) || getIsUnwrap(fromToken, toToken))
|
||||||
);
|
);
|
||||||
|
|
||||||
const swapOptimizationOrder: Parameters<FindSwapPath>[1]["order"] = isLimit ? ["length", "liquidity"] : undefined;
|
const swapOptimizationOrder: Parameters<FindSwapPath>[1]["order"] = isLimit ? ["length", "liquidity"] : undefined;
|
||||||
|
|
||||||
let swapAmounts: SwapAmounts | undefined;
|
let swapAmounts: SwapAmounts | undefined;
|
||||||
|
|
||||||
const fromTokenPrice = fromToken.prices.minPrice;
|
const fromTokenPrice = fromToken.prices.minPrice;
|
||||||
const triggerRatio = params.triggerPrice
|
const triggerRatio = params.triggerPrice
|
||||||
? getTriggerRatio({
|
? getTriggerRatio({
|
||||||
fromToken,
|
fromToken,
|
||||||
toToken,
|
toToken,
|
||||||
|
triggerPrice: params.triggerPrice,
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (isWrapOrUnwrap) {
|
||||||
|
const tokenAmount = "fromAmount" in params ? params.fromAmount : params.toAmount;
|
||||||
|
const usdAmount = convertToUsd(tokenAmount, fromToken.decimals, fromTokenPrice)!;
|
||||||
|
const price = fromTokenPrice;
|
||||||
|
|
||||||
|
swapAmounts = {
|
||||||
|
amountIn: tokenAmount,
|
||||||
|
usdIn: usdAmount!,
|
||||||
|
amountOut: tokenAmount,
|
||||||
|
usdOut: usdAmount!,
|
||||||
|
swapPathStats: undefined,
|
||||||
|
priceIn: price,
|
||||||
|
priceOut: price,
|
||||||
|
minOutputAmount: tokenAmount,
|
||||||
|
};
|
||||||
|
|
||||||
|
return swapAmounts;
|
||||||
|
} else if ("fromAmount" in params) {
|
||||||
|
swapAmounts = getSwapAmountsByFromValue({
|
||||||
|
tokenIn: fromToken,
|
||||||
|
tokenOut: toToken,
|
||||||
|
amountIn: params.fromAmount,
|
||||||
|
triggerRatio,
|
||||||
|
isLimit,
|
||||||
|
findSwapPath: findSwapPath,
|
||||||
|
uiFeeFactor,
|
||||||
|
swapOptimizationOrder,
|
||||||
|
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
swapAmounts = getSwapAmountsByToValue({
|
||||||
|
tokenIn: fromToken,
|
||||||
|
tokenOut: toToken,
|
||||||
|
amountOut: params.toAmount,
|
||||||
|
triggerRatio,
|
||||||
|
isLimit: isLimit,
|
||||||
|
findSwapPath: findSwapPath,
|
||||||
|
uiFeeFactor,
|
||||||
|
swapOptimizationOrder,
|
||||||
|
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!swapAmounts) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createSwapOrderParams: Parameters<typeof sdk.orders.createSwapOrder>[0] = {
|
||||||
|
tokensData,
|
||||||
|
fromToken: tokensData[params.fromTokenAddress],
|
||||||
|
toToken: tokensData[params.toTokenAddress],
|
||||||
|
swapAmounts,
|
||||||
|
isLimit,
|
||||||
|
allowedSlippage: params.allowedSlippageBps ?? 100,
|
||||||
|
referralCodeForTxn: params.referralCodeForTxn,
|
||||||
triggerPrice: params.triggerPrice,
|
triggerPrice: params.triggerPrice,
|
||||||
})
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if (isWrapOrUnwrap) {
|
|
||||||
const tokenAmount = "fromAmount" in params ? params.fromAmount : params.toAmount;
|
|
||||||
const usdAmount = convertToUsd(tokenAmount, fromToken.decimals, fromTokenPrice)!;
|
|
||||||
const price = fromTokenPrice;
|
|
||||||
|
|
||||||
swapAmounts = {
|
|
||||||
amountIn: tokenAmount,
|
|
||||||
usdIn: usdAmount!,
|
|
||||||
amountOut: tokenAmount,
|
|
||||||
usdOut: usdAmount!,
|
|
||||||
swapPathStats: undefined,
|
|
||||||
priceIn: price,
|
|
||||||
priceOut: price,
|
|
||||||
minOutputAmount: tokenAmount,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return swapAmounts;
|
return sdk.orders.createSwapOrder(createSwapOrderParams);
|
||||||
} else if ("fromAmount" in params) {
|
|
||||||
swapAmounts = getSwapAmountsByFromValue({
|
|
||||||
tokenIn: fromToken,
|
|
||||||
tokenOut: toToken,
|
|
||||||
amountIn: params.fromAmount,
|
|
||||||
triggerRatio,
|
|
||||||
isLimit,
|
|
||||||
findSwapPath: findSwapPath,
|
|
||||||
uiFeeFactor,
|
|
||||||
swapOptimizationOrder,
|
|
||||||
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
swapAmounts = getSwapAmountsByToValue({
|
|
||||||
tokenIn: fromToken,
|
|
||||||
tokenOut: toToken,
|
|
||||||
amountOut: params.toAmount,
|
|
||||||
triggerRatio,
|
|
||||||
isLimit: isLimit,
|
|
||||||
findSwapPath: findSwapPath,
|
|
||||||
uiFeeFactor,
|
|
||||||
swapOptimizationOrder,
|
|
||||||
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!swapAmounts) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const createSwapOrderParams: Parameters<typeof sdk.orders.createSwapOrder>[0] = {
|
|
||||||
tokensData,
|
|
||||||
fromToken: tokensData[params.fromTokenAddress],
|
|
||||||
toToken: tokensData[params.toTokenAddress],
|
|
||||||
swapAmounts,
|
|
||||||
isLimit,
|
|
||||||
allowedSlippage: params.allowedSlippageBps ?? 100,
|
|
||||||
referralCodeForTxn: params.referralCodeForTxn,
|
|
||||||
triggerPrice: params.triggerPrice,
|
|
||||||
};
|
|
||||||
|
|
||||||
return sdk.orders.createSwapOrder(createSwapOrderParams);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user