Files
com_r35157_nenjim-hubd-impl…/README_alarm.md
T

4.2 KiB

Jupiter Perps Price Alarm

A small Java program that listens to Jupiter Perps' on-chain aggregated oracle accounts through Solana WebSocket accountSubscribe.

It does not poll once per second. Every account update observed by the connected RPC node is decoded immediately. The program reconnects automatically, performs an initial/reconnect state fetch, and can connect to multiple independent RPC endpoints for redundancy.

The program supports any number of alarms for SOL, ETH, and BTC. Only one oracle stream is created per configured asset and RPC endpoint, regardless of how many alarms use that asset.

Java version

The project uses the current JDK compiler but generates Java 17-compatible class files:

tasks.withType(JavaCompile).configureEach {
    options.release = 17
}

Alarm configuration

The default file is price-alarms.conf:

# Asset   Direction   Target    TRIGGER     SEVERITY   NOTE
##################################################################################################
SOL       ABOVE       75.7      ONETIME     2          "ALARM: Risiko for Perps Solana short LIKVIDERING!"
SOL       BELOW       60.8      ONETIME     2          "ALARM: Risiko for Perps Solana long LIKVIDERING!"
SOL       BELOW       71.4      ONETIME     2          "ALARM: Risiko for Solana Raydium LÅN LIKVIDERING!"
ETH       ABOVE       1848.41   ONETIME     2          "ALARM: Risiko for Perps Ethereum short LIKVIDERING!"
ETH       BELOW       1789      ONETIME     1          "OK: Perps Ethereum short er lukket!"

Supported values:

  • Asset: SOL, ETH, or BTC
  • Direction: ABOVE or BELOW
  • Trigger: ONETIME or PERSISTENT
  • Target: positive decimal USD price
  • Severity: zero or positive integer
  • Note: quoted text; escaped quotes can be written as \"

SEVERITY and NOTE are parsed and retained in PriceAlarmDefinition, but are intentionally not used by the alarm actions yet.

Trigger behavior

On the first received price after program start:

  • An already satisfied alarm triggers immediately.
  • An unsatisfied alarm waits for the price to cross into its triggered side.

Alarm state is retained across WebSocket reconnects within the same process. If the price moves from the safe side to the triggered side during a connection outage, the first price received after reconnect will therefore trigger the alarm.

After that:

  • ONETIME triggers only once during the current program run.
  • PERSISTENT triggers each time the price crosses from the safe side into the triggered side.
  • Remaining on the triggered side does not repeatedly fire the alarm.

ONETIME state is currently kept in memory. Restarting the process arms the alarm again.

Build and test

gradle classes
gradle run --args='--self-test'

The self-test covers:

  • binary oracle decoding
  • configuration parsing
  • initial satisfied alarm behavior
  • ONETIME behavior
  • PERSISTENT crossing behavior

Run

Using the default price-alarms.conf in the working directory:

gradle run

Using another file:

gradle run --args='--config=/path/to/price-alarms.conf'

The path can also be selected through:

export PRICE_ALARMS_CONFIG='/path/to/price-alarms.conf'

Use two RPC WebSocket streams

A single WebSocket/RPC provider is not a durable event log. For better resilience, provide two independent endpoints:

export SOLANA_WS_URLS='wss://first-provider.example,wss://second-provider.example'
gradle run

With two configured assets and two RPC endpoints, the program opens four WebSocket connections.

Pushover emergency alarm

export PUSHOVER_APP_TOKEN='...'
export PUSHOVER_USER_KEY='...'
gradle run

The current implementation sends priority=2, retry=30, expire=10800, and sound=persistent.

Important limitations

  • processed is intentionally used for minimum delay, but a processed update may belong to a fork that is later abandoned.
  • Solana PubSub is not guaranteed delivery. Two independent RPC streams reduce, but do not eliminate, the risk of missing an update.
  • The alarm reports the Jupiter Perps oracle price. It does not prove that a specific position was liquidated.
  • This is an alerting aid, not a substitute for placing an on-platform stop-loss or reducing leverage.