# Wallet and signer used for position-decrease transactions
################################################################
WALLET_ID {{JUPITER_PERPS_WALLET}}
SIGNER_KEY_NAME <SIGNER>
# ID ASSET DIRECTION RECEIVE_TOKEN SIZE_DELTA_USD MAX_SLIPPAGE_BPS
############################################################################
# Values may reference {{VARIABLES}} defined in alarms.conf.
#3 SOL SHORT USDC 5.00 200
#6 SOL LONG USDC 5.00 200
Supported values:
ID: the alarm ID from alarms.conf.
ASSET: SOL, ETH, or BTC.
DIRECTION: LONG or SHORT.
RECEIVE_TOKEN: USDC or the same token as ASSET.
SIZE_DELTA_USD: the amount by which the position should be reduced; must be greater than zero.
MAX_SLIPPAGE_BPS: an integer from 0 to 10000.
The wallet and signer should be resolved during startup.
All transaction-row values should be validated during startup and resolved again whenever the alarm triggers, matching the behavior of the existing position-increase action.
Duplicate alarm IDs must be rejected.
An alarm ID absent from this configuration must not perform a decrease transaction.
Look up the configured PositionDecrease by alarm ID.
Return without doing anything if the alarm ID is not configured.
Resolve the definition using the current AlarmVariableResolver.
Fetch the wallet's current open positions through JupiterPerpsService.getOpenPositions(...).
Find the position matching the configured asset and direction.
Require exactly one matching open position.
Use its positionAccount when calling buildPositionDecreaseTransaction(...).
Sign the unsigned transaction using the configured SolanaWallet.
Submit it through executePositionDecreaseTransaction(...).
Log the returned Solana transaction signature.
The position account should be resolved dynamically instead of stored in the configuration. This allows the action to continue working when a position is closed and later reopened.
If no matching position exists, or more than one position matches the configured asset and direction, the action must fail safely without signing or submitting a transaction.
Runtime failures should be logged and isolated so the remaining configured alarm actions can still run.
Application integration
Register JupiterPerpsPositionDecreaseAlarmAction in the CompositeAlarmAction list created by JupiterPerpsAlarmImpl.
It should reuse the existing:
Configuration directory
Cached SolanaBlockChain
JupiterPerpsService
AlarmVariableResolver
As with the other configured actions, its configuration should be parsed and validated before WebSocket monitoring begins.
Documentation
Update README_alarm.md to document:
The new action configuration file
Its configuration columns and supported values
Dynamic resolution of the position account
Startup and trigger-time variable resolution
The fact that active rows sign and submit real Jupiter Perps transactions
The requirement that RECEIVE_TOKEN is USDC or the position's traded asset
Safe failure when no unique matching position can be found
The new example configuration file
Update references to the number of required configuration and action files.
Out of scope
This issue does not include:
Closing an entire position
Closing all positions
Changes to alarm-condition or trigger behavior
Retry or transaction-idempotency mechanisms
Changes to the Jupiter Perps decrease API added in issue #38
Changes to wallet signing
Acceptance criteria
The project compiles successfully.
The new decrease configuration and parser follow the existing increase-action structure.
Configuration values may reference shared alarm variables.
Invalid configuration fails during startup before WebSockets are opened.
An unconfigured alarm ID performs no decrease.
A configured alarm dynamically finds the matching open position by asset and direction.
Missing or ambiguous matching positions fail without signing or submitting.
A configured alarm can build, sign, and execute a partial position decrease.
The resulting Solana transaction signature is logged.
Existing console, Pushover, and position-increase actions continue to work.
Example configuration and alarm documentation are updated.
## Background
Issue #38 added and verified end-to-end support for decreasing an existing Jupiter Perps position through:
* `JupiterPerpsService.buildPositionDecreaseTransaction(...)`
* `JupiterPerpsService.executePositionDecreaseTransaction(...)`
The alarm application already supports automatic position increases through:
* `JupiterPerpsPositionIncreaseAlarmAction`
* `JupiterPerpsPositionIncreaseAlarmActionConfiguration`
* `JupiterPerpsPositionIncreaseAlarmActionConfigurationParser`
* `alarmaction_JupiterPerpsPositionIncreaseAlarmAction.conf`
We need an equivalent configurable alarm action for decreasing existing positions.
The new implementation should follow the existing position-increase action architecture and behavior as closely as possible.
## Configuration
Add a new configuration file:
```text
alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf
```
and an example file:
```text
conf/alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf.example
```
Suggested format:
```text
# Wallet and signer used for position-decrease transactions
################################################################
WALLET_ID {{JUPITER_PERPS_WALLET}}
SIGNER_KEY_NAME <SIGNER>
# ID ASSET DIRECTION RECEIVE_TOKEN SIZE_DELTA_USD MAX_SLIPPAGE_BPS
############################################################################
# Values may reference {{VARIABLES}} defined in alarms.conf.
#3 SOL SHORT USDC 5.00 200
#6 SOL LONG USDC 5.00 200
```
Supported values:
* `ID`: the alarm ID from `alarms.conf`.
* `ASSET`: `SOL`, `ETH`, or `BTC`.
* `DIRECTION`: `LONG` or `SHORT`.
* `RECEIVE_TOKEN`: `USDC` or the same token as `ASSET`.
* `SIZE_DELTA_USD`: the amount by which the position should be reduced; must be greater than zero.
* `MAX_SLIPPAGE_BPS`: an integer from `0` to `10000`.
The wallet and signer should be resolved during startup.
All transaction-row values should be validated during startup and resolved again whenever the alarm triggers, matching the behavior of the existing position-increase action.
Duplicate alarm IDs must be rejected.
An alarm ID absent from this configuration must not perform a decrease transaction.
## Configuration model and parser
Add:
* `JupiterPerpsPositionDecreaseAlarmActionConfiguration`
* `JupiterPerpsPositionDecreaseAlarmActionConfigurationParser`
The configuration model should mirror the existing increase configuration and contain:
* `walletId`
* `signerKeyName`
* `Map<Integer, PositionDecrease> positionDecreases`
A `PositionDecrease` should retain the unresolved expressions:
* `assetExpression`
* `directionExpression`
* `receiveTokenExpression`
* `sizeUsdDeltaExpression`
* `maxSlippageBpsExpression`
Resolving it should produce a typed `ResolvedPositionDecrease`.
A receive-token representation may be added to map `SOL`, `BTC`, `ETH`, and `USDC` to their corresponding SPL mint addresses.
Startup validation should reject:
* Missing or blank wallet IDs
* Missing or blank signer-key names
* Duplicate alarm IDs
* Unknown assets
* Unknown directions
* Unknown receive tokens
* A receive token other than USDC or the position's traded asset
* A size delta that is zero or negative
* Slippage outside `0..10000`
* Invalid or unresolved variables
* Incorrect column counts
Parser errors should include the configuration path and line number, matching the existing increase parser.
## Alarm action
Add:
```text
JupiterPerpsPositionDecreaseAlarmAction
```
It should implement `ConfiguredAlarmAction` and use the same constructor dependencies as `JupiterPerpsPositionIncreaseAlarmAction`:
* Configuration directory
* `SolanaBlockChain`
* `JupiterPerpsService`
* `AlarmVariableResolver`
`getConfigurationFileName()` should return:
```text
alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf
```
When an alarm triggers, the action should:
1. Look up the configured `PositionDecrease` by alarm ID.
2. Return without doing anything if the alarm ID is not configured.
3. Resolve the definition using the current `AlarmVariableResolver`.
4. Fetch the wallet's current open positions through `JupiterPerpsService.getOpenPositions(...)`.
5. Find the position matching the configured asset and direction.
6. Require exactly one matching open position.
7. Use its `positionAccount` when calling `buildPositionDecreaseTransaction(...)`.
8. Sign the unsigned transaction using the configured `SolanaWallet`.
9. Submit it through `executePositionDecreaseTransaction(...)`.
10. Log the returned Solana transaction signature.
The position account should be resolved dynamically instead of stored in the configuration. This allows the action to continue working when a position is closed and later reopened.
If no matching position exists, or more than one position matches the configured asset and direction, the action must fail safely without signing or submitting a transaction.
The transaction flow should be:
```java
SolanaUnsignedTransaction unsignedTransaction =
jupiter.buildPositionDecreaseTransaction(
position.positionAccount(),
resolvedDecrease.receiveTokenMint(),
resolvedDecrease.sizeUsdDelta(),
resolvedDecrease.maxSlippageBps()
);
SolanaSignedTransaction signedTransaction =
wallet.signTransaction(unsignedTransaction);
ΩSolanaTransactionSignatureΩ signature =
jupiter.executePositionDecreaseTransaction(signedTransaction);
```
Runtime failures should be logged and isolated so the remaining configured alarm actions can still run.
## Application integration
Register `JupiterPerpsPositionDecreaseAlarmAction` in the `CompositeAlarmAction` list created by `JupiterPerpsAlarmImpl`.
It should reuse the existing:
* Configuration directory
* Cached `SolanaBlockChain`
* `JupiterPerpsService`
* `AlarmVariableResolver`
As with the other configured actions, its configuration should be parsed and validated before WebSocket monitoring begins.
## Documentation
Update `README_alarm.md` to document:
* The new action configuration file
* Its configuration columns and supported values
* Dynamic resolution of the position account
* Startup and trigger-time variable resolution
* The fact that active rows sign and submit real Jupiter Perps transactions
* The requirement that `RECEIVE_TOKEN` is USDC or the position's traded asset
* Safe failure when no unique matching position can be found
* The new example configuration file
Update references to the number of required configuration and action files.
## Out of scope
This issue does not include:
* Closing an entire position
* Closing all positions
* Changes to alarm-condition or trigger behavior
* Retry or transaction-idempotency mechanisms
* Changes to the Jupiter Perps decrease API added in issue #38
* Changes to wallet signing
## Acceptance criteria
* The project compiles successfully.
* The new decrease configuration and parser follow the existing increase-action structure.
* Configuration values may reference shared alarm variables.
* Invalid configuration fails during startup before WebSockets are opened.
* An unconfigured alarm ID performs no decrease.
* A configured alarm dynamically finds the matching open position by asset and direction.
* Missing or ambiguous matching positions fail without signing or submitting.
* A configured alarm can build, sign, and execute a partial position decrease.
* The resulting Solana transaction signature is logged.
* Existing console, Pushover, and position-increase actions continue to work.
* Example configuration and alarm documentation are updated.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Background
Issue #38 added and verified end-to-end support for decreasing an existing Jupiter Perps position through:
JupiterPerpsService.buildPositionDecreaseTransaction(...)JupiterPerpsService.executePositionDecreaseTransaction(...)The alarm application already supports automatic position increases through:
JupiterPerpsPositionIncreaseAlarmActionJupiterPerpsPositionIncreaseAlarmActionConfigurationJupiterPerpsPositionIncreaseAlarmActionConfigurationParseralarmaction_JupiterPerpsPositionIncreaseAlarmAction.confWe need an equivalent configurable alarm action for decreasing existing positions.
The new implementation should follow the existing position-increase action architecture and behavior as closely as possible.
Configuration
Add a new configuration file:
and an example file:
Suggested format:
Supported values:
ID: the alarm ID fromalarms.conf.ASSET:SOL,ETH, orBTC.DIRECTION:LONGorSHORT.RECEIVE_TOKEN:USDCor the same token asASSET.SIZE_DELTA_USD: the amount by which the position should be reduced; must be greater than zero.MAX_SLIPPAGE_BPS: an integer from0to10000.The wallet and signer should be resolved during startup.
All transaction-row values should be validated during startup and resolved again whenever the alarm triggers, matching the behavior of the existing position-increase action.
Duplicate alarm IDs must be rejected.
An alarm ID absent from this configuration must not perform a decrease transaction.
Configuration model and parser
Add:
JupiterPerpsPositionDecreaseAlarmActionConfigurationJupiterPerpsPositionDecreaseAlarmActionConfigurationParserThe configuration model should mirror the existing increase configuration and contain:
walletIdsignerKeyNameMap<Integer, PositionDecrease> positionDecreasesA
PositionDecreaseshould retain the unresolved expressions:assetExpressiondirectionExpressionreceiveTokenExpressionsizeUsdDeltaExpressionmaxSlippageBpsExpressionResolving it should produce a typed
ResolvedPositionDecrease.A receive-token representation may be added to map
SOL,BTC,ETH, andUSDCto their corresponding SPL mint addresses.Startup validation should reject:
0..10000Parser errors should include the configuration path and line number, matching the existing increase parser.
Alarm action
Add:
It should implement
ConfiguredAlarmActionand use the same constructor dependencies asJupiterPerpsPositionIncreaseAlarmAction:SolanaBlockChainJupiterPerpsServiceAlarmVariableResolvergetConfigurationFileName()should return:When an alarm triggers, the action should:
PositionDecreaseby alarm ID.AlarmVariableResolver.JupiterPerpsService.getOpenPositions(...).positionAccountwhen callingbuildPositionDecreaseTransaction(...).SolanaWallet.executePositionDecreaseTransaction(...).The position account should be resolved dynamically instead of stored in the configuration. This allows the action to continue working when a position is closed and later reopened.
If no matching position exists, or more than one position matches the configured asset and direction, the action must fail safely without signing or submitting a transaction.
The transaction flow should be:
Runtime failures should be logged and isolated so the remaining configured alarm actions can still run.
Application integration
Register
JupiterPerpsPositionDecreaseAlarmActionin theCompositeAlarmActionlist created byJupiterPerpsAlarmImpl.It should reuse the existing:
SolanaBlockChainJupiterPerpsServiceAlarmVariableResolverAs with the other configured actions, its configuration should be parsed and validated before WebSocket monitoring begins.
Documentation
Update
README_alarm.mdto document:RECEIVE_TOKENis USDC or the position's traded assetUpdate references to the number of required configuration and action files.
Out of scope
This issue does not include:
Acceptance criteria