Skip to content

Releases: keitaj/hyperliquid-bot

v0.27.0

04 May 03:18
e47d2ae

Choose a tag to compare

What's New

Features

  • Forager composite-score auto-exclude (#146, #147) — A second-tier auto-exclude path that scores each coin on three dimensions and pauses quoting when the blended score falls below a threshold, separate from the existing markout-driven auto_exclude. The score combines activity (close-event flow vs. idle), close-quality (maker close rate, with a minimum-closes gate before the dimension is trusted), and cost (per-1K-volume net cost). Each dimension is weighted independently so operators can re-balance without touching the score formula. Opt-in via --forager; tuning via --forager-threshold (default 30.0) plus --forager-w-activity / -quality / -cost (defaults 0.3 / 0.4 / 0.3). Cooldown and consecutive-strike gates mirror the existing auto_exclude semantics so the two features compose cleanly. The five non-CLI knobs (FORAGER_WINDOW_SECONDS, FORAGER_CHECK_INTERVAL_SECONDS, FORAGER_ACTIVITY_IDLE_MIN_SECONDS, FORAGER_COST_MAX_PER_1K, FORAGER_MIN_CLOSES_FOR_QUALITY) stay env-only to keep the CLI surface manageable. (#147 promotes the previously hardcoded 5-close quality gate to the same env knob, so all formula thresholds are configurable.)

  • JSON config as a layered input source (#148, #149) — New --config <path> CLI flag (repeatable; later files override earlier) and BOT_CONFIG=<path> env var that load a JSON file as a config layer between the dataclass defaults and CLI/env. Final precedence is CLI > env > JSON > defaults, so the rollout is opt-in and pure-additive: existing CLI/env workflows are byte-identical when no --config is supplied. The loader auto-detects flat ({"spread_bps": 10, …}) vs. nested ({"market_making": {"spread_bps": 10, …}}) form; nested keys are flattened against _NESTED_TO_FLAT_MAP so downstream MMConfig.from_legacy_dict() is unchanged. JSON values under the risk namespace are wired through to Config.{KEY.upper()} via a new _apply_json_risk_overrides helper (closes the gap from the initial #148 wiring); CLI risk flags still beat JSON, JSON beats env, and the helper pops applied keys so they don't leak back into strategy_config. Unknown keys log a typo warning but otherwise pass through; malformed JSON aborts startup with exit code 2 (fail-safe). An examples/config.example.json covers the nested form.

Fixes

  • _STRATEGY_PARAMS / _COMMON_PARAMS at module scope (#150) — Latent bug discovered during round-trip migration-tool verification: PR #148 lazily imports these lists in validation.strategy_validator.known_market_making_keys() for typo detection, but only _RISK_PARAMS was promoted to module level in #149 — the other two stayed inside main(), so any --config invocation hit ImportError at startup. This release moves both lists out of main() (call-sites unchanged) and adds a TestKnownMarketMakingKeys regression class that pins the contract via direct import + indirect function calls; future demotion will fail CI rather than reach production.

Upgrade Notes

  • All changes are backward compatible — defaults preserve existing behaviour:
    • Forager is default-disabled. Without --forager the strategy and shutdown paths are byte-identical to v0.26.0. Operators flipping it on are advised to start with the default weights and --forager-threshold 30, observe [forager] log lines for at least one full session, and tighten only after the per-coin score distribution stabilises.
    • JSON config is opt-in. With neither --config nor BOT_CONFIG set, the JSON loader is never invoked. When set, the layer sits below env/CLI; existing .env.farming + start_farming*.sh flows continue to win on every shared key.
    • Migration tool: a companion scripts/env_to_json.py lives in the hip-3-farming-agent-team repo — it converts .env.farming to a nested JSON config, skipping credentials/DEX/framework keys. Useful for staging a JSON deployment without re-typing the existing knob set.
    • Level 1 risk guardrails (risk_manager.py hardcoded values) are untouched in this release.

Operator Checklist

  • Forager Phase A (default weights, min_fills=3, score threshold 30): turn on via FORAGER_ENABLED=true + --forager. Watch the per-coin score component breakdown in the periodic [forager] summary; confirm no coin is scored below threshold purely on a single dimension before tightening any weight.
  • JSON config Phase A (loader path validation): set BOT_CONFIG=/abs/path/config.json in .env.farming while leaving all CLI flags in start_farming*.sh intact. Confirm [config] Loaded N key(s) from … and [config] JSON layer loaded: N key(s) from 1 file(s) appear at startup with no warnings beyond intentional skips. Phase B (stripping CLI flags so JSON drives values) is a separate operator step after a 24h observation window.

v0.26.0

02 May 08:47
05135da

Choose a tag to compare

What's New

Features

  • Order refresh tolerance (#144, #145) — Opt-in gate that preserves Hyperliquid's price-time queue priority by keeping an existing limit order across cycles when its recorded price has drifted no more than --refresh-tolerance-bp basis points from the current ideal price. The cancel still fires immediately when the drift exceeds tolerance, and a safety-net upper bound on age applies independently via --refresh-max-age-seconds (default refresh_interval_seconds * 4). New OrderTracker.refresh_orders_with_tolerance records cumulative kept / cancelled-by-drift / cancelled-by-age counters; _compute_ideal_prices is the single source of truth for both the run-loop tolerance check and order placement, so the volatility rolling buffer is updated exactly once per cycle. Default 0 keeps the legacy age-only behaviour.
  • Per-coin unrealized_loss_close_bps overrides (#143) — --coin-unrealized-loss-overrides "INTC:25,OIL:10" lets the unrealized-loss early-close threshold be tuned per coin (relax on low-vol coins, tighten on volatile ones). Setting a coin to 0 disables the feature for that coin only.
  • DYNAMIC_AGE clamp observability + close-reason max_age field (#142) — Per-coin clamp distribution (min_clamp / max_clamp / mid / samples) is logged at the dynamic-age summary cadence, and force-close events now carry the effective max_age they fired against so post-mortems can distinguish baseline vs. dynamic-age driven force-closes.
  • Builder fee approval verification (#139) — After every approveBuilderFee call the bot now reads back the fee approval and logs the result, so a stale approval that failed to land is surfaced rather than silently retried each cycle. The retry path itself is unchanged.
  • USDT0 in check_balance.py spot summary (#138) — Cash HIP-3 perps settle in USDT0; the standalone balance script now prints USDT0 alongside USDC and USDH (header updated to Spot (USDC/USDH/USDT0):). Wallets without USDT0 are unaffected.

Tuning

  • Lower auto_exclude min_fills default from 5 to 3 (#140) — The original 5-fill floor was too strict at the default 300s adverse-selection summary window: many low-volume coins never accumulated enough fills to trigger an exclusion even when adverse selection was clearly negative. Three fills is enough signal at the default cadence; explicit --auto-exclude-min-fills overrides still apply.

Tests / Internal

  • Codify the close-order invariant for OrderTracker (#141) — Contract tests pin the load-bearing invariant that close orders never enter OrderTracker._tracked_orders and so are never cancelled by ImbalanceGuard / BboGuard / BboVelocityGuard / FillFeed cleanup paths. Prevents regression of the close-side preservation behaviour validated in the 2026-05-01 system review.

Upgrade Notes

  • All changes are backward compatible — defaults preserve existing behaviour:
    • Refresh tolerance is opt-in via --refresh-tolerance-bp (default 0 = age-only). Recommended Phase B value is 2 (bp); start with 1 on the most volatile coins to gauge the kept-rate ratio.
    • Per-coin unrealized_loss_close_bps overrides default to the global value when unset; setting a coin to 0 disables only that coin's threshold.
    • The DYNAMIC_AGE observability fields are additive — existing log parsers that don't recognise the new keys are unaffected.
    • Builder fee approval verification is wired into the existing reassert path; failures still log and continue (init never aborts).
    • auto_exclude remains default-disabled — the min_fills=3 change only kicks in when the feature is explicitly enabled via --auto-exclude + --enable-adverse-selection-log.
    • Level 1 risk guardrails (risk_manager.py hardcoded values) are untouched in this release.

Operator Checklist

  • For Phase B rollout of refresh tolerance: set REFRESH_TOLERANCE_BP=2 (and optionally REFRESH_MAX_AGE_SECONDS) in the deployment env. The farming-team scripts already forward both via the start-session helpers.
  • After deploying, watch OrderTracker.get_refresh_stats() (logged events Cancelled drift/age <side> order ... for ... and keeping {side} oid=...) to confirm a healthy kept ratio (target ~0.3-0.7 in calm markets).

v0.25.0

30 Apr 19:20
3c2d3f0

Choose a tag to compare

What's New

Features

  • Per-DEX builder fee support (#136) — Adds optional builder-code attachment per HIP-3 DEX so the bot can become eligible for HIP-3 deployer rewards programs that gate on builder-code presence. New BUILDER_FEES_<DEX>_ADDRESS, BUILDER_FEES_<DEX>_TENTHS_BPS, and BUILDER_FEES_<DEX>_MAX_FEE_RATE env vars; bulk_place_orders groups orders by DEX so each group ships with its matching builder, and standard Hyperliquid coins keep their existing no-builder path. Idempotent approveBuilderFee is reasserted at every startup; failures are logged but never abort initialization.
  • Auto-exclude on consecutive adverse-selection windows (#132) — Opt-in feedback loop that pauses a coin when AdverseSelectionTracker reports moderate adverse selection (avg_<window> ≤ --auto-exclude-threshold-bps, default -3.0) for --auto-exclude-consecutive summary windows in a row (default 3, ~15 min at the default 300s interval). The coin auto-resumes after --auto-exclude-cooldown seconds (default 1800). Closes a gap left by loss_streak_limit (close-PnL based, weak against slow bleed) and dynamic_offset (capped at +3 bps).

Bug Fixes

  • Distinguish deposits from API failures in risk_manager (#133) — The >50% single-cycle balance check previously logged every change as "Likely spot API failure" and never refreshed daily_starting_balance, leaving subsequent daily_loss_limit checks comparing against a stale baseline. Now dispatches on direction:

    • Increase → treated as deposit. Logs at INFO and resets daily_starting_balance to the new value.
    • Decrease → treated as likely API failure. Logs at WARNING and skips the daily-loss check for that cycle.

    metrics.total_balance is never mutated; <50% changes are unchanged; Level 1 hardcoded guardrails are untouched.

Refactors (internal — no behaviour change)

  • MM config dataclass grouping (#128, #129, #130, #131) — Final phase of grouping all CLI/env-driven MM parameters into typed sub-dataclasses on MMConfig (close timing, imbalance, schedule, dynamic offset, dynamic age, auto-exclude). Legacy flat keys still load via from_legacy_dict; argparse dest= is aligned with config keys per Phase 2b. Flat instance attributes on MarketMakingStrategy preserved as aliases so existing references and tests are unchanged.
  • Dedupe MM coin-override and volatility helpers (#137) — Replaces four inline copies of HIP-3 coin parsing with coin_utils.parse_coin, extracts a single _lookup_coin_override (full-name → bare-name → default fallback chain) used by _get_coin_offset / _get_coin_spread / _get_coin_size, and shares one _compute_realized_volatility(coin) between _get_volatility_adjusted_offset and _get_dynamic_position_age.

Docs

  • Refresh stale "Phase 1" docstring in mm_config (#135) and add strategies/mm_config.py to the AGENTS.md architecture table (#134).

Upgrade Notes

  • All changes are backward compatible — defaults preserve existing behaviour:
    • Builder fees are opt-in via env vars; without them, every order ships with builder=None exactly as before.
    • Auto-exclude is default-disabled — requires both --auto-exclude and --enable-adverse-selection-log to be active.
    • The risk_manager change preserves Level 1 hardcoded guardrails and never mutates metrics.total_balance.
    • All MM config refactors keep CLI flags, env vars, and flat instance attributes intact.

v0.24.0

27 Apr 00:27
a543104

Choose a tag to compare

What's New

Features

  • Drain mode for graceful pre-shutdown (#127) — New --drain-flag-file flag enables flag-file based drain mode for the market-making strategy. When the configured file exists, the strategy stops placing new entry orders and only manages existing positions via the normal maker-first close flow. Designed to be triggered by an external script before SIGTERM so positions can unwind via maker close instead of taker IOC close at session boundaries. Behaviour mirrors the existing quiet_hours full-stop mode; drain takes priority when both apply.
  • Periodic dynamic_age summary log (#125) — Adds a 5-minute interval summary log line that lists each tracked coin's current realized volatility (bps) and the resulting effective max_position_age (seconds). Provides runtime visibility into how --dynamic-age is reshaping holding times per coin without requiring debug-level logging.

Bug Fixes

  • Race-safe active_orders cleanup (#126) — Replace dict membership check + del with dict.pop(key, None) in update_order_status to remove a window where two coroutines could both pass the in check before either deletes the entry, causing a KeyError. Pure refactor; no observable behaviour change in non-racy paths.

Upgrade Notes

  • All changes are backward compatible — defaults preserve existing behaviour:
    • --drain-flag-file defaults to empty string (disabled). Existing deployments behave unchanged unless the flag is explicitly set.
    • The dynamic_age summary log is emitted only when --dynamic-age is enabled and at most once every 5 minutes; it adds no measurable overhead.
    • The update_order_status change is a refactor with no public API or default-path behaviour change.

Operational Tips

  • To use drain mode in a session-switch helper, point --drain-flag-file at a path the helper can touch shortly before sending SIGTERM, then remove the file (or have the next bot start clean it up). The bot enters drain on the next main-loop tick after the file appears.

v0.23.0

25 Apr 10:39
3194702

Choose a tag to compare

What's New

Features

  • Volatility-adjusted MAX_POSITION_AGE (#124) — New --dynamic-age flag adjusts position holding time based on per-coin realized volatility. High-volatility coins get shorter holding times (reducing adverse selection); low-volatility coins get longer times (improving maker fill probability). Configurable via --dynamic-age-baseline-vol, --dynamic-age-min, --dynamic-age-max.
  • Per-coin order size overrides (#123) — New --coin-size-overrides flag (e.g. \"TSLA:150,NVDA:150\") allows setting different order sizes per coin. Setting a coin to `0` skips orders for that coin. Follows the same lookup pattern as existing --coin-offset-overrides and --coin-spread-overrides.
  • Unrealized loss based early taker close (#122) — New --unrealized-loss-close-bps flag triggers immediate taker close when a position's unrealized loss exceeds the configured bps threshold, regardless of position age. Targets large-loss trades that accumulate while waiting for maker close.

Upgrade Notes

  • All changes are backward compatible — defaults preserve existing behavior:
    • --dynamic-age is off by default (boolean flag)
    • --coin-size-overrides defaults to empty string (all coins use global `--order-size-usd`)
    • --unrealized-loss-close-bps defaults to 0 (disabled)
  • The dynamic age feature reuses the same _recent_mids price history as --vol-adjust, so no extra data collection overhead when both are enabled.

v0.22.0

25 Apr 08:14
afa07ff

Choose a tag to compare

What's New

Features

  • Coin list deduplication at startup (#121) — Automatically deduplicates coin list and logs a warning when duplicates are detected, preventing double-order issues
  • Range syntax for spread_schedule (#119) — Supports hour-range shorthand (e.g. "0-3:1.5") in addition to individual hour entries, making schedule configuration more concise
  • Close reason classification logging (#118) — Logs structured close reasons (TP/SL/signal/force/manual) for position closes, improving post-session analysis

Improvements

  • Extract _TIER_NAMES module constant (#120) — Refactored duplicated tier-name dicts into a single module-level constant for consistency and maintainability

Bug Fixes

  • _open_positions guard in close_position (#117) — Added missing guard to prevent reduce-only order rejections when position is already closed

Upgrade Notes

  • All changes are backward compatible — no new flags or configuration required
  • If your coin list had duplicates, you will now see a warning log at startup instead of silent double-ordering
  • Spread schedule now accepts range syntax: --spread-schedule "0-3:1.5,14-16:1.5" (individual hour syntax still works)

v0.21.0

21 Apr 22:13
488124a

Choose a tag to compare

What's New

Features

  • Micro-price asymmetric offset (#109) — Adjusts buy/sell offsets based on top-of-book size imbalance to reduce adverse selection on the riskier side
  • BBO velocity guard (#109) — Cancels orders when BBO moves consistently in one direction (3+ consecutive ticks), preventing fills during fast moves
  • Close order optimization (#112) — Progressive close pricing with spread tiers, BBO tracking via CloseRefreshGuard for improved maker close rates
  • Hourly spread schedule (#113) — Per-hour spread multiplier (--spread-schedule "14:1.5,20:1.5") to widen spreads during high adverse-selection time windows
  • Dynamic offset auto-adjustment (#114) — Auto-adjusts per-coin BBO offset based on adverse selection severity from the tracker; widens for high-AS coins, tightens for favorable ones

Bug Fixes

  • CloseRefreshGuard activation fix (#110) — Fixed rate limiting and WS reconnect issues that prevented close order refresh from working
  • Reduce-only rejection fix (#111) — 3-layer defense with fresh position verification in force-close path (11 errors/session → 2)
  • WS reconnect guard re-registration (#115) — Fixed AdverseSelectionTracker, VelocityGuard, and PositionCloser being silently lost on WebSocket reconnection (~3h intervals)
  • Final reduce-only guard (#116) — Check _open_positions (updated instantly by FillFeed) right before all reduce_only orders, eliminating the 2-second cache race condition

Docs

  • Updated README with all new parameters and AI YAML reference

Upgrade Notes

  • All new features are disabled by default (backward compatible)
  • Enable with: --microprice-skew, --velocity-guard, --dynamic-offset, --spread-schedule "..."
  • Requires --enable-ws for micro-price, velocity guard, and dynamic offset
  • Requires --enable-adverse-selection-log for dynamic offset

v0.20.0

20 Apr 00:28
b2c2300

Choose a tag to compare

What's New

Features

  • Quiet Hours (#101): Skip or widen spread during high adverse-selection UTC hours. --quiet-hours-utc 17 stops quoting during UTC 17; --quiet-hours-spread-multiplier 2.0 widens spread instead.
  • Close Refresh Guard (#102): Refresh close orders when BBO changes, improving maker close fill rate before the taker fallback deadline. --close-refresh-threshold-bps 1.0
  • Per-Coin Spread Overrides (#103): Configure BBO offset and spread per coin. --coin-offset-overrides "SP500:0.5,MSFT:3" allows tighter spreads for efficient coins and wider spreads for high-taker coins.
  • Adverse Selection Tracker (#104): Measure mid-price movement 5s/30s/60s after each fill to quantify adverse selection per coin. Observation only. --enable-adverse-selection-log
  • Dynamic Close Pricing (#106): Progressive loss acceptance during force-close phase, scaling from aggressive-loss-bps to force-close-max-loss-bps as position approaches the taker deadline.

Bug Fixes

  • Reduce-Only Collision Prevention (#105): Fix race condition causing "Reduce only order would increase position" errors. Three-layer defense: FillFeed→PositionCloser notification, dead OID defer, position verification.

Improvements

  • ImbalanceGuard Summary Logging (#107): Periodic summary with max imbalance per coin and cancel/skip counts for operational diagnostics.
  • README Update (#108): Document all new CLI flags in both human-readable and AI YAML reference sections.

Stats

  • 8 PRs merged
  • 100 new tests (544 → 630 total)
  • 2,550 lines added

v0.19.0

19 Apr 14:39
5db16f1

Choose a tag to compare

What's Changed

Features

  • WebSocket fill feed for instant opposite-side cancellation (#96)
  • BboGuard for instant stale quote cancellation on BBO change (#97)
  • ImbalanceGuard for reactive one-sided order cancellation on L2 skew (#98)
  • WebSocket auto-reconnect on connection drop (#99)

Refactoring

  • Downgrade bulk cancel partial failure logs from WARNING to DEBUG (#100)

Bug Fixes

  • Create separate WS-enabled Info for WebSocket feed

Full Changelog: v0.18.0...v0.19.0

v0.18.0

19 Apr 07:03
fd6255b

Choose a tag to compare

What's Changed

Features

  • WebSocket market data feed for real-time L2 book updates (#95)

Refactoring

  • Extract TTLCache utility and deduplicate position sizing (#92)

Bug Fixes

  • Use SDK-compliant price rounding with sz_decimals (#94)
  • Remove unused imports (#96)

Full Changelog: v0.17.0...v0.18.0