Environment-aware Stellar arbitrage CLI with real execution (Lightsail Stellar SDK), safety checks, and persistence. Now includes a multi-chain runner scaffold to host future adapters (e.g., Ripple).
- Java 21, Spring Boot 3.3
- Build the fat jar:
./mvnw -DskipTests packageProfiles:
application-mainnet.propertiesandapplication-testnet.propertiesdefine Horizon URL, passphrase, and assets.- Select with
-Dspring.profiles.active=testnet(default) ormainnet.
Key properties:
arb.assets: comma-separated asset list, e.g.XLM,USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVNarb.eval.use-best-of(default false): if true, allow mixed orientation; keep false in production.arb.circle.usdc.issuer: Circle USDC issuer to permit USDC as a starting asset.- Execution sizing/safety (see
application.properties):arb.exec.auto-size.enabled,arb.exec.max-notional,arb.exec.min-notional,arb.exec.depth-usage-fraction,arb.exec.native-xlm-safety-buffer,arb.exec.asset-safety-fraction.
Database:
- JPA/Hibernate with MySQL. Entities include ledger quotes, cycle evaluations, and execution attempts with diagnostics.
Enable dynamic market discovery so the watcher evaluates triangles across a rotating set of liquid assets:
arb.discovery.enabled(default false): when true, a background job populates an in-memory asset list and persists filtered pairs intocandidate_pair.arb.discovery.update-interval-minutes(default 60): validity window for discovered pairs; also used for refresh cadence.arb.discovery.max-assets(default 40): cap on distinct assets to observe (including hubs like XLM/USDC).arb.discovery.max-pairs(default 250): cap on base/quote pairs saved to DB per refresh.arb.discovery.thresholds.max-spread-bps(default 50): filter out pairs with wider spread.arb.discovery.thresholds.min-depth-at-notional(default 1.0): require minimum depth when sellingarb.start-notionalbase.- Option A (curated list):
arb.discovery.optionA.enabled(default false)arb.discovery.optionA.assets(CSV likeEURT:G...,USDT:G...,BTC:G...)arb.discovery.optionA.force-include(default false) — include curated assets in the asset set even if pruning fails; pairs are still only added when spread/depth pass.arb.discovery.optionA.sources(e.g.ticker,expert) — fetch top markets/assets from SDF Ticker and/or Stellar Expert and seed candidates.arb.discovery.optionA.ticker.url/arb.discovery.optionA.expert.url— base URLs for the external indexers.arb.discovery.include-weak-pairs(default false) — when true, emit pairs even if they fail thresholds, marked with sourcehzn-book-weakfor inspection/testing.
When discovery is enabled, OrderBookWatcher automatically uses the discovered asset set; if disabled or empty, it falls back to arb.assets.
By default (when arb.chains is empty), the legacy OrderBookWatcher runs:
./mvnw -Dspring.profiles.active=testnet spring-boot:runMainnet profile example (ensure keys, trustlines, and funding first):
./mvnw -Dspring.profiles.active=mainnet spring-boot:runActivate the multi-chain runner by specifying one or more chains via arb.chains:
# Stellar only (delegates to OrderBookWatcher in a background thread)
./mvnw -Dspring.profiles.active=testnet -Darb.chains=stellar spring-boot:run
# Stellar + Ripple (Ripple is a stub for now)
./mvnw -Dspring.profiles.active=testnet -Darb.chains=stellar,ripple spring-boot:runOptional: disable Stellar in multi-chain mode
./mvnw -Darb.chains=stellar -Darb.stellar.enabled=false spring-boot:runRecommended highly liquid XRPL issuers/pairs for early observation:
- XRP (native) – always available.
- USD issued by Bitstamp: issuer
rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B(pair:XRP/USD:rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B). - EUR issued by Bitstamp: issuer
rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq(pair:XRP/EUR:rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq). - BTC issued by Bitstamp: issuer
r9Dr5xwkeLegBeXq6ujinjZzUpb89TzZAW(pair:XRP/BTC:r9Dr5xwkeLegBeXq6ujinjZzUpb89TzZAW).
To watch multiple XRPL books, separate by commas in ARB_RIPPLE_PAIRS, e.g.:
export ARB_RIPPLE_PAIRS="XRP/USD:rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B,XRP/EUR:rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq,XRP/BTC:r9Dr5xwkeLegBeXq6ujinjZzUpb89TzZAW"
./mainnet.shThese issuers are widely recognized; always validate trustlines and issuer reputations before executing live trades.
KeygenCli is a standalone non-Spring class:
./mvnw -q -DskipTests -Dexec.mainClass=com.zduniak.arb.cli.KeygenCli exec:javaThis prints a new G... public key and S... secret seed. Handle the seed securely (do not commit).
A simple Dockerfile and render.yaml are included to deploy a worker that runs on mainnet. Ensure environment variables and secrets are provided securely in the hosting platform.
- Preflight checks and
destMinbounds protect against slippage and fees. - Execution auto-sizing caps notional by wallet balance and order book depth.
- Diagnostics for second-stage checks are persisted in
exec_attemptfor auditing. - Hourly XLM/USDC reference rate snapshots are stored in
reference_rate; seequeries.txtfor inspection queries. - Keep
arb.eval.use-best-of=falsein production to avoid mixed-orientation artifacts.
- Extract reusable market simulation utilities to support additional chain adapters.
- Implement Ripple/XRPL discovery and (optionally) execution.
- Dynamic pair discovery and improved orchestration.