diff --git a/conf/alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf.example b/conf/alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf.example index 7dca901..3176184 100644 --- a/conf/alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf.example +++ b/conf/alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf.example @@ -1,8 +1,8 @@ # Wallet and signer used for position-decrease transactions ################################################################ -WALLET_ID {{JUPITER_PERPS_WALLET}} -SIGNER_KEY_NAME - +WALLET_ID {{JUPITER_PERPS_WALLET}} +SIGNER_KEY_NAME +RESERVE_POSITION_SIZE_USD_LIMIT 10 # Id Asset Direction ReceiveToken SizeDeltaUSD MaxSlippageBPS ######################################################################## diff --git a/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmAction.tjava b/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmAction.tjava index 1b68979..948ef6e 100644 --- a/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmAction.tjava +++ b/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmAction.tjava @@ -9,6 +9,7 @@ import com.r35157.libs.solana.SolanaSignedTransaction; import com.r35157.libs.solana.SolanaUnsignedTransaction; import java.io.IOException; +import java.math.BigDecimal; import java.nio.file.Path; import java.util.Objects; @@ -68,6 +69,10 @@ public final class JupiterPerpsPositionDecreaseAlarmAction JupiterPerpsPosition position = findOpenPosition(positionDecrease); + if (!isPositionSizeOk(position, positionDecrease)) { + return; + } + SolanaUnsignedTransaction utrans = jupiter.buildPositionDecreaseTransaction( position.positionAccount(), @@ -76,8 +81,10 @@ public final class JupiterPerpsPositionDecreaseAlarmAction positionDecrease.sizeUsdDelta(), positionDecrease.maxSlippageBps() ); + SolanaSignedTransaction strans = wallet.signTransaction(utrans); + ΩSolanaTransactionSignatureΩ signature = jupiter.executePositionDecreaseTransaction(strans); @@ -135,6 +142,39 @@ public final class JupiterPerpsPositionDecreaseAlarmAction return matchingPosition; } + private boolean isPositionSizeOk( + JupiterPerpsPosition position, + JupiterPerpsPositionDecreaseAlarmActionConfiguration + .ResolvedPositionDecrease positionDecrease + ) { + ΩUSDCAmountΩ currentPositionSize = position.positionSize(); + ΩUSDCAmountΩ decreaseSizeUsd = positionDecrease.sizeUsdDelta(); + ΩUSDCAmountΩ reservePositionSizeUsdLimit = + configuration.reservePositionSizeUsdLimit(); + + ΩUSDCAmountΩ remainingPositionSize = + currentPositionSize.subtract(decreaseSizeUsd); + + if (remainingPositionSize.compareTo( + reservePositionSizeUsdLimit + ) < 0) { + System.err.println( + "Position Decrease of " + + decreaseSizeUsd + + " USD skipped: Position size is " + + currentPositionSize + + " USD and would become " + + remainingPositionSize + + " USD, which is below the " + + reservePositionSizeUsdLimit + + " USD reserve." + ); + return false; + } + + return true; + } + private final JupiterPerpsPositionDecreaseAlarmActionConfiguration configuration; private final SolanaWallet wallet; diff --git a/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmActionConfiguration.tjava b/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmActionConfiguration.tjava index 1e4ac57..6bbc4b9 100644 --- a/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmActionConfiguration.tjava +++ b/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmActionConfiguration.tjava @@ -10,11 +10,21 @@ import java.util.Objects; public record JupiterPerpsPositionDecreaseAlarmActionConfiguration( ΩSolanaWalletIdΩ walletId, String signerKeyName, + ΩUSDCAmountΩ reservePositionSizeUsdLimit, Map positionDecreases ) { public JupiterPerpsPositionDecreaseAlarmActionConfiguration { Objects.requireNonNull(walletId, "walletId"); Objects.requireNonNull(signerKeyName, "signerKeyName"); + Objects.requireNonNull(reservePositionSizeUsdLimit, "reservePositionSizeUsdLimit"); + + if (reservePositionSizeUsdLimit.signum() < 0) { + throw new IllegalArgumentException( + "Position size USD reserve limit cannot be negative: " + + reservePositionSizeUsdLimit + ); + } + positionDecreases = Map.copyOf(positionDecreases); if (walletId.isBlank()) { diff --git a/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmActionConfigurationParser.tjava b/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmActionConfigurationParser.tjava index e58cc6a..a7a2bc1 100644 --- a/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmActionConfigurationParser.tjava +++ b/src/main/tjava/com/r35157/jupiterperpsalarm/impl/ref/JupiterPerpsPositionDecreaseAlarmActionConfigurationParser.tjava @@ -1,6 +1,7 @@ package com.r35157.jupiterperpsalarm.impl.ref; import java.io.IOException; +import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; import java.util.LinkedHashMap; @@ -20,6 +21,7 @@ public final class JupiterPerpsPositionDecreaseAlarmActionConfigurationParser { positionDecreases = new LinkedHashMap<>(); ΩSolanaWalletIdΩ walletId = null; String signerKeyName = null; + ΩUSDCAmountΩ reservePositionSizeUsdLimit = null; for (int lineNumber = 1; lineNumber <= lines.size(); lineNumber++) { String line = removeComment(lines.get(lineNumber - 1)).trim(); @@ -51,6 +53,20 @@ public final class JupiterPerpsPositionDecreaseAlarmActionConfigurationParser { ); } signerKeyName = variableResolver.resolve(columns[1]); + } else if (firstColumn.equals("RESERVE_POSITION_SIZE_USD_LIMIT")) { + requireColumnCount( + columns, + 2, + "RESERVE_POSITION_SIZE_USD_LIMIT value" + ); + if (reservePositionSizeUsdLimit != null) { + throw new IllegalArgumentException( + "Duplicate RESERVE_POSITION_SIZE_USD_LIMIT" + ); + } + reservePositionSizeUsdLimit = new ΩUSDCAmountΩ( + variableResolver.resolve(columns[1]) + ); } else { parsePositionDecrease( columns, @@ -78,9 +94,16 @@ public final class JupiterPerpsPositionDecreaseAlarmActionConfigurationParser { ); } + if (reservePositionSizeUsdLimit == null) { + throw new IllegalArgumentException( + "Missing RESERVE_POSITION_SIZE_USD_LIMIT in " + path + ); + } + return new JupiterPerpsPositionDecreaseAlarmActionConfiguration( walletId, signerKeyName, + reservePositionSizeUsdLimit, positionDecreases ); }